Expand coverage for runtime and sqlite paths

This commit is contained in:
phamnazage-jpg
2026-05-23 10:55:57 +08:00
parent 2ad277743d
commit bcc67c4a8a
17 changed files with 3393 additions and 1 deletions

View File

@@ -0,0 +1,57 @@
package sqlite
import (
"context"
"testing"
)
func TestAccessClosureRecordsRepoCreateValidationAndDefaults(t *testing.T) {
t.Parallel()
ctx := context.Background()
store := openTestDB(t)
repo := store.AccessClosures()
if _, err := repo.Create(ctx, AccessClosureRecord{}); err == nil || err.Error() != "batch_id is required" {
t.Fatalf("Create() error = %v, want batch_id is required", err)
}
if _, err := repo.Create(ctx, AccessClosureRecord{BatchID: 1}); err == nil || err.Error() != "closure_type is required" {
t.Fatalf("Create() error = %v, want closure_type is required", err)
}
if _, err := repo.Create(ctx, AccessClosureRecord{BatchID: 1, ClosureType: "self_service"}); err == nil || err.Error() != "status is required" {
t.Fatalf("Create() error = %v, want status is required", err)
}
batchID := createTestBatch(t, store)
recordID, err := repo.Create(ctx, AccessClosureRecord{
BatchID: batchID,
ClosureType: "self_service",
Status: "ready",
})
if err != nil {
t.Fatalf("Create() error = %v", err)
}
if recordID <= 0 {
t.Fatalf("recordID = %d, want positive id", recordID)
}
records, err := repo.GetByBatchID(ctx, batchID)
if err != nil {
t.Fatalf("GetByBatchID() error = %v", err)
}
if len(records) != 1 {
t.Fatalf("len(records) = %d, want 1", len(records))
}
if records[0].DetailsJSON != "{}" {
t.Fatalf("DetailsJSON = %q, want {}", records[0].DetailsJSON)
}
}
func TestAccessClosureRecordsRepoGetByBatchIDValidation(t *testing.T) {
t.Parallel()
_, err := openTestDB(t).AccessClosures().GetByBatchID(context.Background(), 0)
if err == nil || err.Error() != "batch_id is required" {
t.Fatalf("GetByBatchID() error = %v, want batch_id is required", err)
}
}

View File

