Files
relspecgo/pkg/writers/mysql/writer_full_test.go
T
warkanum 495a21b67b test: expand coverage across readers, writers, cmd, ui, diff and merge
Implements tests/_plans and previously deferred packages; updates plan
README with new coverage numbers.
2026-10-03 21:33:59 +02:00

160 lines
4.8 KiB
Go

package mysql
import (
"os"
"path/filepath"
"strings"
"testing"
"git.warky.dev/wdevs/relspecgo/pkg/models"
"git.warky.dev/wdevs/relspecgo/pkg/writers"
)
func shopDB() *models.Database {
s := models.InitSchema("shop")
t := models.InitTable("users", "shop")
add := func(name, typ string, mod func(*models.Column)) {
c := models.InitColumn(name, "users", "shop")
c.Type = typ
if mod != nil {
mod(c)
}
t.Columns[name] = c
}
add("id", "int", func(c *models.Column) { c.IsPrimaryKey, c.NotNull, c.AutoIncrement = true, true, true })
add("email", "string", func(c *models.Column) { c.Length, c.NotNull = 255, true })
add("nick", "string", nil)
add("age", "int", func(c *models.Column) { c.Default = 18 })
add("active", "boolean", func(c *models.Column) { c.Default = true })
add("zeta", "string", nil)
add("alpha", "string", nil)
for _, name := range []string{"uq_b", "uq_a", "uq_c"} {
u := models.InitConstraint(name, models.UniqueConstraint)
u.Columns = []string{"email"}
t.Constraints[name] = u
}
s.Tables = append(s.Tables, t)
db := models.InitDatabase("shop")
db.Schemas = append(db.Schemas, s)
return db
}
func writeFile(t *testing.T, db *models.Database) string {
t.Helper()
out := filepath.Join(t.TempDir(), "out.sql")
if err := NewWriter(&writers.WriterOptions{OutputPath: out}).WriteDatabase(db); err != nil {
t.Fatal(err)
}
b, err := os.ReadFile(out)
if err != nil {
t.Fatal(err)
}
return string(b)
}
func TestWriteDatabase_ToFile(t *testing.T) {
got := writeFile(t, shopDB())
for _, want := range []string{
"-- Database: shop", "CREATE TABLE IF NOT EXISTS `shop`.`users`",
"`id` ", "AUTO_INCREMENT", "`email` VARCHAR(255) NOT NULL", "DEFAULT 18",
"PRIMARY KEY (`id`)", "CONSTRAINT `uq_a` UNIQUE (`email`)", "ENGINE=InnoDB",
} {
if !strings.Contains(got, want) {
t.Errorf("missing %q\n%s", want, got)
}
}
}
func TestWriteDatabase_Deterministic(t *testing.T) {
first := writeFile(t, shopDB())
for i := 0; i < 30; i++ {
if got := writeFile(t, shopDB()); got != first {
t.Fatalf("output differs on run %d:\n--- first\n%s\n--- got\n%s", i, first, got)
}
}
}
func TestWriteDatabase_UniqueConstraintsSorted(t *testing.T) {
got := writeFile(t, shopDB())
a, b, c := strings.Index(got, "`uq_a`"), strings.Index(got, "`uq_b`"), strings.Index(got, "`uq_c`")
if !(a < b && b < c) {
t.Errorf("unique constraints must be sorted by name: %d %d %d", a, b, c)
}
}
func TestWriteSchemaAndTable_UseOutputPath(t *testing.T) {
db := shopDB()
dir := t.TempDir()
sOut := filepath.Join(dir, "s.sql")
if err := NewWriter(&writers.WriterOptions{OutputPath: sOut}).WriteSchema(db.Schemas[0]); err != nil {
t.Fatal(err)
}
if b, _ := os.ReadFile(sOut); !strings.Contains(string(b), "CREATE TABLE IF NOT EXISTS `shop`.`users`") {
t.Errorf("schema output:\n%s", b)
}
tOut := filepath.Join(dir, "t.sql")
if err := NewWriter(&writers.WriterOptions{OutputPath: tOut}).WriteTable(db.Schemas[0].Tables[0]); err != nil {
t.Fatal(err)
}
if b, _ := os.ReadFile(tOut); !strings.Contains(string(b), "CREATE TABLE IF NOT EXISTS `users`") {
t.Errorf("table output (unqualified name):\n%s", b)
}
}
func TestWriteSchema_WithoutWriterDoesNotPanic(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("panic: %v", r)
}
}()
s := shopDB().Schemas[0]
s.Tables = nil // nothing to print to stdout
if err := NewWriter(&writers.WriterOptions{}).WriteSchema(s); err != nil {
t.Fatal(err)
}
}
func TestWriteDatabase_Errors(t *testing.T) {
if err := NewWriter(nil).WriteDatabase(shopDB()); err == nil || !strings.Contains(err.Error(), "options are required") {
t.Errorf("nil options: %v", err)
}
bad := filepath.Join(t.TempDir(), "missing", "x.sql")
if err := NewWriter(&writers.WriterOptions{OutputPath: bad}).WriteDatabase(shopDB()); err == nil {
t.Error("bad output path must fail")
}
}
func TestWriteDatabase_ConnectionFailure(t *testing.T) {
opts := &writers.WriterOptions{Metadata: map[string]any{
"connection_string": "u:p@tcp(127.0.0.1:1)/none?timeout=1s",
}}
if err := NewWriter(opts).WriteDatabase(shopDB()); err == nil || !strings.Contains(err.Error(), "failed to execute SQL") {
t.Errorf("got %v", err)
}
}
func TestQuoteHelpers(t *testing.T) {
if got := quote("a`b"); got != "`a``b`" {
t.Errorf("quote: %q", got)
}
if got := quoted([]string{"a", "b"}); got != "`a`, `b`" {
t.Errorf("quoted: %q", got)
}
if got := quoted(nil); got != "" {
t.Errorf("quoted(nil): %q", got)
}
}
func TestPrimaryKeyConstraintOverridesColumnFlags(t *testing.T) {
db := shopDB()
tbl := db.Schemas[0].Tables[0]
pk := models.InitConstraint("pk", models.PrimaryKeyConstraint)
pk.Columns = []string{"email", "id"}
tbl.Constraints["pk"] = pk
if got := writeFile(t, db); !strings.Contains(got, "PRIMARY KEY (`email`, `id`)") {
t.Errorf("composite pk:\n%s", got)
}
}