feat(writers): stamp RelSpec version in generated headers; preserve DBML column order

- Add pkg/buildinfo with Version/BuildDate (set via ldflags, VCS fallback);
  cmd/relspec now sources version info from it
- Emit "RelSpec <version> (built: <date>)" in generated file headers for
  bun, gorm, drizzle, pgsql (incl. migration), mssql, sqlite writers
- pgsql writer: getSortedColumns now sorts by Sequence then Name so the
  streaming WriteSchema/WriteDatabase path preserves source column order
- dbml reader: mergeTable re-bases merged-in column Sequence values past the
  existing max, fixing colliding sequences (and alphabetical fallback) when a
  table is split across multiple DBML files
- Tests for bun/gorm header + column order, and dbml multi-file merge ordering
This commit is contained in:
2026-09-10 22:08:49 +02:00
parent 58e46e5b59
commit 19a2cc1fe3
17 changed files with 214 additions and 65 deletions
+37
View File
@@ -8,6 +8,7 @@ import (
"strings"
"testing"
"git.warky.dev/wdevs/relspecgo/pkg/buildinfo"
"git.warky.dev/wdevs/relspecgo/pkg/models"
"git.warky.dev/wdevs/relspecgo/pkg/writers"
)
@@ -120,6 +121,42 @@ func TestWriter_WriteTable_MultilineDescriptionProducesValidGo(t *testing.T) {
}
}
func TestWriter_WriteTable_EmbedsRelspecVersionHeader(t *testing.T) {
table := models.InitTable("users", "public")
table.Columns["id"] = &models.Column{Name: "id", Type: "bigint", IsPrimaryKey: true, NotNull: true, Sequence: 1}
outputPath := filepath.Join(t.TempDir(), "users.go")
writer := NewWriter(&writers.WriterOptions{OutputPath: outputPath, PackageName: "models"})
if err := writer.WriteTable(table); err != nil {
t.Fatalf("WriteTable() error = %v", err)
}
generated, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("failed to read generated code: %v", err)
}
src := string(generated)
if _, err := parser.ParseFile(token.NewFileSet(), outputPath, generated, parser.AllErrors); err != nil {
t.Fatalf("generated code is invalid Go: %v\n%s", err, src)
}
wantHeader := "// " + buildinfo.GeneratedComment()
if !strings.Contains(src, wantHeader) {
t.Errorf("generated code missing RelSpec version header %q\n%s", wantHeader, src)
}
// The provenance comment must sit between the "Code generated" marker and the package clause.
genIdx := strings.Index(src, "// Code generated by relspecgo. DO NOT EDIT.")
hdrIdx := strings.Index(src, wantHeader)
pkgIdx := strings.Index(src, "package models")
if genIdx < 0 || hdrIdx < 0 || pkgIdx < 0 || !(genIdx < hdrIdx && hdrIdx < pkgIdx) {
t.Errorf("RelSpec version header is misplaced (gen=%d hdr=%d pkg=%d)\n%s", genIdx, hdrIdx, pkgIdx, src)
}
if !strings.Contains(wantHeader, "RelSpec ") || !strings.Contains(wantHeader, "(built: ") {
t.Errorf("version header not in expected 'RelSpec <version> (built: <date>)' form: %q", wantHeader)
}
}
func TestWriter_WriteDatabase_MultiFile(t *testing.T) {
// Create a database with two tables
db := models.InitDatabase("testdb")