@@ -159,3 +159,115 @@ func TestReadMigrationNotFound(t *testing.T) {
t.Fatal("readMigration('nonexistent.sql') error = nil, want error")
}
}
func TestEnsureMigrationLedgerAndLoadAppliedMigrations(t *testing.T) {
store := openTestDB(t)
db := store.SQLDB()
tx, err := db.BeginTx(context.Background(), nil)
if err != nil {
t.Fatalf("BeginTx() error = %v", err)
}
defer tx.Rollback()
if err := ensureMigrationLedger(context.Background(), tx); err != nil {
t.Fatalf("ensureMigrationLedger() error = %v", err)
}
if err := ensureMigrationLedger(context.Background(), tx); err != nil {
t.Fatalf("ensureMigrationLedger() second call error = %v", err)
}
if _, err := tx.ExecContext(context.Background(), "INSERT INTO schema_migrations (version) VALUES (?)", "9999_test.sql"); err != nil {
t.Fatalf("insert schema_migrations error = %v", err)
}
applied, err := loadAppliedMigrations(context.Background(), tx)
if err != nil {
t.Fatalf("loadAppliedMigrations() error = %v", err)
}
if !applied["9999_test.sql"] {
t.Fatalf("loadAppliedMigrations() = %#v, want 9999_test.sql=true", applied)
}
}
func TestBackfillLegacySchemaIfNeededRecordsCompleteLegacySchema(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "legacy.db")
rawDB, err := sql.Open("sqlite", "file:"+filepath.ToSlash(dbPath))
if err != nil {
t.Fatalf("sql.Open() error = %v", err)
}
defer rawDB.Close()
for _, ddl := range []string{
"CREATE TABLE hosts (id INTEGER PRIMARY KEY, host_id TEXT NOT NULL, base_url TEXT NOT NULL, host_version TEXT NOT NULL, capability_probe_json TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)",
"CREATE TABLE packs (id INTEGER PRIMARY KEY, pack_id TEXT NOT NULL, version TEXT NOT NULL, checksum TEXT NOT NULL, manifest_json TEXT NOT NULL DEFAULT '{}', metadata_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)",
"CREATE TABLE providers (id INTEGER PRIMARY KEY, pack_id INTEGER NOT NULL, provider_id TEXT NOT NULL, display_name TEXT NOT NULL, base_url TEXT NOT NULL, platform TEXT NOT NULL, account_type TEXT NOT NULL DEFAULT 'apikey', smoke_test_model TEXT NOT NULL DEFAULT '', provider_manifest_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP)",
} {
if _, err := rawDB.ExecContext(context.Background(), ddl); err != nil {
t.Fatalf("Exec legacy ddl error = %v", err)
}
}
tx, err := rawDB.BeginTx(context.Background(), nil)
if err != nil {
t.Fatalf("BeginTx() error = %v", err)
}
defer tx.Rollback()
if err := ensureMigrationLedger(context.Background(), tx); err != nil {
t.Fatalf("ensureMigrationLedger() error = %v", err)
}
applied, err := loadAppliedMigrations(context.Background(), tx)
if err != nil {
t.Fatalf("loadAppliedMigrations() error = %v", err)
}
if err := backfillLegacySchemaIfNeeded(context.Background(), tx, []string{"0001_init.sql"}, applied); err != nil {
t.Fatalf("backfillLegacySchemaIfNeeded() error = %v", err)
}
if !applied["0001_init.sql"] {
t.Fatalf("applied = %#v, want 0001_init.sql marked", applied)
}
}
func TestBackfillLegacySchemaIfNeededRejectsPartialLegacySchema(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "legacy-partial.db")
rawDB, err := sql.Open("sqlite", "file:"+filepath.ToSlash(dbPath))
if err != nil {
t.Fatalf("sql.Open() error = %v", err)
}
defer rawDB.Close()
if _, err := rawDB.ExecContext(context.Background(), "CREATE TABLE hosts (id INTEGER PRIMARY KEY)"); err != nil {
t.Fatalf("Exec partial legacy ddl error = %v", err)
}
tx, err := rawDB.BeginTx(context.Background(), nil)
if err != nil {
t.Fatalf("BeginTx() error = %v", err)
}
defer tx.Rollback()
if err := ensureMigrationLedger(context.Background(), tx); err != nil {
t.Fatalf("ensureMigrationLedger() error = %v", err)
}
applied, err := loadAppliedMigrations(context.Background(), tx)
if err != nil {
t.Fatalf("loadAppliedMigrations() error = %v", err)
}
err = backfillLegacySchemaIfNeeded(context.Background(), tx, []string{"0001_init.sql"}, applied)
if err == nil || err.Error() != "legacy sqlite schema is partially applied without schema_migrations" {
t.Fatalf("backfillLegacySchemaIfNeeded() error = %v, want partial legacy schema error", err)
}
}
func TestRollbackMigrationReturnsOriginalErrorWhenRollbackSucceeds(t *testing.T) {
store := openTestDB(t)
tx, err := store.SQLDB().BeginTx(context.Background(), nil)
if err != nil {
t.Fatalf("BeginTx() error = %v", err)
}
baseErr := errors.New("apply failed")
if err := rollbackMigration(tx, baseErr); !errors.Is(err, baseErr) {
t.Fatalf("rollbackMigration() error = %v, want original error", err)
}
}

View File

