package lint_test import ( "os" "path/filepath" "testing" "github.com/hein/pgtidy/pkg/diagnostics" "github.com/hein/pgtidy/pkg/lint" ) func fixtureDir() string { return filepath.Join("..", "..", "testdata", "lint") } func checkFile(t *testing.T, name string) []diagnostics.Diagnostic { t.Helper() src, err := os.ReadFile(filepath.Join(fixtureDir(), name)) if err != nil { t.Fatalf("read %s: %v", name, err) } eng := lint.New() diags, err := eng.Check(string(src), name) if err != nil { t.Fatalf("Check(%s): %v", name, err) } return diags } func ruleIDs(diags []diagnostics.Diagnostic) map[string]int { m := make(map[string]int) for _, d := range diags { m[d.RuleID]++ } return m } func TestMigrationViolations(t *testing.T) { diags := checkFile(t, "migration_violations.sql") ids := ruleIDs(diags) cases := []struct { id string count int }{ {"MIG001", 1}, // one non-concurrent index {"MIG002", 1}, // one NOT NULL + no DEFAULT {"MIG003", 2}, // one FK + one CHECK without NOT VALID } for _, c := range cases { if got := ids[c.id]; got != c.count { t.Errorf("rule %s: want %d findings, got %d", c.id, c.count, got) } } } func TestMigrationClean(t *testing.T) { diags := checkFile(t, "migration_clean.sql") ids := ruleIDs(diags) for _, id := range []string{"MIG001", "MIG002", "MIG003"} { if n := ids[id]; n != 0 { t.Errorf("rule %s: want 0 findings on clean fixture, got %d", id, n) } } } func TestCorrectnessViolations(t *testing.T) { diags := checkFile(t, "correctness_violations.sql") ids := ruleIDs(diags) cases := []struct { id string count int }{ {"COR001", 1}, {"COR002", 1}, {"COR003", 1}, } for _, c := range cases { if got := ids[c.id]; got != c.count { t.Errorf("rule %s: want %d findings, got %d", c.id, c.count, got) } } } func TestNamingViolations(t *testing.T) { diags := checkFile(t, "naming_violations.sql") ids := ruleIDs(diags) cases := []struct { id string count int }{ {"NAM001", 1}, // UserAccounts {"NAM002", 2}, // userId, emailAddress {"NAM003", 1}, // GetUserById } for _, c := range cases { if got := ids[c.id]; got != c.count { t.Errorf("rule %s: want %d findings, got %d", c.id, c.count, got) } } } func TestParseError(t *testing.T) { eng := lint.New() diags, err := eng.Check("SELECT FROM WHERE", "test.sql") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(diags) == 0 || diags[0].RuleID != "PARSE" { t.Errorf("expected a PARSE diagnostic, got %v", diags) } } func TestCustomEngine(t *testing.T) { eng := lint.Custom() eng.Register(lint.New().Rules()[0]) // register first rule only // Just confirm it doesn't panic and returns results. diags, err := eng.Check("SELECT 1", "stdin") if err != nil { t.Fatalf("unexpected error: %v", err) } _ = diags }