proxy-pool/internal/adapters/postgresadmin/migrations_test.go

113 lines
3.4 KiB
Go

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) != 1 {
t.Fatalf("len(Migrations()) = %d, want 1", len(migrations))
}
if migrations[0].Version != 1 || migrations[0].Name != "admin_state" {
t.Fatalf("migration metadata = %+v", migrations[0])
}
normalized := strings.TrimSpace(migrations[0].SQL)
if !strings.HasPrefix(normalized, "BEGIN;") || !strings.HasSuffix(normalized, "COMMIT;") {
t.Fatalf("migration is not transaction wrapped: %q", 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 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)
}
}
}