package main // Unit tests for the schema (novox/hq ADR 0459 ยง4, issue 1517): the migrations are read with no gap and a // bad name is refused (AC1), and no table holds a mail body or text taken from one (AC3). AC2 (the manifest // requires postgres-database, binds it, renders database-url and runs prepare) is manifest_test.go's; AC4 // (against a real postgres) is pgstore_integration_test.go, compiled but never run from here. import ( "strings" "testing" "testing/fstest" ) // 1517/AC1: Migrations() reads migrations/0001-*.sql onward with no gap. func TestMigrationsReadsTheRealOnesWithNoGap(t *testing.T) { ms, err := Migrations() if err != nil { t.Fatal(err) } if len(ms) == 0 { t.Fatal("no migrations found") } for i, m := range ms { if m.Number != i+1 { t.Fatalf("migration %d is numbered %04d, want %04d", i, m.Number, i+1) } } } // 1517/AC1: a gap in the numbering is refused. func TestMigrationsRefusesAGap(t *testing.T) { fsys := fstest.MapFS{ "m/0001-first.sql": {Data: []byte("create table a (id int);")}, "m/0003-third.sql": {Data: []byte("create table b (id int);")}, } if _, err := migrationsFrom(fsys, "m"); err == nil || !strings.Contains(err.Error(), "gap") { t.Fatalf("migrationsFrom with a gap: %v, want an error naming the gap", err) } } // 1517/AC1: a file whose name is not NNNN-what-it-does.sql is refused by name. func TestMigrationsRefusesABadName(t *testing.T) { fsys := fstest.MapFS{ "m/0001-first.sql": {Data: []byte("create table a (id int);")}, "m/not-a-migration.sql": {Data: []byte("create table b (id int);")}, } if _, err := migrationsFrom(fsys, "m"); err == nil || !strings.Contains(err.Error(), "not a migration's name") { t.Fatalf("migrationsFrom with a bad name: %v, want an error naming it", err) } } // forbiddenColumn is 1517/AC3: no table in schema.sql or any migration has a column for a mail body or text. var forbiddenColumns = []string{"body", "text", "content", "snippet"} // 1517/AC3: no table in schema.sql or any migration has a column named body, text, content or snippet, and // summary is bounded to 160 characters by a CHECK. func TestTheSchemaKeepsNoMailBodyOrText(t *testing.T) { for _, sql := range allSchemaSQL(t) { for _, line := range strings.Split(sql, "\n") { word := strings.ToLower(strings.TrimSpace(strings.Fields(line + " x")[0])) for _, forbidden := range forbiddenColumns { if word == forbidden { t.Fatalf("a column is named %q, which may hold a mail body or text taken from one: %q", forbidden, line) } } } } if !strings.Contains(consolidated, "char_length(summary) <= 160") { t.Fatal("summary has no CHECK bounding it to 160 characters") } } func allSchemaSQL(t *testing.T) []string { t.Helper() ms, err := Migrations() if err != nil { t.Fatal(err) } out := []string{consolidated} for _, m := range ms { out = append(out, m.SQL) } return out }