- pkg/lsp: JSON-RPC 2.0 LSP server (formatting, diagnostics, codeAction quick-fixes) - cmd/pgtidy: lsp and config subcommands - pkg/diagnostics: TextFix struct for byte-range autofixes - pkg/lint: MIG001/MIG003 autofixes, ApplyFixes helper, --fix flag on lint command - editors/vscode: TypeScript extension with LanguageClient, showVersion/showConfig/formatDocument commands, logo - editors/datagrip: Gradle JetBrains plugin via LSP4IJ, pluginIcon - .goreleaser.yaml, .github/workflows: CI + release pipeline - Makefile: snapshot, release, vscode-compile, vscode-package targets - go.mod + all imports: module path updated to git.warky.dev/wdevs/pgtidy - assets: logo files (256px, 128px, 1024px, ico)
105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
package lint_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.warky.dev/wdevs/pgtidy/pkg/lint"
|
|
)
|
|
|
|
func TestMIG001Fix(t *testing.T) {
|
|
src := "CREATE INDEX idx_orders_user ON orders(user_id);"
|
|
eng := lint.New()
|
|
diags, _ := eng.Check(src, "test.sql")
|
|
|
|
var hasFix bool
|
|
for _, d := range diags {
|
|
if d.RuleID == "MIG001" && d.Fix != nil {
|
|
hasFix = true
|
|
}
|
|
}
|
|
if !hasFix {
|
|
t.Fatal("MIG001 diagnostic missing Fix")
|
|
}
|
|
|
|
fixed := lint.ApplyFixes(src, diags)
|
|
if !strings.Contains(fixed, "CONCURRENTLY") {
|
|
t.Errorf("fix did not insert CONCURRENTLY; got: %s", fixed)
|
|
}
|
|
// Re-check: MIG001 should be gone
|
|
diags2, _ := eng.Check(fixed, "test.sql")
|
|
for _, d := range diags2 {
|
|
if d.RuleID == "MIG001" {
|
|
t.Errorf("MIG001 still fires after fix: %s", fixed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMIG003Fix(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
src string
|
|
}{
|
|
{
|
|
"FK constraint",
|
|
"ALTER TABLE orders ADD CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id);",
|
|
},
|
|
{
|
|
"CHECK constraint",
|
|
"ALTER TABLE orders ADD CONSTRAINT chk_positive CHECK (amount > 0);",
|
|
},
|
|
}
|
|
eng := lint.New()
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
diags, _ := eng.Check(tc.src, "test.sql")
|
|
var hasFix bool
|
|
for _, d := range diags {
|
|
if d.RuleID == "MIG003" && d.Fix != nil {
|
|
hasFix = true
|
|
}
|
|
}
|
|
if !hasFix {
|
|
t.Fatal("MIG003 diagnostic missing Fix")
|
|
}
|
|
fixed := lint.ApplyFixes(tc.src, diags)
|
|
if !strings.Contains(fixed, "NOT VALID") {
|
|
t.Errorf("fix did not insert NOT VALID; got: %s", fixed)
|
|
}
|
|
// Re-check: MIG003 should be gone
|
|
diags2, _ := eng.Check(fixed, "test.sql")
|
|
for _, d := range diags2 {
|
|
if d.RuleID == "MIG003" {
|
|
t.Errorf("MIG003 still fires after fix: %s", fixed)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApplyFixes_MultipleInOneFile(t *testing.T) {
|
|
src := `CREATE INDEX a ON t(x);
|
|
ALTER TABLE t ADD CONSTRAINT fk FOREIGN KEY (x) REFERENCES u(id);`
|
|
|
|
eng := lint.New()
|
|
diags, _ := eng.Check(src, "test.sql")
|
|
fixed := lint.ApplyFixes(src, diags)
|
|
|
|
if !strings.Contains(fixed, "CONCURRENTLY") {
|
|
t.Error("CONCURRENTLY missing after multi-fix")
|
|
}
|
|
if !strings.Contains(fixed, "NOT VALID") {
|
|
t.Error("NOT VALID missing after multi-fix")
|
|
}
|
|
}
|
|
|
|
func TestApplyFixes_NoFixes(t *testing.T) {
|
|
src := "SELECT 1;"
|
|
eng := lint.New()
|
|
diags, _ := eng.Check(src, "test.sql")
|
|
fixed := lint.ApplyFixes(src, diags)
|
|
if fixed != src {
|
|
t.Errorf("ApplyFixes changed unfixable source: %q", fixed)
|
|
}
|
|
}
|