package postgresadmin import ( "context" "errors" "regexp" "sort" "strings" "testing" "proxy-pool/internal/domain/adminstate" ) func TestApplyMigrationsValidatesContextAndPool(t *testing.T) { t.Parallel() if err := ApplyMigrations(context.Background(), nil); !errors.Is(err, adminstate.ErrInvalidCommand) { t.Fatalf("ApplyMigrations(nil) error = %v, want ErrInvalidCommand", err) } ctx, cancel := context.WithCancel(context.Background()) cancel() if err := ApplyMigrations(ctx, nil); !errors.Is(err, context.Canceled) { t.Fatalf("ApplyMigrations(canceled) error = %v, want context.Canceled", err) } } func TestMigrationsAreOrderedTransactionalAndImmutable(t *testing.T) { t.Parallel() migrations := Migrations() if len(migrations) != 2 { t.Fatalf("len(Migrations()) = %d, want 2", len(migrations)) } if migrations[0].Version != 1 || migrations[0].Name != "admin_state" || migrations[1].Version != 2 || migrations[1].Name != "disable_routing" { t.Fatalf("migration metadata = %+v", migrations) } for _, migration := range migrations { normalized := strings.TrimSpace(migration.SQL) if !strings.HasPrefix(normalized, "BEGIN;") || !strings.HasSuffix(normalized, "COMMIT;") { t.Fatalf("migration %d is not transaction wrapped: %q", migration.Version, normalized) } } for index := 1; index < len(migrations); index++ { if migrations[index-1].Version >= migrations[index].Version { t.Fatalf("migration versions are not strictly increasing: %+v", migrations) } } migrations[0].SQL = "changed" if Migrations()[0].SQL == "changed" { t.Fatal("Migrations() returned mutable package storage") } } func TestRoutingDisableMigrationExtendsOnlyAuditActionConstraint(t *testing.T) { t.Parallel() lower := strings.ToLower(Migrations()[1].SQL) for _, required := range []string{ "alter table admin_audit_log", "drop constraint if exists admin_audit_log_action_check", "disable_routing", } { if !strings.Contains(lower, required) { t.Errorf("routing disable migration missing %q", required) } } for _, forbidden := range []string{"create table", "proxy", "credential", "extraction", "ownership", "idempotency"} { if strings.Contains(lower, forbidden) { t.Errorf("routing disable migration contains forbidden identifier %q", forbidden) } } } func TestAdminSchemaContainsOnlyManagementTables(t *testing.T) { t.Parallel() sql := Migrations()[0].SQL tablePattern := regexp.MustCompile(`(?im)^CREATE TABLE IF NOT EXISTS ([a-z][a-z0-9_]*)\s*\(`) matches := tablePattern.FindAllStringSubmatch(sql, -1) tables := make([]string, 0, len(matches)) for _, match := range matches { tables = append(tables, match[1]) } sort.Strings(tables) want := []string{ "admin_audit_log", "admin_outbox", "config_revisions", "control_revisions", "routing_admin_state", "upstream_admin_state", } if strings.Join(tables, ",") != strings.Join(want, ",") { t.Fatalf("created tables = %v, want %v", tables, want) } lower := strings.ToLower(sql) for _, forbidden := range []string{ "proxy_id", "proxy_host", "proxy_port", "credential", "password", "extraction_record", "ownership", "idempotency", } { if strings.Contains(lower, forbidden) { t.Errorf("migration contains forbidden detail identifier %q", forbidden) } } for _, destructive := range []string{"drop table", "truncate ", "delete from"} { if strings.Contains(lower, destructive) { t.Errorf("forward migration contains destructive statement %q", destructive) } } } func TestAdminSchemaContainsTransactionAndOutboxConstraints(t *testing.T) { t.Parallel() lower := strings.ToLower(Migrations()[0].SQL) for _, required := range []string{ "revision bigint generated by default as identity primary key", "config_version varchar(128) not null unique", "checksum char(64) not null", "upstreams text[] not null", "current_upstream varchar(128) not null", "request_id varchar(128) not null", "source_ip inet", "payload jsonb not null", "claim_owner varchar(128)", "claim_until timestamptz", "published_at timestamptz", "foreign key (revision) references control_revisions(revision)", "where published_at is null", } { if !strings.Contains(lower, required) { t.Errorf("migration missing required schema fragment %q", required) } } }