@@ -210,6 +210,30 @@ func TestHostsRepoGetByHostIDNotFound(t *testing.T) {
}
}
func TestHostsRepoGetByBaseURL(t *testing.T) {
store := openTestDB(t)
createTestHostWithBaseURL(t, store, "host-base-url", "https://base-url.example.com")
got, err := store.Hosts().GetByBaseURL(context.Background(), "https://base-url.example.com")
if err != nil {
t.Fatalf("GetByBaseURL() error = %v", err)
}
if got.HostID != "host-base-url" {
t.Fatalf("GetByBaseURL() host_id = %q, want host-base-url", got.HostID)
}
}
func TestHostsRepoGetByBaseURLErrors(t *testing.T) {
store := openTestDB(t)
if _, err := store.Hosts().GetByBaseURL(context.Background(), ""); err == nil {
t.Fatal("GetByBaseURL(\"\") error = nil, want error")
}
if _, err := store.Hosts().GetByBaseURL(context.Background(), "https://missing.example.com"); !errors.Is(err, sql.ErrNoRows) {
t.Fatalf("GetByBaseURL(missing) error = %v, want sql.ErrNoRows", err)
}
}
func TestHostsRepoListAll(t *testing.T) {
store := openTestDB(t)
@@ -273,6 +297,52 @@ func TestHostsRepoUpdateProbeByHostID(t *testing.T) {
}
}
func TestHostsRepoUpdateConnectionByHostID(t *testing.T) {
store := openTestDB(t)
createTestHost(t, store)
if err := store.Hosts().UpdateConnectionByHostID(context.Background(), "host-"+sanitizeTestName(t.Name()), "https://updated.example.com", "0.3.0", "", "", "token-1"); err != nil {
t.Fatalf("UpdateConnectionByHostID() error = %v", err)
}
host, err := store.Hosts().GetByHostID(context.Background(), "host-"+sanitizeTestName(t.Name()))
if err != nil {
t.Fatalf("GetByHostID() error = %v", err)
}
if host.BaseURL != "https://updated.example.com" {
t.Fatalf("BaseURL = %q, want updated URL", host.BaseURL)
}
if host.HostVersion != "0.3.0" {
t.Fatalf("HostVersion = %q, want 0.3.0", host.HostVersion)
}
if host.CapabilityProbeJSON != "{}" {
t.Fatalf("CapabilityProbeJSON = %q, want {}", host.CapabilityProbeJSON)
}
if host.AuthType != "apikey" {
t.Fatalf("AuthType = %q, want apikey", host.AuthType)
}
if host.AuthToken != "token-1" {
t.Fatalf("AuthToken = %q, want token-1", host.AuthToken)
}
}
func TestHostsRepoUpdateConnectionByHostIDErrors(t *testing.T) {
store := openTestDB(t)
if err := store.Hosts().UpdateConnectionByHostID(context.Background(), "", "https://example.com", "0.2.0", "{}", "apikey", "token"); err == nil {
t.Fatal("UpdateConnectionByHostID() empty host_id error = nil")
}
if err := store.Hosts().UpdateConnectionByHostID(context.Background(), "missing", "", "0.2.0", "{}", "apikey", "token"); err == nil {
t.Fatal("UpdateConnectionByHostID() empty base_url error = nil")
}
if err := store.Hosts().UpdateConnectionByHostID(context.Background(), "missing", "https://example.com", "", "{}", "apikey", "token"); err == nil {
t.Fatal("UpdateConnectionByHostID() empty host_version error = nil")
}
if err := store.Hosts().UpdateConnectionByHostID(context.Background(), "missing", "https://example.com", "0.2.0", "{}", "apikey", "token"); err == nil {
t.Fatal("UpdateConnectionByHostID() missing host error = nil")
}
}
func TestHostsRepoDeleteByHostIDNotFound(t *testing.T) {
store := openTestDB(t)
err := store.Hosts().DeleteByHostID(context.Background(), "nonexistent")

View File

@@ -102,6 +102,148 @@ func TestImportBatchesRepoListByProviderID(t *testing.T) {
}
}
func TestImportBatchesRepoGetLatestByProviderIDAndHostID(t *testing.T) {
store := openTestDB(t)
hostA := createTestHost(t, store)
hostB := createTestHostWithBaseURL(t, store, "host-b-"+sanitizeTestName(t.Name()), "https://host-b.example.com")
packID := createTestPack(t, store)
providerID := createTestProviderWithPack(t, store, packID)
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packID, ProviderID: providerID,
Mode: "partial", BatchStatus: "running", AccessStatus: "pending",
}); err != nil {
t.Fatalf("Create(hostA older) error = %v", err)
}
latestA, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packID, ProviderID: providerID,
Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
})
if err != nil {
t.Fatalf("Create(hostA newer) error = %v", err)
}
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packID, ProviderID: providerID,
Mode: "partial", BatchStatus: "partially_succeeded", AccessStatus: "degraded",
}); err != nil {
t.Fatalf("Create(hostB) error = %v", err)
}
got, err := store.ImportBatches().GetLatestByProviderIDAndHostID(context.Background(), providerID, hostA)
if err != nil {
t.Fatalf("GetLatestByProviderIDAndHostID() error = %v", err)
}
if got.ID != latestA {
t.Fatalf("GetLatestByProviderIDAndHostID() id = %d, want %d", got.ID, latestA)
}
}
func TestImportBatchesRepoListByProviderIDAndHostID(t *testing.T) {
store := openTestDB(t)
hostA := createTestHost(t, store)
hostB := createTestHostWithBaseURL(t, store, "host-b-"+sanitizeTestName(t.Name()), "https://host-b2.example.com")
packID := createTestPack(t, store)
providerID := createTestProviderWithPack(t, store, packID)
olderA, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packID, ProviderID: providerID,
Mode: "partial", BatchStatus: "running", AccessStatus: "pending",
})
if err != nil {
t.Fatalf("Create(hostA older) error = %v", err)
}
newerA, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packID, ProviderID: providerID,
Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
})
if err != nil {
t.Fatalf("Create(hostA newer) error = %v", err)
}
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packID, ProviderID: providerID,
Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
}); err != nil {
t.Fatalf("Create(hostB) error = %v", err)
}
batches, err := store.ImportBatches().ListByProviderIDAndHostID(context.Background(), providerID, hostA)
if err != nil {
t.Fatalf("ListByProviderIDAndHostID() error = %v", err)
}
if len(batches) != 2 {
t.Fatalf("ListByProviderIDAndHostID() len = %d, want 2", len(batches))
}
if batches[0].ID != newerA || batches[1].ID != olderA {
t.Fatalf("ListByProviderIDAndHostID() ids = [%d %d], want [%d %d]", batches[0].ID, batches[1].ID, newerA, olderA)
}
}
func TestImportBatchesRepoListLatestReconcilable(t *testing.T) {
store := openTestDB(t)
hostA := createTestHost(t, store)
hostB := createTestHostWithBaseURL(t, store, "host-b-"+sanitizeTestName(t.Name()), "https://host-b3.example.com")
packAID := createTestPack(t, store)
packBID := createTestPackWithSuffix(t, store, "reconcile")
providerA := createTestProviderWithPack(t, store, packAID)
providerB := createTestProviderWithPack(t, store, packBID)
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packAID, ProviderID: providerA,
Mode: "partial", BatchStatus: "running", AccessStatus: "pending",
}); err != nil {
t.Fatalf("Create(providerA older) error = %v", err)
}
latestA, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packAID, ProviderID: providerA,
Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
})
if err != nil {
t.Fatalf("Create(providerA latest) error = %v", err)
}
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packBID, ProviderID: providerB,
Mode: "strict", BatchStatus: "partially_succeeded", AccessStatus: "degraded",
}); err != nil {
t.Fatalf("Create(providerB latest) error = %v", err)
}
if _, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packBID, ProviderID: providerB,
Mode: "strict", BatchStatus: "failed", AccessStatus: "broken",
}); err != nil {
t.Fatalf("Create(providerB newer failed) error = %v", err)
}
batches, err := store.ImportBatches().ListLatestReconcilable(context.Background())
if err != nil {
t.Fatalf("ListLatestReconcilable() error = %v", err)
}
if len(batches) != 1 {
t.Fatalf("ListLatestReconcilable() len = %d, want 1 because providerB latest batch is not reconcilable", len(batches))
}
if batches[0].ID != latestA {
t.Fatalf("ListLatestReconcilable() id = %d, want %d", batches[0].ID, latestA)
}
batchID, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packBID, ProviderID: providerB,
Mode: "strict", BatchStatus: "partially_succeeded", AccessStatus: "degraded",
})
if err != nil {
t.Fatalf("Create(providerB new reconcilable) error = %v", err)
}
batches, err = store.ImportBatches().ListLatestReconcilable(context.Background())
if err != nil {
t.Fatalf("ListLatestReconcilable() second call error = %v", err)
}
if len(batches) != 2 {
t.Fatalf("ListLatestReconcilable() second len = %d, want 2", len(batches))
}
if batches[0].ID != batchID || batches[1].ID != latestA {
t.Fatalf("ListLatestReconcilable() ids = [%d %d], want [%d %d]", batches[0].ID, batches[1].ID, batchID, latestA)
}
}
func TestImportBatchesRepoGetByIDNotFound(t *testing.T) {
store := openTestDB(t)
_, err := store.ImportBatches().GetByID(context.Background(), 999)
@@ -265,6 +407,102 @@ func TestManagedResourcesRepoGetByBatchIDEmpty(t *testing.T) {
}
}
func TestManagedResourcesRepoGetByResourceIdentity(t *testing.T) {
store := openTestDB(t)
batchID := createTestBatch(t, store)
batch, err := store.ImportBatches().GetByID(context.Background(), batchID)
if err != nil {
t.Fatalf("GetByID() error = %v", err)
}
if _, err := store.ManagedResources().Create(context.Background(), ManagedResource{
BatchID: batchID, HostID: batch.HostID, ResourceType: "channel", HostResourceID: "channel-1", ResourceName: "Channel 1",
}); err != nil {
t.Fatalf("Create() error = %v", err)
}
resource, err := store.ManagedResources().GetByResourceIdentity(context.Background(), batch.HostID, "channel", "channel-1")
if err != nil {
t.Fatalf("GetByResourceIdentity() error = %v", err)
}
if resource.ResourceName != "Channel 1" {
t.Fatalf("GetByResourceIdentity() resource_name = %q, want Channel 1", resource.ResourceName)
}
}
func TestManagedResourcesRepoListByProviderScopes(t *testing.T) {
store := openTestDB(t)
hostA := createTestHost(t, store)
hostB := createTestHostWithBaseURL(t, store, "host-b-"+sanitizeTestName(t.Name()), "https://managed-host-b.example.com")
packID := createTestPack(t, store)
providerID := createTestProviderWithPack(t, store, packID)
otherProviderID := createTestProviderWithPack(t, store, createTestPackWithSuffix(t, store, "managed"))
batchA, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostA, PackID: packID, ProviderID: providerID,
Mode: "partial", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
})
if err != nil {
t.Fatalf("Create(batchA) error = %v", err)
}
batchB, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packID, ProviderID: providerID,
Mode: "partial", BatchStatus: "partially_succeeded", AccessStatus: "degraded",
})
if err != nil {
t.Fatalf("Create(batchB) error = %v", err)
}
otherBatch, err := store.ImportBatches().Create(context.Background(), ImportBatch{
HostID: hostB, PackID: packID, ProviderID: otherProviderID,
Mode: "partial", BatchStatus: "succeeded", AccessStatus: "subscription_ready",
})
if err != nil {
t.Fatalf("Create(otherBatch) error = %v", err)
}
for _, resource := range []ManagedResource{
{BatchID: batchA, HostID: hostA, ResourceType: "group", HostResourceID: "g-a", ResourceName: "Group A"},
{BatchID: batchB, HostID: hostB, ResourceType: "account", HostResourceID: "a-b", ResourceName: "Account B"},
{BatchID: otherBatch, HostID: hostB, ResourceType: "channel", HostResourceID: "c-other", ResourceName: "Other"},
} {
if _, err := store.ManagedResources().Create(context.Background(), resource); err != nil {
t.Fatalf("Create(%s) error = %v", resource.HostResourceID, err)
}
}
byProvider, err := store.ManagedResources().ListByProviderID(context.Background(), providerID)
if err != nil {
t.Fatalf("ListByProviderID() error = %v", err)
}
if len(byProvider) != 2 {
t.Fatalf("ListByProviderID() len = %d, want 2", len(byProvider))
}
byProviderHost, err := store.ManagedResources().ListByProviderIDAndHostID(context.Background(), providerID, hostB)
if err != nil {
t.Fatalf("ListByProviderIDAndHostID() error = %v", err)
}
if len(byProviderHost) != 1 {
t.Fatalf("ListByProviderIDAndHostID() len = %d, want 1", len(byProviderHost))
}
if byProviderHost[0].HostResourceID != "a-b" {
t.Fatalf("ListByProviderIDAndHostID() resource = %q, want a-b", byProviderHost[0].HostResourceID)
}
}
func TestManagedResourcesRepoQueryValidationErrors(t *testing.T) {
store := openTestDB(t)
if _, err := store.ManagedResources().GetByResourceIdentity(context.Background(), 0, "group", "g-1"); err == nil {
t.Fatal("GetByResourceIdentity() host_id=0 error = nil")
}
if _, err := store.ManagedResources().ListByProviderID(context.Background(), 0); err == nil {
t.Fatal("ListByProviderID() provider_id=0 error = nil")
}
if _, err := store.ManagedResources().ListByProviderIDAndHostID(context.Background(), 1, 0); err == nil {
t.Fatal("ListByProviderIDAndHostID() host_id=0 error = nil")
}
}
func TestManagedResourcesRepoValidationErrors(t *testing.T) {
store := openTestDB(t)
for _, tt := range []struct {

View File

@@ -0,0 +1,88 @@
package sqlite
import (
"context"
"testing"
)
func TestImportRunItemEventsRepoAppendValidationAndDefaults(t *testing.T) {
t.Parallel()
ctx := context.Background()
store := openTestDB(t)
repo := store.ImportRunEvents()
if err := repo.Append(ctx, ImportRunItemEvent{}); err == nil || err.Error() != "event_id is required" {
t.Fatalf("Append() error = %v, want event_id is required", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{EventID: "evt-1"}); err == nil || err.Error() != "run_id is required" {
t.Fatalf("Append() error = %v, want run_id is required", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{EventID: "evt-1", RunID: "run-1"}); err == nil || err.Error() != "item_id is required" {
t.Fatalf("Append() error = %v, want item_id is required", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{EventID: "evt-1", RunID: "run-1", ItemID: "item-1"}); err == nil || err.Error() != "event_type is required" {
t.Fatalf("Append() error = %v, want event_type is required", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{EventID: "evt-1", RunID: "run-1", ItemID: "item-1", EventType: "confirm"}); err == nil || err.Error() != "stage is required" {
t.Fatalf("Append() error = %v, want stage is required", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{EventID: "evt-1", RunID: "run-1", ItemID: "item-1", EventType: "confirm", Stage: "confirm"}); err == nil || err.Error() != "message is required" {
t.Fatalf("Append() error = %v, want message is required", err)
}
if err := store.ImportRuns().Create(ctx, ImportRun{
RunID: "run-1",
HostID: "host-1",
Mode: "strict",
AccessMode: "subscription",
State: "running",
}); err != nil {
t.Fatalf("ImportRuns().Create() error = %v", err)
}
if err := store.ImportRunItems().Create(ctx, ImportRunItem{
ItemID: "item-1",
RunID: "run-1",
BaseURL: "https://api.example.com/v1",
ProviderID: "provider-1",
APIKeyFingerprint: "fp-1",
CurrentStage: "confirm",
ConfirmationStatus: "pending",
AccessStatus: "unknown",
MatchedAccountState: "active",
AccountResolution: "created",
}); err != nil {
t.Fatalf("ImportRunItems().Create() error = %v", err)
}
if err := repo.Append(ctx, ImportRunItemEvent{
EventID: "evt-1",
RunID: "run-1",
ItemID: "item-1",
EventType: "confirm",
Stage: "confirm",
Message: "scheduled",
}); err != nil {
t.Fatalf("Append() error = %v", err)
}
events, err := repo.ListByItemID(ctx, "item-1")
if err != nil {
t.Fatalf("ListByItemID() error = %v", err)
}
if len(events) != 1 {
t.Fatalf("len(events) = %d, want 1", len(events))
}
if events[0].PayloadJSON != "{}" {
t.Fatalf("PayloadJSON = %q, want {}", events[0].PayloadJSON)
}
}
func TestImportRunItemEventsRepoListByItemIDValidation(t *testing.T) {
t.Parallel()
_, err := openTestDB(t).ImportRunEvents().ListByItemID(context.Background(), "")
if err == nil || err.Error() != "item_id is required" {
t.Fatalf("ListByItemID() error = %v, want item_id is required", err)
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"reflect"
"testing"
"time"
)
func TestRunStateStore(t *testing.T) {
@@ -169,3 +170,182 @@ func TestRunStateStore(t *testing.T) {
t.Fatalf("items[0].AdvisoryMessagesJSON = %q, want advisory json", items[0].AdvisoryMessagesJSON)
}
}
func TestImportRunsRepoList(t *testing.T) {
t.Parallel()
ctx := context.Background()
store := openTestDB(t)
for _, run := range []ImportRun{
{RunID: "run-a", HostID: "host-a", Mode: "strict", AccessMode: "subscription", State: "running"},
{RunID: "run-b", HostID: "host-b", Mode: "partial", AccessMode: "self_service", State: "completed"},
} {
if err := store.ImportRuns().Create(ctx, run); err != nil {
t.Fatalf("ImportRuns().Create(%q) error = %v", run.RunID, err)
}
}
runs, err := store.ImportRuns().List(ctx, 1)
if err != nil {
t.Fatalf("ImportRuns().List(limit=1) error = %v", err)
}
if len(runs) != 1 {
t.Fatalf("ImportRuns().List(limit=1) len = %d, want 1", len(runs))
}
runs, err = store.ImportRuns().List(ctx, 0)
if err != nil {
t.Fatalf("ImportRuns().List(limit=0) error = %v", err)
}
if len(runs) != 2 {
t.Fatalf("ImportRuns().List(limit=0) len = %d, want 2", len(runs))
}
}
func TestImportRunItemsRepoCreateUpdateAndLease(t *testing.T) {
t.Parallel()
ctx := context.Background()
store := openTestDB(t)
run := ImportRun{RunID: "run-lease", HostID: "host-lease", Mode: "strict", AccessMode: "subscription", State: "running"}
if err := store.ImportRuns().Create(ctx, run); err != nil {
t.Fatalf("ImportRuns().Create() error = %v", err)
}
item := ImportRunItem{
ItemID: "item-lease",
RunID: "run-lease",
BaseURL: "https://api.example.com/v1",
ProviderID: "provider-lease",
APIKeyFingerprint: "fp_lease",
CurrentStage: "confirm",
ConfirmationStatus: "pending",
AccessStatus: "unknown",
MatchedAccountState: "active",
AccountResolution: "created",
RequestedModelsJSON: `["model-a"]`,
AdvisoryMessagesJSON: `["first"]`,
}
if err := store.ImportRunItems().Create(ctx, item); err != nil {
t.Fatalf("ImportRunItems().Create() error = %v", err)
}
item.ConfirmationStatus = "confirmed"
item.CurrentStage = "validate"
item.AccessStatus = "active"
item.AdvisoryMessagesJSON = `["updated"]`
if err := store.ImportRunItems().Update(ctx, item); err != nil {
t.Fatalf("ImportRunItems().Update() error = %v", err)
}
got, err := store.ImportRunItems().GetByItemID(ctx, "item-lease")
if err != nil {
t.Fatalf("ImportRunItems().GetByItemID() error = %v", err)
}
if got.CurrentStage != "validate" || got.ConfirmationStatus != "confirmed" {
t.Fatalf("updated item = %+v, want validate/confirmed", got)
}
leaseItem := item
leaseItem.ItemID = "item-pending"
leaseItem.CurrentStage = "confirm"
leaseItem.ConfirmationStatus = "pending"
leaseItem.AccessStatus = "unknown"
if err := store.ImportRunItems().Create(ctx, leaseItem); err != nil {
t.Fatalf("ImportRunItems().Create(item-pending) error = %v", err)
}
now := time.Date(2026, 5, 23, 1, 2, 3, 0, time.UTC)
leased, ok, err := store.ImportRunItems().TryAcquireConfirmationLease(ctx, "item-pending", "worker-1", now, 2*time.Minute)
if err != nil {
t.Fatalf("TryAcquireConfirmationLease() error = %v", err)
}
if !ok {
t.Fatal("TryAcquireConfirmationLease() ok = false, want true")
}
if leased.LeaseOwner != "worker-1" {
t.Fatalf("LeaseOwner = %q, want worker-1", leased.LeaseOwner)
}
if leased.ConfirmationAttempts != 1 {
t.Fatalf("ConfirmationAttempts = %d, want 1", leased.ConfirmationAttempts)
}
_, ok, err = store.ImportRunItems().TryAcquireConfirmationLease(ctx, "item-pending", "worker-2", now.Add(30*time.Second), time.Minute)
if err != nil {
t.Fatalf("TryAcquireConfirmationLease(second) error = %v", err)
}
if ok {
t.Fatal("TryAcquireConfirmationLease(second) ok = true, want false while lease active")
}
}
func TestImportRunEventsRepoCreateAndHelpers(t *testing.T) {
t.Parallel()
ctx := context.Background()
store := openTestDB(t)
if err := store.ImportRuns().Create(ctx, ImportRun{
RunID: "run-events",
HostID: "host-events",
Mode: "strict",
AccessMode: "subscription",
State: "running",
}); err != nil {
t.Fatalf("ImportRuns().Create() error = %v", err)
}
if err := store.ImportRunItems().Create(ctx, ImportRunItem{
ItemID: "item-events",
RunID: "run-events",
BaseURL: "https://api.example.com/v1",
ProviderID: "provider-events",
APIKeyFingerprint: "fp_events",
CurrentStage: "confirm",
ConfirmationStatus: "pending",
AccessStatus: "unknown",
MatchedAccountState: "active",
AccountResolution: "created",
}); err != nil {
t.Fatalf("ImportRunItems().Create() error = %v", err)
}
event := ImportRunItemEvent{
EventID: "evt-create",
RunID: "run-events",
ItemID: "item-events",
EventType: "confirmation",
Stage: "confirm",
Message: "created by wrapper",
}
if err := store.ImportRunEvents().Create(ctx, event); err != nil {
t.Fatalf("ImportRunEvents().Create() error = %v", err)
}
if err := store.ImportRunItemEvents().Create(ctx, ImportRunItemEvent{
EventID: "evt-alias",
RunID: "run-events",
ItemID: "item-events",
EventType: "alias",
Stage: "confirm",
Message: "created by alias accessor",
}); err != nil {
t.Fatalf("ImportRunItemEvents().Create() error = %v", err)
}
events, err := store.ImportRunEvents().ListByItemID(ctx, "item-events")
if err != nil {
t.Fatalf("ImportRunEvents().ListByItemID() error = %v", err)
}
if len(events) != 2 {
t.Fatalf("ImportRunEvents().ListByItemID() len = %d, want 2", len(events))
}
nullable := sqlNullInt64{Int64: 42, Valid: true}
if ptr := nullable.ptr(); ptr == nil || *ptr != 42 {
t.Fatalf("sqlNullInt64.ptr() = %#v, want 42", ptr)
}
if ptr := (sqlNullInt64{}).ptr(); ptr != nil {
t.Fatalf("sqlNullInt64{}.ptr() = %#v, want nil", ptr)
}
}

View File

@@ -157,3 +157,33 @@ func TestPacksRepoGetByPackIDNotFound(t *testing.T) {
t.Fatalf("GetByPackID() error = %v, want sql.ErrNoRows", err)
}
}
func TestPacksRepoListAll(t *testing.T) {
store := openTestDB(t)
packs, err := store.Packs().ListAll(context.Background())
if err != nil {
t.Fatalf("ListAll() empty error = %v", err)
}
if len(packs) != 0 {
t.Fatalf("ListAll() empty len = %d, want 0", len(packs))
}
if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-a", Version: "1.0.0", Checksum: "chk-a"}); err != nil {
t.Fatalf("Create(pack-a) error = %v", err)
}
if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-b", Version: "1.1.0", Checksum: "chk-b"}); err != nil {
t.Fatalf("Create(pack-b) error = %v", err)
}
packs, err = store.Packs().ListAll(context.Background())
if err != nil {
t.Fatalf("ListAll() error = %v", err)
}
if len(packs) != 2 {
t.Fatalf("ListAll() len = %d, want 2", len(packs))
}
if packs[0].PackID != "pack-a" || packs[1].PackID != "pack-b" {
t.Fatalf("ListAll() ids = [%q %q], want [pack-a pack-b]", packs[0].PackID, packs[1].PackID)
}
}

View File

@@ -55,6 +55,33 @@ func TestProvidersRepoListByProviderID(t *testing.T) {
}
}
func TestProvidersRepoListByPackID(t *testing.T) {
store := openTestDB(t)
packID := createTestPack(t, store)
otherPackID := createTestPackWithSuffix(t, store, "other")
if _, err := store.Providers().Create(context.Background(), Provider{PackID: packID, ProviderID: "provider-a", DisplayName: "A", BaseURL: "https://a.example.com", Platform: "openai"}); err != nil {
t.Fatalf("Create(provider-a) error = %v", err)
}
if _, err := store.Providers().Create(context.Background(), Provider{PackID: packID, ProviderID: "provider-b", DisplayName: "B", BaseURL: "https://b.example.com", Platform: "openai"}); err != nil {
t.Fatalf("Create(provider-b) error = %v", err)
}
if _, err := store.Providers().Create(context.Background(), Provider{PackID: otherPackID, ProviderID: "provider-c", DisplayName: "C", BaseURL: "https://c.example.com", Platform: "openai"}); err != nil {
t.Fatalf("Create(provider-c) error = %v", err)
}
providers, err := store.Providers().ListByPackID(context.Background(), packID)
if err != nil {
t.Fatalf("ListByPackID() error = %v", err)
}
if len(providers) != 2 {
t.Fatalf("ListByPackID() count = %d, want 2", len(providers))
}
if providers[0].ProviderID != "provider-a" || providers[1].ProviderID != "provider-b" {
t.Fatalf("ListByPackID() provider ids = [%q %q], want [provider-a provider-b]", providers[0].ProviderID, providers[1].ProviderID)
}
}
func createTestPackWithSuffix(t *testing.T, store *DB, suffix string) int64 {
t.Helper()
id, err := store.Packs().Create(context.Background(), Pack{
@@ -145,6 +172,44 @@ func TestProvidersRepoGetByPackIDAndProviderIDNotFound(t *testing.T) {
}
}
func TestProvidersRepoGetByID(t *testing.T) {
store := openTestDB(t)
packID := createTestPack(t, store)
providerID, err := store.Providers().Create(context.Background(), Provider{
PackID: packID,
ProviderID: "provider-id-lookup",
DisplayName: "Lookup",
BaseURL: "https://lookup.example.com",
Platform: "openai",
})
if err != nil {
t.Fatalf("Create() error = %v", err)
}
got, err := store.Providers().GetByID(context.Background(), providerID)
if err != nil {
t.Fatalf("GetByID() error = %v", err)
}
if got.ProviderID != "provider-id-lookup" {
t.Fatalf("GetByID() provider_id = %q, want provider-id-lookup", got.ProviderID)
}
}
func TestProvidersRepoGetByIDErrors(t *testing.T) {
store := openTestDB(t)
if _, err := store.Providers().GetByID(context.Background(), 0); err == nil {
t.Fatal("GetByID(0) error = nil, want error")
}
if _, err := store.Providers().ListByPackID(context.Background(), 0); err == nil {
t.Fatal("ListByPackID(0) error = nil, want error")
}
if _, err := store.Providers().GetByID(context.Background(), 999); !errors.Is(err, sql.ErrNoRows) {
t.Fatalf("GetByID(999) error = %v, want sql.ErrNoRows", err)
}
}
func TestProvidersRepoGetByPackIDEmptyError(t *testing.T) {
store := openTestDB(t)
_, err := store.Providers().GetByPackIDAndProviderID(context.Background(), 0, "p")