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.
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
func TestColumnDataOps(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
|
||||
if se.CreateColumn(5, 0, "x", "int", false, false) != nil || se.CreateColumn(0, 5, "x", "int", false, false) != nil {
|
||||
t.Error("create with bad index must return nil")
|
||||
}
|
||||
col := se.CreateColumn(0, 0, "age", "integer", true, true)
|
||||
if col == nil || col.Type != "integer" || !col.IsPrimaryKey || !col.NotNull {
|
||||
t.Fatalf("create: %+v", col)
|
||||
}
|
||||
if se.GetColumn(0, 0, "age") != col || se.GetColumn(0, 0, "nope") != nil || se.GetColumn(9, 0, "age") != nil {
|
||||
t.Error("get mismatch")
|
||||
}
|
||||
|
||||
if se.CreateColumn(0, 0, "a", "text", false, false) == nil {
|
||||
t.Error("create second column")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
si, ti int
|
||||
old, new string
|
||||
want bool
|
||||
}{
|
||||
{"bad table", 0, 9, "age", "age", false},
|
||||
{"missing column", 0, 0, "zzz", "zzz", false},
|
||||
{"in place", 0, 0, "age", "age", true},
|
||||
{"rename", 0, 0, "age", "years", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := se.UpdateColumn(tt.si, tt.ti, tt.old, tt.new, "bigint", false, true, "0", "desc"); got != tt.want {
|
||||
t.Errorf("got %v", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
got := se.GetColumn(0, 0, "years")
|
||||
if got == nil || got.Name != "years" || got.Type != "bigint" || got.IsPrimaryKey || got.Default != "0" || got.Description != "desc" {
|
||||
t.Errorf("after update: %+v", got)
|
||||
}
|
||||
if se.GetColumn(0, 0, "age") != nil {
|
||||
t.Error("old name must be gone")
|
||||
}
|
||||
|
||||
if len(se.GetAllColumns(0, 0)) != 4 || se.GetAllColumns(0, 9) != nil {
|
||||
t.Error("GetAllColumns")
|
||||
}
|
||||
if se.DeleteColumn(0, 9, "a") || se.DeleteColumn(0, 0, "zzz") {
|
||||
t.Error("delete bad target must fail")
|
||||
}
|
||||
if !se.DeleteColumn(0, 0, "a") || se.DeleteColumn(0, 0, "a") {
|
||||
t.Error("delete should succeed once")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateColumn_NilMap(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
se.db.Schemas[0].Tables[0].Columns = nil
|
||||
if se.CreateColumn(0, 0, "a", "text", false, false) == nil {
|
||||
t.Error("create with nil map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelationshipDataOps(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
rel := &models.Relationship{Name: "fk_a", FromTable: "users", ToTable: "orders"}
|
||||
|
||||
if se.CreateRelationship(9, 0, rel) != nil || se.CreateRelationship(0, 9, rel) != nil || se.CreateRelationship(0, -1, rel) != nil {
|
||||
t.Error("create bad index")
|
||||
}
|
||||
// Before any relationship exists, update/delete/get/names report nothing.
|
||||
se.db.Schemas[0].Tables[0].Relationships = nil
|
||||
if se.UpdateRelationship(0, 0, "fk_a", rel) || se.DeleteRelationship(0, 0, "fk_a") ||
|
||||
se.GetRelationship(0, 0, "fk_a") != nil || se.GetRelationshipNames(0, 0) != nil {
|
||||
t.Error("nil map handling")
|
||||
}
|
||||
|
||||
if se.CreateRelationship(0, 0, rel) != rel {
|
||||
t.Fatal("create")
|
||||
}
|
||||
se.CreateRelationship(0, 0, &models.Relationship{Name: "fk_0"})
|
||||
if got := se.GetRelationshipNames(0, 0); !reflect.DeepEqual(got, []string{"fk_0", "fk_a"}) {
|
||||
t.Errorf("names must be sorted: %v", got)
|
||||
}
|
||||
if se.GetRelationship(0, 0, "fk_a") != rel || se.GetRelationship(0, 0, "none") != nil {
|
||||
t.Error("get")
|
||||
}
|
||||
|
||||
renamed := &models.Relationship{Name: "fk_b"}
|
||||
if !se.UpdateRelationship(0, 0, "fk_a", renamed) {
|
||||
t.Fatal("update")
|
||||
}
|
||||
if se.GetRelationship(0, 0, "fk_a") != nil || se.GetRelationship(0, 0, "fk_b") != renamed {
|
||||
t.Error("rename")
|
||||
}
|
||||
if se.UpdateRelationship(9, 0, "x", renamed) || se.UpdateRelationship(0, 9, "x", renamed) {
|
||||
t.Error("update bad index")
|
||||
}
|
||||
if se.DeleteRelationship(9, 0, "x") || se.DeleteRelationship(0, 9, "x") {
|
||||
t.Error("delete bad index")
|
||||
}
|
||||
if !se.DeleteRelationship(0, 0, "fk_b") || se.GetRelationship(0, 0, "fk_b") != nil {
|
||||
t.Error("delete")
|
||||
}
|
||||
if se.GetRelationship(9, 0, "x") != nil || se.GetRelationship(0, 9, "x") != nil ||
|
||||
se.GetRelationshipNames(9, 0) != nil || se.GetRelationshipNames(0, 9) != nil {
|
||||
t.Error("bad index reads")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemaDataOps(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
s := se.CreateSchema("sales", "desc")
|
||||
if s == nil || s.Name != "sales" || s.Description != "desc" || s.Tables == nil || s.Sequences == nil || s.Enums == nil {
|
||||
t.Fatalf("create: %+v", s)
|
||||
}
|
||||
if len(se.GetAllSchemas()) != 2 || se.GetSchema(1) != s || se.GetSchema(2) != nil || se.GetSchema(-1) != nil {
|
||||
t.Error("get")
|
||||
}
|
||||
se.UpdateSchema(1, "billing", "owner", "d2")
|
||||
if s.Name != "billing" || s.Owner != "owner" || s.Description != "d2" {
|
||||
t.Errorf("update: %+v", s)
|
||||
}
|
||||
se.UpdateSchema(9, "x", "x", "x") // no panic
|
||||
if se.DeleteSchema(9) || se.DeleteSchema(-1) {
|
||||
t.Error("delete bad index")
|
||||
}
|
||||
if !se.DeleteSchema(1) || len(se.db.Schemas) != 1 {
|
||||
t.Error("delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableDataOps(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
if se.CreateTable(9, "x", "") != nil {
|
||||
t.Error("create bad schema")
|
||||
}
|
||||
tbl := se.CreateTable(0, "orders", "d")
|
||||
if tbl == nil || tbl.Schema != "public" || tbl.Columns == nil || tbl.Constraints == nil || tbl.Indexes == nil {
|
||||
t.Fatalf("create: %+v", tbl)
|
||||
}
|
||||
if se.GetTable(0, 1) != tbl || se.GetTable(0, 2) != nil || se.GetTable(9, 0) != nil || se.GetTable(0, -1) != nil {
|
||||
t.Error("get")
|
||||
}
|
||||
if len(se.GetAllTables()) != 2 || len(se.GetTablesInSchema(0)) != 2 || se.GetTablesInSchema(9) != nil {
|
||||
t.Error("get all")
|
||||
}
|
||||
se.UpdateTable(0, 1, "orders2", "d2")
|
||||
if tbl.Name != "orders2" || tbl.Description != "d2" {
|
||||
t.Errorf("update: %+v", tbl)
|
||||
}
|
||||
se.UpdateTable(9, 0, "x", "x")
|
||||
se.UpdateTable(0, 9, "x", "x")
|
||||
if se.DeleteTable(9, 0) || se.DeleteTable(0, 9) {
|
||||
t.Error("delete bad index")
|
||||
}
|
||||
if !se.DeleteTable(0, 1) || len(se.db.Schemas[0].Tables) != 1 {
|
||||
t.Error("delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateDatabase(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
se.updateDatabase("n", "d", "c", "pgsql", "16")
|
||||
db := se.db
|
||||
if db.Name != "n" || db.Description != "d" || db.Comment != "c" || db.DatabaseType != models.PostgresqlDatabaseType || db.DatabaseVersion != "16" {
|
||||
t.Errorf("%+v", db)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainDataOps(t *testing.T) {
|
||||
se := NewSchemaEditor(models.InitDatabase("d"))
|
||||
se.createDomain("a", "da")
|
||||
se.createDomain("b", "db")
|
||||
if len(se.db.Domains) != 2 || se.db.Domains[1].Sequence != 1 {
|
||||
t.Fatalf("create: %+v", se.db.Domains)
|
||||
}
|
||||
se.updateDomain(0, "a2", "da2")
|
||||
se.updateDomain(9, "x", "x")
|
||||
if se.db.Domains[0].Name != "a2" || se.db.Domains[0].Description != "da2" {
|
||||
t.Error("update")
|
||||
}
|
||||
se.deleteDomain(9)
|
||||
se.deleteDomain(-1)
|
||||
se.deleteDomain(0)
|
||||
if len(se.db.Domains) != 1 || se.db.Domains[0].Name != "b" {
|
||||
t.Errorf("delete: %+v", se.db.Domains)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
const uiFixtures = "../../tests/assets"
|
||||
|
||||
func newUIEditor() *SchemaEditor {
|
||||
se := NewSchemaEditor(models.InitDatabase("start"))
|
||||
se.db = newTestEditor().db
|
||||
return se
|
||||
}
|
||||
|
||||
func hasPage(se *SchemaEditor, name string) bool {
|
||||
return se.pages.HasPage(name)
|
||||
}
|
||||
|
||||
func TestSortedKeysAndColumnNames(t *testing.T) {
|
||||
if got := sortedKeys(map[string]int{"b": 1, "a": 2, "c": 3}); strings.Join(got, ",") != "a,b,c" {
|
||||
t.Errorf("sortedKeys: %v", got)
|
||||
}
|
||||
if got := sortedKeys[int](nil); len(got) != 0 {
|
||||
t.Errorf("nil map: %v", got)
|
||||
}
|
||||
tbl := models.InitTable("t", "s")
|
||||
tbl.Columns["z"] = models.InitColumn("z", "t", "s")
|
||||
tbl.Columns["a"] = models.InitColumn("a", "t", "s")
|
||||
if got := getColumnNames(tbl); strings.Join(got, ",") != "a,z" {
|
||||
t.Errorf("getColumnNames: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocations(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
se.db.Schemas = append(se.db.Schemas, models.InitSchema("empty"))
|
||||
sl := se.schemaLocations()
|
||||
if len(sl) != 2 || sl[0].label != "public" || sl[1].schemaIndex != 1 || sl[0].tableIndex != -1 {
|
||||
t.Errorf("schemaLocations: %+v", sl)
|
||||
}
|
||||
tl := se.tableLocations()
|
||||
if len(tl) != 1 || tl[0].label != "public.users" || tl[0].schemaIndex != 0 || tl[0].tableIndex != 0 {
|
||||
t.Errorf("tableLocations: %+v", tl)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSkipTablesUI(t *testing.T) {
|
||||
if got := parseSkipTablesUI(""); len(got) != 0 {
|
||||
t.Errorf("empty: %v", got)
|
||||
}
|
||||
got := parseSkipTablesUI(" Users , ORDERS ,, ")
|
||||
if len(got) != 2 || !got["users"] || !got["orders"] {
|
||||
t.Errorf("got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpTexts(t *testing.T) {
|
||||
for name, fn := range map[string]func() string{"load": getLoadHelpText, "save": getSaveHelpText, "import": getImportHelpText} {
|
||||
if txt := fn(); !strings.Contains(txt, "dbml") && name != "save" || txt == "" {
|
||||
t.Errorf("%s help text: %q", name, txt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectKinds(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
if err := se.SaveIndex(0, 0, "", &models.Index{Name: "idx_e", Columns: []string{"email"}, Unique: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := se.SaveView(0, -1, &models.View{Name: "v1", Definition: "select 1"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := se.SaveSequence(0, -1, &models.Sequence{Name: "s1", IncrementBy: 1, StartValue: 1}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := se.SaveScript(0, -1, &models.Script{Name: "sc1", SQL: "select 1"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kinds := map[string]objectKind{
|
||||
"indexes": se.indexKind(), "views": se.viewKind(), "sequences": se.sequenceKind(), "scripts": se.scriptKind(),
|
||||
}
|
||||
for page, k := range kinds {
|
||||
t.Run(page, func(t *testing.T) {
|
||||
if k.page != page || k.title == "" || k.singular == "" || len(k.headers) == 0 {
|
||||
t.Fatalf("metadata: %+v", k)
|
||||
}
|
||||
rows := k.rows()
|
||||
if len(rows) != 1 {
|
||||
t.Fatalf("rows: %+v", rows)
|
||||
}
|
||||
for _, r := range rows {
|
||||
if len(r.cells) != len(k.headers) {
|
||||
t.Errorf("cells %v do not match headers %v", r.cells, k.headers)
|
||||
}
|
||||
}
|
||||
if len(k.locations()) == 0 {
|
||||
t.Error("no locations")
|
||||
}
|
||||
|
||||
// Editing an existing row without changes keeps it valid.
|
||||
form := tview.NewForm()
|
||||
save := k.buildForm(form, &rows[0])
|
||||
if form.GetFormItemCount() == 0 {
|
||||
t.Error("no form fields")
|
||||
}
|
||||
loc := k.locations()[0]
|
||||
loc.schemaIndex, loc.tableIndex = rows[0].schemaIndex, rows[0].tableIndex
|
||||
if err := save(loc); err != nil {
|
||||
t.Errorf("save unchanged: %v", err)
|
||||
}
|
||||
|
||||
// A blank new form is rejected by validation.
|
||||
blank := tview.NewForm()
|
||||
saveBlank := k.buildForm(blank, nil)
|
||||
if err := saveBlank(k.locations()[0]); err == nil {
|
||||
t.Error("blank form accepted")
|
||||
}
|
||||
|
||||
if !k.remove(rows[0]) || len(k.rows()) != 0 {
|
||||
t.Error("remove failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectKind_CreateIndexFromForm(t *testing.T) {
|
||||
se := newTestEditor()
|
||||
k := se.indexKind()
|
||||
form := tview.NewForm()
|
||||
save := k.buildForm(form, nil)
|
||||
form.GetFormItemByLabel("Name").(*tview.InputField).SetText("idx_new")
|
||||
form.GetFormItemByLabel("Columns (comma separated)").(*tview.InputField).SetText("id, email")
|
||||
if err := save(k.locations()[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
idx := se.db.Schemas[0].Tables[0].Indexes["idx_new"]
|
||||
if idx == nil || len(idx.Columns) != 2 || idx.Type != "btree" {
|
||||
t.Errorf("index: %+v", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadDatabase(t *testing.T) {
|
||||
for _, tt := range []struct{ format, path string }{
|
||||
{"dbml", "dbml/simple.dbml"}, {"json", "json/database.json"}, {"yaml", "yaml/database.yaml"},
|
||||
{"drawdb", "drawdb/simple.json"}, {"dctx", "dctx/p1.dctx"}, {"graphql", "graphql/simple.graphql"},
|
||||
{"prisma", "prisma/example.prisma"}, {"typeorm", "typeorm/example.ts"},
|
||||
{"drizzle", "drizzle/schema.ts"}, {"gorm", "gorm/simple.go"}, {"bun", "bun/simple.go"},
|
||||
} {
|
||||
t.Run(tt.format, func(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
se.loadDatabase(tt.format, filepath.Join(uiFixtures, tt.path), "")
|
||||
if hasPage(se, "error-dialog") || !hasPage(se, "success-dialog") {
|
||||
t.Fatalf("expected success dialog (pages: error=%v)", hasPage(se, "error-dialog"))
|
||||
}
|
||||
if se.loadConfig == nil || se.loadConfig.SourceType != tt.format || len(se.db.Schemas) == 0 {
|
||||
t.Errorf("state: %+v db=%+v", se.loadConfig, se.db)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
errCases := []struct {
|
||||
name, format, path, conn string
|
||||
}{
|
||||
{"pgsql no conn", "pgsql", "", ""},
|
||||
{"file required", "json", "", ""},
|
||||
{"unsupported", "nope", "x", ""},
|
||||
{"missing file", "json", filepath.Join(t.TempDir(), "missing.json"), ""},
|
||||
}
|
||||
for _, tt := range errCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
before := se.db
|
||||
se.loadDatabase(tt.format, tt.path, tt.conn)
|
||||
if !hasPage(se, "error-dialog") {
|
||||
t.Error("expected error dialog")
|
||||
}
|
||||
if se.db != before || se.loadConfig != nil {
|
||||
t.Error("state must be unchanged on error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNewDatabase(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
se.loadConfig = &LoadConfig{SourceType: "json"}
|
||||
se.createNewDatabase()
|
||||
if se.db.Name != "New Database" || len(se.db.Schemas) != 0 || se.loadConfig != nil || !hasPage(se, "success-dialog") {
|
||||
t.Errorf("state: %+v", se.db)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveDatabase(t *testing.T) {
|
||||
for _, tt := range []struct{ format, file string }{
|
||||
{"json", "o.json"}, {"yaml", "o.yaml"}, {"dbml", "o.dbml"}, {"drawdb", "o.drawdb.json"},
|
||||
{"graphql", "o.graphql"}, {"prisma", "o.prisma"}, {"typeorm", "o.ts"}, {"drizzle", "d.ts"},
|
||||
{"gorm", "g.go"}, {"bun", "b.go"},
|
||||
} {
|
||||
t.Run(tt.format, func(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
out := filepath.Join(t.TempDir(), tt.file)
|
||||
se.saveDatabase(tt.format, out)
|
||||
if hasPage(se, "error-dialog") {
|
||||
t.Fatal("unexpected error dialog")
|
||||
}
|
||||
if se.saveConfig == nil || se.saveConfig.FilePath != out || se.saveConfig.TargetType != tt.format {
|
||||
t.Errorf("saveConfig: %+v", se.saveConfig)
|
||||
}
|
||||
if info, err := os.Stat(out); err != nil || info.Size() == 0 {
|
||||
t.Errorf("output: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for name, args := range map[string][2]string{
|
||||
"pgsql unsupported": {"pgsql", "x.sql"},
|
||||
"path required": {"json", ""},
|
||||
"unknown format": {"nope", "x"},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
se.saveDatabase(args[0], args[1])
|
||||
if !hasPage(se, "error-dialog") || se.saveConfig != nil {
|
||||
t.Error("expected error dialog and no saveConfig")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportAndMerge(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
se.importAndMergeDatabase("json", filepath.Join(uiFixtures, "json/database.json"), "", false, false, false, false, false, "")
|
||||
if hasPage(se, "error-dialog") {
|
||||
t.Fatal("unexpected error dialog")
|
||||
}
|
||||
|
||||
for name, args := range map[string][3]string{
|
||||
"pgsql no conn": {"pgsql", "", ""},
|
||||
"file required": {"json", "", ""},
|
||||
"unsupported": {"nope", "x", ""},
|
||||
"missing file": {"json", filepath.Join(t.TempDir(), "missing.json"), ""},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
se.importAndMergeDatabase(args[0], args[1], args[2], false, false, false, false, false, "")
|
||||
if !hasPage(se, "error-dialog") {
|
||||
t.Error("expected error dialog")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPerformMerge(t *testing.T) {
|
||||
se := newUIEditor()
|
||||
src := models.InitDatabase("src")
|
||||
s := models.InitSchema("public")
|
||||
tbl := models.InitTable("orders", "public")
|
||||
tbl.Columns["id"] = models.InitColumn("id", "orders", "public")
|
||||
skip := models.InitTable("skipme", "public")
|
||||
s.Tables = append(s.Tables, tbl, skip)
|
||||
src.Schemas = append(src.Schemas, s)
|
||||
|
||||
se.performMerge(src, false, false, false, false, false, "SkipMe")
|
||||
if !hasPage(se, "success-dialog") {
|
||||
t.Error("expected success dialog")
|
||||
}
|
||||
names := map[string]bool{}
|
||||
for _, tb := range se.db.Schemas[0].Tables {
|
||||
names[tb.Name] = true
|
||||
}
|
||||
if !names["users"] || !names["orders"] || names["skipme"] || len(names) != 2 {
|
||||
t.Errorf("tables after merge: %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditorAccessors(t *testing.T) {
|
||||
db := models.InitDatabase("d")
|
||||
lc, sc := &LoadConfig{SourceType: "json"}, &SaveConfig{TargetType: "yaml"}
|
||||
se := NewSchemaEditorWithConfigs(db, lc, sc)
|
||||
if se.GetDatabase() != db || se.loadConfig != lc || se.saveConfig != sc || se.app == nil || se.pages == nil {
|
||||
t.Errorf("%+v", se)
|
||||
}
|
||||
if se.createMainMenu() == nil {
|
||||
t.Error("main menu")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
// richEditor returns an editor whose database has one of every object the screens render.
|
||||
func richEditor(t *testing.T) *SchemaEditor {
|
||||
t.Helper()
|
||||
se := NewSchemaEditor(newTestEditor().db)
|
||||
db := se.db
|
||||
tbl := db.Schemas[0].Tables[0]
|
||||
col := tbl.Columns["id"]
|
||||
col.Type, col.IsPrimaryKey, col.NotNull = "integer", true, true
|
||||
tbl.Relationships["fk_self"] = &models.Relationship{Name: "fk_self", FromTable: "users", ToTable: "users", FromColumns: []string{"id"}, ToColumns: []string{"id"}}
|
||||
se.createDomainNoUI("core")
|
||||
if err := se.AssignTableToDomain(0, "public", "users"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = se.SaveIndex(0, 0, "", &models.Index{Name: "idx_e", Columns: []string{"email"}})
|
||||
_ = se.SaveView(0, -1, &models.View{Name: "v", Definition: "select 1"})
|
||||
_ = se.SaveSequence(0, -1, &models.Sequence{Name: "s", IncrementBy: 1, StartValue: 1})
|
||||
_ = se.SaveScript(0, -1, &models.Script{Name: "sc", SQL: "select 1"})
|
||||
return se
|
||||
}
|
||||
|
||||
// TestScreensRender builds every screen and dialog against a populated database
|
||||
// and checks that none panics and that each registers a page.
|
||||
func TestScreensRender(t *testing.T) {
|
||||
col := func(se *SchemaEditor) *models.Column { return se.db.Schemas[0].Tables[0].Columns["id"] }
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
page string
|
||||
run func(se *SchemaEditor)
|
||||
}{
|
||||
{"schema list", "schemas", func(se *SchemaEditor) { se.showSchemaList() }},
|
||||
{"schema editor", "schema-editor", func(se *SchemaEditor) { se.showSchemaEditor(0, se.db.Schemas[0]) }},
|
||||
{"new schema", "new-schema", func(se *SchemaEditor) { se.showNewSchemaDialog() }},
|
||||
{"edit schema", "edit-schema", func(se *SchemaEditor) { se.showEditSchemaDialog(0) }},
|
||||
{"table list", "tables", func(se *SchemaEditor) { se.showTableList() }},
|
||||
{"table editor", "table-editor", func(se *SchemaEditor) { se.showTableEditor(0, 0, se.db.Schemas[0].Tables[0]) }},
|
||||
{"new table", "new-table", func(se *SchemaEditor) { se.showNewTableDialog(0) }},
|
||||
{"new table from list", "new-table-from-list", func(se *SchemaEditor) { se.showNewTableDialogFromList() }},
|
||||
{"edit table", "edit-table", func(se *SchemaEditor) { se.showEditTableDialog(0, 0) }},
|
||||
{"column editor", "column-editor", func(se *SchemaEditor) { se.showColumnEditor(0, 0, 0, col(se)) }},
|
||||
{"new column", "new-column", func(se *SchemaEditor) { se.showNewColumnDialog(0, 0) }},
|
||||
{"relationship list", "relationships", func(se *SchemaEditor) { se.showRelationshipList(0, 0) }},
|
||||
{"new relationship", "new-relationship", func(se *SchemaEditor) { se.showNewRelationshipDialog(0, 0) }},
|
||||
{"edit relationship", "edit-relationship", func(se *SchemaEditor) { se.showEditRelationshipDialog(0, 0, "fk_self") }},
|
||||
{"delete relationship", "delete-relationship-confirm", func(se *SchemaEditor) { se.showDeleteRelationshipConfirm(0, 0, "fk_self") }},
|
||||
{"domain list", "domains", func(se *SchemaEditor) { se.showDomainList() }},
|
||||
{"new domain", "new-domain", func(se *SchemaEditor) { se.showNewDomainDialog() }},
|
||||
{"domain editor", "edit-domain", func(se *SchemaEditor) { se.showDomainEditor(0, se.db.Domains[0]) }},
|
||||
{"delete domain", "delete-domain-confirm", func(se *SchemaEditor) { se.showDeleteDomainConfirm(0) }},
|
||||
{"domain tables", "domain-tables", func(se *SchemaEditor) { se.showDomainTables(0) }},
|
||||
{"assign domain table", "assign-domain-table", func(se *SchemaEditor) { se.showAssignDomainTable(0, func() {}) }},
|
||||
{"edit database", "edit-database", func(se *SchemaEditor) { se.showEditDatabaseForm() }},
|
||||
{"exit confirm", "exit-confirm", func(se *SchemaEditor) { se.showExitConfirmation("a", "main") }},
|
||||
{"exit editor confirm", "exit-editor-confirm", func(se *SchemaEditor) { se.showExitEditorConfirm() }},
|
||||
{"delete schema confirm", "confirm-delete-schema", func(se *SchemaEditor) { se.showDeleteSchemaConfirm(0) }},
|
||||
{"delete table confirm", "confirm-delete-table", func(se *SchemaEditor) { se.showDeleteTableConfirm(0, 0) }},
|
||||
{"delete column confirm", "confirm-delete-column", func(se *SchemaEditor) { se.showDeleteColumnConfirm(0, 0, "id") }},
|
||||
{"load screen", "load-database", func(se *SchemaEditor) { se.showLoadScreen() }},
|
||||
{"save screen", "save-database", func(se *SchemaEditor) { se.showSaveScreen() }},
|
||||
{"import screen", "import-database", func(se *SchemaEditor) { se.showImportScreen() }},
|
||||
{"update existing confirm", "update-confirm", func(se *SchemaEditor) {
|
||||
se.loadConfig = &LoadConfig{SourceType: "json", FilePath: "x.json"}
|
||||
se.showUpdateExistingDatabaseConfirm()
|
||||
}},
|
||||
{"import confirm", "import-confirm", func(se *SchemaEditor) {
|
||||
se.showImportConfirmation(models.InitDatabase("src"), false, false, false, false, false, "")
|
||||
}},
|
||||
{"conn builder", "", func(se *SchemaEditor) { se.showConnStringBuilder("", "", "main", func(string) {}) }},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
se := richEditor(t)
|
||||
before := len(se.pages.GetPageNames(false))
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
tt.run(se)
|
||||
if tt.page != "" && !se.pages.HasPage(tt.page) {
|
||||
t.Errorf("page %q not registered; pages: %v", tt.page, se.pages.GetPageNames(false))
|
||||
}
|
||||
if tt.page == "" && len(se.pages.GetPageNames(false)) <= before {
|
||||
t.Error("no page added")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectScreensRender(t *testing.T) {
|
||||
se := richEditor(t)
|
||||
for name, k := range map[string]objectKind{
|
||||
"indexes": se.indexKind(), "views": se.viewKind(), "sequences": se.sequenceKind(), "scripts": se.scriptKind(),
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
se.showObjectList(k)
|
||||
if !se.pages.HasPage(k.page) {
|
||||
t.Errorf("list page %q missing; pages: %v", k.page, se.pages.GetPageNames(false))
|
||||
}
|
||||
rows := k.rows()
|
||||
se.showObjectForm(k, nil)
|
||||
se.showObjectForm(k, &rows[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user