package store import ( "context" "testing" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "github.com/vasyakrg/dns-autoresolver/internal/provider" "github.com/vasyakrg/dns-autoresolver/internal/store/db" "github.com/vasyakrg/dns-autoresolver/internal/store/dto" ) // defaultProject is the seed default tenant project (see migrations/0001_init.sql). var defaultProject = uuid.MustParse("00000000-0000-0000-0000-000000000002") func newStore(t *testing.T) (*Store, context.Context) { dsn := startPostgres(t) pool, err := pgxpool.New(context.Background(), dsn) if err != nil { t.Fatal(err) } t.Cleanup(pool.Close) return New(pool), context.Background() } func TestAccountCRUD(t *testing.T) { s, ctx := newStore(t) acc, err := s.Queries().CreateAccount(ctx, db.CreateAccountParams{ ID: uuid.New(), ProjectID: defaultProject, Provider: "selectel", SecretEnc: "enc-blob", Comment: "prod", }) if err != nil { t.Fatal(err) } got, err := s.Queries().GetAccount(ctx, db.GetAccountParams{ID: acc.ID, ProjectID: defaultProject}) if err != nil || got.Provider != "selectel" || got.SecretEnc != "enc-blob" { t.Fatalf("get mismatch: %+v err=%v", got, err) } list, err := s.Queries().ListAccounts(ctx, defaultProject) if err != nil || len(list) != 1 { t.Fatalf("list mismatch: %+v err=%v", list, err) } if err := s.Queries().DeleteAccount(ctx, db.DeleteAccountParams{ID: acc.ID, ProjectID: defaultProject}); err != nil { t.Fatal(err) } if _, err := s.Queries().GetAccount(ctx, db.GetAccountParams{ID: acc.ID, ProjectID: defaultProject}); err == nil { t.Fatal("expected error after delete, got nil") } } func TestTemplateJSONBRoundTrip(t *testing.T) { s, ctx := newStore(t) doc := dto.TemplateDoc{Records: []dto.RecordDTO{ {Type: "A", Name: "www.example.com.", TTL: 300, Values: []string{"1.2.3.4"}}, {Type: "SRV", Name: "_autodiscover._tcp.example.com.", TTL: 3600, Values: []string{"0 0 443 mail.example.com."}}, }} tpl, err := s.Queries().CreateTemplate(ctx, db.CreateTemplateParams{ ID: uuid.New(), ProjectID: defaultProject, Name: "base", Doc: &doc, }) if err != nil { t.Fatal(err) } got, err := s.Queries().GetTemplate(ctx, db.GetTemplateParams{ID: tpl.ID, ProjectID: defaultProject}) if err != nil { t.Fatal(err) } if got.Doc == nil || len(got.Doc.Records) != 2 || got.Doc.Records[1].Type != "SRV" { t.Fatalf("jsonb round-trip failed: %+v", got.Doc) } doc2 := dto.TemplateDoc{Records: []dto.RecordDTO{ {Type: "A", Name: "www.example.com.", TTL: 60, Values: []string{"5.6.7.8"}}, }} updated, err := s.Queries().UpdateTemplate(ctx, db.UpdateTemplateParams{ ID: tpl.ID, ProjectID: defaultProject, Name: "base-v2", Doc: &doc2, }) if err != nil { t.Fatal(err) } if updated.Version != tpl.Version+1 || updated.Doc == nil || len(updated.Doc.Records) != 1 { t.Fatalf("update mismatch: %+v", updated) } if err := s.Queries().DeleteTemplate(ctx, db.DeleteTemplateParams{ID: tpl.ID, ProjectID: defaultProject}); err != nil { t.Fatal(err) } } func TestImportDomains_CommitsAllOnSuccess(t *testing.T) { s, ctx := newStore(t) acc, err := s.Queries().CreateAccount(ctx, db.CreateAccountParams{ ID: uuid.New(), ProjectID: defaultProject, Provider: "selectel", SecretEnc: "enc-blob", }) if err != nil { t.Fatal(err) } zones := []provider.Zone{ {ID: "z1", Name: "a.example.com"}, {ID: "z2", Name: "b.example.com"}, } doms, err := s.ImportDomains(ctx, defaultProject, acc.ID, zones) if err != nil { t.Fatal(err) } if len(doms) != 2 { t.Fatalf("expected 2 domains returned, got %d", len(doms)) } list, err := s.ListDomains(ctx, defaultProject) if err != nil { t.Fatal(err) } if len(list) != 2 { t.Fatalf("expected 2 persisted domains, got %d", len(list)) } } // TestImportDomains_RollsBackAllOnError verifies the transactional contract: // if any zone in the batch fails to insert (here, an FK violation because // the account doesn't exist), none of the batch is left committed. func TestImportDomains_RollsBackAllOnError(t *testing.T) { s, ctx := newStore(t) bogusAccountID := uuid.New() // no matching provider_accounts row zones := []provider.Zone{ {ID: "z1", Name: "a.example.com"}, {ID: "z2", Name: "b.example.com"}, } if _, err := s.ImportDomains(ctx, defaultProject, bogusAccountID, zones); err == nil { t.Fatal("expected FK violation error, got nil") } list, err := s.ListDomains(ctx, defaultProject) if err != nil { t.Fatal(err) } if len(list) != 0 { t.Fatalf("expected 0 domains after rollback, got %d", len(list)) } }