Files
relspecgo/pkg/writers/sqlite/directives_test.go
T
HeinandClaude Sonnet 5 ce3b615b0a feat(dbml): @postgres/@sqlite dialect directives (#19)
Add parseable `@<namespace>[(<target>)]: <args>` directives embedded in DBML.
They are stored losslessly on each object's Metadata, round-trip unchanged
through the DBML writer, and are translated to SQL only by the writer for the
matching dialect.

- models: Directive type + catalog; Metadata map added to Column and Index
- dbml reader: parse and attach directives at database/table/column/index
  level; line-numbered errors; repeatable by default with singleton duplicate
  detection. Fixes a preexisting bug where an `indexes {}` closing brace ended
  the table early, dropping trailing Note: and directive lines.
- dbml writer: re-emit directives at their location; idempotent output
- pgsql writer: PARTITION BY / INHERITS / WITH / TABLESPACE (table),
  STORAGE / COMPRESSION / identity (column), WITH / TABLESPACE (index)
- sqlite writer: WITHOUT ROWID / STRICT (table), COLLATE (column)
- --strict-directives flag on ReaderOptions and WriterOptions
- docs/DBML_DIRECTIVES.md + reader/writer READMEs

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ss2MY5J11cRGwEz86ZXk7d
2026-09-08 16:17:37 +02:00

83 lines
2.8 KiB
Go

package sqlite
import (
"bytes"
"strings"
"testing"
"git.warky.dev/wdevs/relspecgo/pkg/models"
"git.warky.dev/wdevs/relspecgo/pkg/writers"
)
func sqliteDirectiveDB(t *testing.T) *models.Database {
t.Helper()
db := models.InitDatabase("testdb")
schema := models.InitSchema("public")
table := models.InitTable("events", "public")
id := models.InitColumn("id", "events", "public")
id.Type = "bigint"
id.IsPrimaryKey = true
id.NotNull = true
table.Columns["id"] = id
name := models.InitColumn("name", "events", "public")
name.Type = "varchar(200)"
name.NotNull = true
models.AddDirective(name.Metadata, models.Directive{Namespace: "sqlite", Args: "collate NOCASE"})
// A postgres directive on the same column must be ignored by the sqlite writer.
models.AddDirective(name.Metadata, models.Directive{Namespace: "postgres", Args: "storage plain"})
table.Columns["name"] = name
models.AddDirective(table.Metadata, models.Directive{Namespace: "sqlite", Args: "without rowid"})
models.AddDirective(table.Metadata, models.Directive{Namespace: "sqlite", Args: "strict"})
models.AddDirective(table.Metadata, models.Directive{Namespace: "postgres", Args: "partition by RANGE (id)"})
schema.Tables = append(schema.Tables, table)
db.Schemas = append(db.Schemas, schema)
return db
}
func TestSqliteDirectives_TableOptionsAndCollate(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(&writers.WriterOptions{})
w.writer = &buf
if err := w.WriteDatabase(sqliteDirectiveDB(t)); err != nil {
t.Fatalf("WriteDatabase: %v", err)
}
out := buf.String()
if !strings.Contains(out, ") WITHOUT ROWID, STRICT;") {
t.Errorf("missing table options clause:\n%s", out)
}
if !strings.Contains(out, `"name" TEXT COLLATE NOCASE NOT NULL`) {
t.Errorf("missing column COLLATE clause:\n%s", out)
}
// postgres directives must never reach sqlite output.
if strings.Contains(strings.ToUpper(out), "PARTITION BY") || strings.Contains(strings.ToUpper(out), "STORAGE PLAIN") {
t.Errorf("postgres directive leaked into sqlite output:\n%s", out)
}
}
func TestSqliteDirectives_StrictUnknownKeyErrors(t *testing.T) {
db := sqliteDirectiveDB(t)
models.AddDirective(db.Schemas[0].Tables[0].Metadata, models.Directive{Namespace: "sqlite", Args: "frobnicate x"})
var buf bytes.Buffer
w := NewWriter(&writers.WriterOptions{StrictDirectives: true})
w.writer = &buf
err := w.WriteDatabase(db)
if err == nil || !strings.Contains(err.Error(), "frobnicate") {
t.Fatalf("want strict error for unknown sqlite key, got %v", err)
}
}
func TestSqliteDirectives_StrictIgnoresPostgres(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(&writers.WriterOptions{StrictDirectives: true})
w.writer = &buf
if err := w.WriteDatabase(sqliteDirectiveDB(t)); err != nil {
t.Fatalf("strict mode should ignore postgres directives, got %v", err)
}
}