package sqlite import ( "context" "testing" ) func TestImportBatchesRepoCreateAndGet(t *testing.T) { store := openTestDB(t) hostID := createTestHost(t, store) packID := createTestPack(t, store) providerID := createTestProviderWithPack(t, store, packID) id, err := store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending", }) if err != nil { t.Fatalf("Create() error = %v", err) } if id <= 0 { t.Fatalf("Create() id = %d, want positive", id) } got, _ := store.ImportBatches().GetByID(context.Background(), id) if got.Mode != "partial" || got.BatchStatus != "running" { t.Fatalf("GetByID() = %+v, want running batch", got) } } func TestImportBatchesRepoUpdateStatus(t *testing.T) { store := openTestDB(t) hostID := createTestHost(t, store) packID := createTestPack(t, store) providerID := createTestProviderWithPack(t, store, packID) id, _ := store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending", }) err := store.ImportBatches().UpdateStatus(context.Background(), id, "succeeded", "subscription_ready") if err != nil { t.Fatalf("UpdateStatus() error = %v", err) } got, _ := store.ImportBatches().GetByID(context.Background(), id) if got.BatchStatus != "succeeded" || got.AccessStatus != "subscription_ready" { t.Fatalf("status = %+v, want succeeded/subscription_ready", got) } } func TestImportBatchesRepoGetLatestByProviderID(t *testing.T) { store := openTestDB(t) hostID := createTestHost(t, store) packID := createTestPack(t, store) providerID := createTestProviderWithPack(t, store, packID) store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending", }) id2, _ := store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready", }) got, _ := store.ImportBatches().GetLatestByProviderID(context.Background(), providerID) if got.ID != id2 { t.Fatalf("latest id = %d, want %d", got.ID, id2) } } func TestImportBatchesRepoListByProviderID(t *testing.T) { store := openTestDB(t) hostID := createTestHost(t, store) packID := createTestPack(t, store) providerID := createTestProviderWithPack(t, store, packID) olderID, err := store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending", }) if err != nil { t.Fatalf("Create older batch error = %v", err) } newerID, err := store.ImportBatches().Create(context.Background(), ImportBatch{ HostID: hostID, PackID: packID, ProviderID: providerID, Mode: "strict", BatchStatus: "succeeded", AccessStatus: "subscription_ready", }) if err != nil { t.Fatalf("Create newer batch error = %v", err) } batches, err := store.ImportBatches().ListByProviderID(context.Background(), providerID) if err != nil { t.Fatalf("ListByProviderID() error = %v", err) } if len(batches) != 2 { t.Fatalf("ListByProviderID() len = %d, want 2", len(batches)) } if batches[0].ID != newerID || batches[1].ID != olderID { t.Fatalf("ListByProviderID() ids = [%d %d], want [%d %d]", batches[0].ID, batches[1].ID, newerID, olderID) } } 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) if err == nil { t.Fatal("GetByID(999) error = nil, want error") } } func TestImportBatchesRepoCreateValidationErrors(t *testing.T) { store := openTestDB(t) for _, tt := range []struct { name string batch ImportBatch }{ {"host_id zero", ImportBatch{HostID: 0, PackID: 1, ProviderID: 1, Mode: "m", BatchStatus: "s", AccessStatus: "s"}}, {"pack_id zero", ImportBatch{HostID: 1, PackID: 0, ProviderID: 1, Mode: "m", BatchStatus: "s", AccessStatus: "s"}}, {"provider_id zero", ImportBatch{HostID: 1, PackID: 1, ProviderID: 0, Mode: "m", BatchStatus: "s", AccessStatus: "s"}}, {"empty mode", ImportBatch{HostID: 1, PackID: 1, ProviderID: 1, Mode: "", BatchStatus: "s", AccessStatus: "s"}}, {"empty batch_status", ImportBatch{HostID: 1, PackID: 1, ProviderID: 1, Mode: "m", BatchStatus: "", AccessStatus: "s"}}, {"empty access_status", ImportBatch{HostID: 1, PackID: 1, ProviderID: 1, Mode: "m", BatchStatus: "s", AccessStatus: ""}}, } { t.Run(tt.name, func(t *testing.T) { _, err := store.ImportBatches().Create(context.Background(), tt.batch) if err == nil { t.Fatal("Create() error = nil") } }) } } func TestImportBatchesRepoUpdateStatusValidation(t *testing.T) { store := openTestDB(t) if err := store.ImportBatches().UpdateStatus(context.Background(), 0, "s", "s"); err == nil { t.Fatal("UpdateStatus id=0 error = nil") } if err := store.ImportBatches().UpdateStatus(context.Background(), 1, "", "s"); err == nil { t.Fatal("UpdateStatus empty batch_status error = nil") } } // --- ImportBatchItems Repo Tests --- func TestImportBatchItemsRepoCreateAndGet(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) id, err := store.ImportBatchItems().Create(context.Background(), ImportBatchItem{ BatchID: batchID, KeyFingerprint: "sha256:abc", AccountStatus: "passed", ProbeSummaryJSON: `{"ok":true}`, }) if err != nil { t.Fatalf("Create() error = %v", err) } if id <= 0 { t.Fatalf("Create() id = %d, want positive", id) } items, _ := store.ImportBatchItems().GetByBatchID(context.Background(), batchID) if len(items) != 1 || items[0].AccountStatus != "passed" { t.Fatalf("items = %+v, want 1 with passed status", items) } } func TestImportBatchItemsRepoMultipleItems(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) for _, status := range []string{"passed", "failed"} { store.ImportBatchItems().Create(context.Background(), ImportBatchItem{ BatchID: batchID, KeyFingerprint: "sha256:" + status, AccountStatus: status, }) } items, _ := store.ImportBatchItems().GetByBatchID(context.Background(), batchID) if len(items) != 2 { t.Fatalf("count = %d, want 2", len(items)) } } func TestImportBatchItemsRepoUpdateResult(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) itemID, _ := store.ImportBatchItems().Create(context.Background(), ImportBatchItem{ BatchID: batchID, KeyFingerprint: "sha256:x", AccountStatus: "pending", }) store.ImportBatchItems().UpdateResult(context.Background(), itemID, "passed", `{"ok":true}`) items, _ := store.ImportBatchItems().GetByBatchID(context.Background(), batchID) if items[0].AccountStatus != "passed" { t.Fatalf("AccountStatus = %q, want passed", items[0].AccountStatus) } } func TestImportBatchItemsRepoGetByBatchIDEmpty(t *testing.T) { store := openTestDB(t) items, err := store.ImportBatchItems().GetByBatchID(context.Background(), 999) if err != nil { t.Fatalf("GetByBatchID() error = %v, want empty result", err) } if len(items) != 0 { t.Fatalf("count = %d, want 0", len(items)) } } func TestImportBatchItemsRepoValidation(t *testing.T) { store := openTestDB(t) _, err := store.ImportBatchItems().Create(context.Background(), ImportBatchItem{ BatchID: 0, KeyFingerprint: "k", AccountStatus: "s", }) if err == nil { t.Fatal("Create batch_id=0 error = nil") } } // --- Managed Resources Repo Tests --- func TestManagedResourcesRepoCreateAndGet(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) batch, _ := store.ImportBatches().GetByID(context.Background(), batchID) id, err := store.ManagedResources().Create(context.Background(), ManagedResource{ BatchID: batchID, HostID: batch.HostID, ResourceType: "group", HostResourceID: "g_01", ResourceName: "test-group", }) if err != nil { t.Fatalf("Create() error = %v", err) } resources, _ := store.ManagedResources().GetByBatchID(context.Background(), batchID) if len(resources) != 1 || resources[0].HostResourceID != "g_01" { t.Fatalf("resources = %+v, want 1 with g_01", resources) } _ = id } func TestManagedResourcesRepoMultipleResources(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) batch, _ := store.ImportBatches().GetByID(context.Background(), batchID) for _, r := range []ManagedResource{ {BatchID: batchID, HostID: batch.HostID, ResourceType: "group", HostResourceID: "g_01", ResourceName: "group-1"}, {BatchID: batchID, HostID: batch.HostID, ResourceType: "channel", HostResourceID: "c_01", ResourceName: "channel-1"}, {BatchID: batchID, HostID: batch.HostID, ResourceType: "account", HostResourceID: "a_01", ResourceName: "account-1"}, } { store.ManagedResources().Create(context.Background(), r) } resources, _ := store.ManagedResources().GetByBatchID(context.Background(), batchID) if len(resources) != 3 { t.Fatalf("count = %d, want 3", len(resources)) } } func TestManagedResourcesRepoGetByBatchIDEmpty(t *testing.T) { store := openTestDB(t) resources, _ := store.ManagedResources().GetByBatchID(context.Background(), 999) if len(resources) != 0 { t.Fatalf("count = %d, want 0", len(resources)) } } 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 { name string r ManagedResource }{ {"batch_id zero", ManagedResource{HostID: 1, ResourceType: "g", HostResourceID: "h", ResourceName: "n"}}, {"host_id zero", ManagedResource{BatchID: 1, ResourceType: "g", HostResourceID: "h", ResourceName: "n"}}, {"empty resource_type", ManagedResource{BatchID: 1, HostID: 1, HostResourceID: "h", ResourceName: "n"}}, {"empty host_resource_id", ManagedResource{BatchID: 1, HostID: 1, ResourceType: "g", ResourceName: "n"}}, {"empty resource_name", ManagedResource{BatchID: 1, HostID: 1, ResourceType: "g", HostResourceID: "h"}}, } { t.Run(tt.name, func(t *testing.T) { _, err := store.ManagedResources().Create(context.Background(), tt.r) if err == nil { t.Fatal("Create() error = nil") } }) } } // --- Probe Results Repo Tests --- func TestProbeResultsRepoCreateAndGet(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) itemID := createTestBatchItem(t, store, batchID) id, err := store.ProbeResults().Create(context.Background(), ProbeResult{ BatchItemID: itemID, ProbeType: "account_smoke", Status: "passed", SummaryJSON: `{"ok":true}`, }) if err != nil { t.Fatalf("Create() error = %v", err) } results, _ := store.ProbeResults().GetByBatchItemID(context.Background(), itemID) if len(results) != 1 || results[0].ProbeType != "account_smoke" { t.Fatalf("results = %+v, want 1 with account_smoke", results) } _ = id } func TestProbeResultsRepoMultipleResults(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) itemID := createTestBatchItem(t, store, batchID) for _, p := range []ProbeResult{ {BatchItemID: itemID, ProbeType: "account_smoke", Status: "passed", SummaryJSON: `{"ok":true}`}, {BatchItemID: itemID, ProbeType: "model_list", Status: "passed", SummaryJSON: `{"models":["m1"]}`}, } { store.ProbeResults().Create(context.Background(), p) } results, _ := store.ProbeResults().GetByBatchItemID(context.Background(), itemID) if len(results) != 2 { t.Fatalf("count = %d, want 2", len(results)) } } func TestProbeResultsRepoGetByBatchItemIDEmpty(t *testing.T) { store := openTestDB(t) results, _ := store.ProbeResults().GetByBatchItemID(context.Background(), 999) if len(results) != 0 { t.Fatalf("count = %d, want 0", len(results)) } } func TestProbeResultsRepoValidationErrors(t *testing.T) { store := openTestDB(t) for _, tt := range []struct { name string probe ProbeResult }{ {"batch_item_id zero", ProbeResult{ProbeType: "t", Status: "s"}}, {"empty probe_type", ProbeResult{BatchItemID: 1, Status: "s"}}, {"empty status", ProbeResult{BatchItemID: 1, ProbeType: "t"}}, } { t.Run(tt.name, func(t *testing.T) { _, err := store.ProbeResults().Create(context.Background(), tt.probe) if err == nil { t.Fatal("Create() error = nil") } }) } } // --- Access Closures Repo Tests --- func TestAccessClosureRecordsRepoCreateAndGet(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) id, err := store.AccessClosures().Create(context.Background(), AccessClosureRecord{ BatchID: batchID, ClosureType: "subscription", Status: "subscription_ready", DetailsJSON: `{"status_code":200}`, }) if err != nil { t.Fatalf("Create() error = %v", err) } records, _ := store.AccessClosures().GetByBatchID(context.Background(), batchID) if len(records) != 1 || records[0].ClosureType != "subscription" { t.Fatalf("records = %+v, want 1 subscription", records) } _ = id } func TestAccessClosureRecordsRepoMultiple(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) store.AccessClosures().Create(context.Background(), AccessClosureRecord{BatchID: batchID, ClosureType: "subscription", Status: "subscription_ready", DetailsJSON: "{}"}) store.AccessClosures().Create(context.Background(), AccessClosureRecord{BatchID: batchID, ClosureType: "self_service", Status: "self_service_ready", DetailsJSON: "{}"}) records, _ := store.AccessClosures().GetByBatchID(context.Background(), batchID) if len(records) != 2 { t.Fatalf("count = %d, want 2", len(records)) } } func TestAccessClosureRecordsRepoGetByBatchIDEmpty(t *testing.T) { store := openTestDB(t) records, _ := store.AccessClosures().GetByBatchID(context.Background(), 999) if len(records) != 0 { t.Fatalf("count = %d, want 0", len(records)) } } func TestAccessClosureRecordsRepoValidation(t *testing.T) { store := openTestDB(t) _, err := store.AccessClosures().Create(context.Background(), AccessClosureRecord{BatchID: 0, ClosureType: "t", Status: "s"}) if err == nil { t.Fatal("Create batch_id=0 error = nil") } } // --- Reconcile Runs Repo Tests --- func createTestProviderWithPack(t *testing.T, store *DB, packID int64) int64 { t.Helper() id, err := store.Providers().Create(context.Background(), Provider{ PackID: packID, ProviderID: "test-provider-" + sanitizeTestName(t.Name()), DisplayName: "TP", BaseURL: "https://tp.com", Platform: "openai", }) if err != nil { t.Fatalf("createTestProviderWithPack error = %v", err) } return id } func createTestProvider(t *testing.T, store *DB) int64 { t.Helper() packID := createTestPack(t, store) return createTestProviderWithPack(t, store, packID) } func TestReconcileRunsRepoCreateAndGet(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) batch, err := store.ImportBatches().GetByID(context.Background(), batchID) if err != nil { t.Fatalf("ImportBatches().GetByID() error = %v", err) } id, err := store.ReconcileRuns().Create(context.Background(), ReconcileRun{ BatchID: batchID, HostID: batch.HostID, ProviderID: batch.ProviderID, Status: "active", SummaryJSON: `{"drifted":false}`, }) if err != nil { t.Fatalf("Create() error = %v", err) } runs, _ := store.ReconcileRuns().GetByBatchID(context.Background(), batchID) if len(runs) != 1 || runs[0].Status != "active" { t.Fatalf("runs = %+v, want 1 active", runs) } _ = id } func TestReconcileRunsRepoMultipleRunsOrderedDesc(t *testing.T) { store := openTestDB(t) batchID := createTestBatch(t, store) batch, err := store.ImportBatches().GetByID(context.Background(), batchID) if err != nil { t.Fatalf("ImportBatches().GetByID() error = %v", err) } id1, _ := store.ReconcileRuns().Create(context.Background(), ReconcileRun{BatchID: batchID, HostID: batch.HostID, ProviderID: batch.ProviderID, Status: "first", SummaryJSON: "{}"}) id2, _ := store.ReconcileRuns().Create(context.Background(), ReconcileRun{BatchID: batchID, HostID: batch.HostID, ProviderID: batch.ProviderID, Status: "second", SummaryJSON: "{}"}) runs, _ := store.ReconcileRuns().GetByBatchID(context.Background(), batchID) if len(runs) != 2 || runs[0].ID != id2 || runs[1].ID != id1 { t.Fatalf("order: got %d, %d; want %d, %d (DESC)", runs[0].ID, runs[1].ID, id2, id1) } } func TestReconcileRunsRepoSeparatesHosts(t *testing.T) { store := openTestDB(t) hostA := createTestHost(t, store) hostB := createTestHostWithBaseURL(t, store, "host-b", "https://host-b.example.com") packID := createTestPack(t, store) providerID := createTestProviderWithPack(t, store, packID) batchA, err := store.ImportBatches().Create(context.Background(), ImportBatch{HostID: hostA, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending"}) if err != nil { t.Fatalf("ImportBatches().Create(hostA) error = %v", err) } batchB, err := store.ImportBatches().Create(context.Background(), ImportBatch{HostID: hostB, PackID: packID, ProviderID: providerID, Mode: "partial", BatchStatus: "running", AccessStatus: "pending"}) if err != nil { t.Fatalf("ImportBatches().Create(hostB) error = %v", err) } if _, err := store.ReconcileRuns().Create(context.Background(), ReconcileRun{BatchID: batchA, HostID: hostA, ProviderID: providerID, Status: "drifted", SummaryJSON: `{"host":"a"}`}); err != nil { t.Fatalf("Create(hostA) error = %v", err) } if _, err := store.ReconcileRuns().Create(context.Background(), ReconcileRun{BatchID: batchB, HostID: hostB, ProviderID: providerID, Status: "active", SummaryJSON: `{"host":"b"}`}); err != nil { t.Fatalf("Create(hostB) error = %v", err) } runs, _ := store.ReconcileRuns().GetByProviderIDAndHostID(context.Background(), providerID, hostA) if len(runs) != 1 { t.Fatalf("len(runs) = %d, want 1", len(runs)) } if runs[0].Status != "drifted" { t.Fatalf("runs[0].Status = %q, want drifted", runs[0].Status) } } func TestReconcileRunsRepoGetByProviderIDAndHostIDEmpty(t *testing.T) { store := openTestDB(t) runs, _ := store.ReconcileRuns().GetByProviderIDAndHostID(context.Background(), 999, 1) if len(runs) != 0 { t.Fatalf("count = %d, want 0", len(runs)) } } func TestReconcileRunsRepoGetByBatchIDEmpty(t *testing.T) { store := openTestDB(t) runs, _ := store.ReconcileRuns().GetByBatchID(context.Background(), 999) if len(runs) != 0 { t.Fatalf("count = %d, want 0", len(runs)) } } func TestReconcileRunsRepoValidation(t *testing.T) { store := openTestDB(t) hostID := createTestHost(t, store) _, err := store.ReconcileRuns().Create(context.Background(), ReconcileRun{BatchID: 1, HostID: hostID, ProviderID: 0, Status: "s"}) if err == nil { t.Fatal("Create provider_id=0 error = nil") } }