package models import ( "reflect" "testing" ) type sortCase struct { name string seq uint } var sortFixture = []sortCase{{"Banana", 3}, {"apple", 1}, {"Cherry", 2}} var ( wantNameAsc = []string{"apple", "Banana", "Cherry"} wantNameDesc = []string{"Cherry", "Banana", "apple"} wantSeqAsc = []string{"apple", "Cherry", "Banana"} wantSeqDesc = []string{"Banana", "Cherry", "apple"} ) func checkNames(t *testing.T, label string, got, want []string) { t.Helper() if !reflect.DeepEqual(got, want) { t.Errorf("%s: got %v, want %v", label, got, want) } } // runSortSuite exercises a by-name and by-sequence sorter pair over the shared fixture. func runSortSuite[T any](t *testing.T, build func(sortCase) T, name func(T) string, byName func([]T, bool) error, bySeq func([]T, bool) error) { t.Helper() mk := func() []T { out := make([]T, 0, len(sortFixture)) for _, c := range sortFixture { out = append(out, build(c)) } return out } names := func(items []T) []string { out := make([]string, 0, len(items)) for _, it := range items { out = append(out, name(it)) } return out } if byName != nil { items := mk() _ = byName(items, false) checkNames(t, "name asc", names(items), wantNameAsc) _ = byName(items, true) checkNames(t, "name desc", names(items), wantNameDesc) _ = byName(nil, false) _ = byName([]T{}, true) } if bySeq != nil { items := mk() _ = bySeq(items, false) checkNames(t, "seq asc", names(items), wantSeqAsc) _ = bySeq(items, true) checkNames(t, "seq desc", names(items), wantSeqDesc) _ = bySeq(nil, false) } } func TestSortSchemas(t *testing.T) { runSortSuite(t, func(c sortCase) *Schema { return &Schema{Name: c.name, Sequence: c.seq} }, func(s *Schema) string { return s.Name }, SortSchemasByName, SortSchemasBySequence) } func TestSortTables(t *testing.T) { runSortSuite(t, func(c sortCase) *Table { return &Table{Name: c.name, Sequence: c.seq} }, func(s *Table) string { return s.Name }, SortTablesByName, SortTablesBySequence) } func TestSortColumns(t *testing.T) { runSortSuite(t, func(c sortCase) *Column { return &Column{Name: c.name, Sequence: c.seq} }, func(s *Column) string { return s.Name }, SortColumnsByName, SortColumnsBySequence) } func TestSortViews(t *testing.T) { runSortSuite(t, func(c sortCase) *View { return &View{Name: c.name, Sequence: c.seq} }, func(s *View) string { return s.Name }, SortViewsByName, SortViewsBySequence) } func TestSortSequences(t *testing.T) { runSortSuite(t, func(c sortCase) *Sequence { return &Sequence{Name: c.name, Sequence: c.seq} }, func(s *Sequence) string { return s.Name }, SortSequencesByName, SortSequencesBySequence) } func TestSortIndexes(t *testing.T) { runSortSuite(t, func(c sortCase) *Index { return &Index{Name: c.name, Sequence: c.seq} }, func(s *Index) string { return s.Name }, SortIndexesByName, SortIndexesBySequence) } func TestSortNameOnly(t *testing.T) { runSortSuite(t, func(c sortCase) *Constraint { return &Constraint{Name: c.name} }, func(s *Constraint) string { return s.Name }, SortConstraintsByName, nil) runSortSuite(t, func(c sortCase) *Relationship { return &Relationship{Name: c.name} }, func(s *Relationship) string { return s.Name }, SortRelationshipsByName, nil) runSortSuite(t, func(c sortCase) *Script { return &Script{Name: c.name} }, func(s *Script) string { return s.Name }, SortScriptsByName, nil) runSortSuite(t, func(c sortCase) *Enum { return &Enum{Name: c.name} }, func(s *Enum) string { return s.Name }, SortEnumsByName, nil) } func TestSortStableForTies(t *testing.T) { cols := []*Column{{Name: "x", Description: "first"}, {Name: "X", Description: "second"}, {Name: "x", Description: "third"}} _ = SortColumnsByName(cols, false) if cols[0].Description != "first" || cols[1].Description != "second" || cols[2].Description != "third" { t.Errorf("ties must keep input order: %v %v %v", cols[0].Description, cols[1].Description, cols[2].Description) } _ = SortColumnsBySequence(cols, true) if cols[0].Description != "first" || cols[2].Description != "third" { t.Errorf("sequence ties must keep input order") } } func TestSortMapVariants(t *testing.T) { cols := map[string]*Column{} idx := map[string]*Index{} cons := map[string]*Constraint{} rels := map[string]*Relationship{} for _, c := range sortFixture { cols[c.name] = &Column{Name: c.name, Sequence: c.seq} idx[c.name] = &Index{Name: c.name, Sequence: c.seq} cons[c.name] = &Constraint{Name: c.name} rels[c.name] = &Relationship{Name: c.name} } colNames := func(l []*Column) (o []string) { for _, x := range l { o = append(o, x.Name) } return } idxNames := func(l []*Index) (o []string) { for _, x := range l { o = append(o, x.Name) } return } conNames := func(l []*Constraint) (o []string) { for _, x := range l { o = append(o, x.Name) } return } relNames := func(l []*Relationship) (o []string) { for _, x := range l { o = append(o, x.Name) } return } checkNames(t, "cols name", colNames(SortColumnsMapByName(cols, false)), wantNameAsc) checkNames(t, "cols name desc", colNames(SortColumnsMapByName(cols, true)), wantNameDesc) checkNames(t, "cols seq", colNames(SortColumnsMapBySequence(cols, false)), wantSeqAsc) checkNames(t, "cols seq desc", colNames(SortColumnsMapBySequence(cols, true)), wantSeqDesc) checkNames(t, "idx name", idxNames(SortIndexesMapByName(idx, false)), wantNameAsc) checkNames(t, "idx seq", idxNames(SortIndexesMapBySequence(idx, true)), wantSeqDesc) checkNames(t, "con name", conNames(SortConstraintsMapByName(cons, false)), wantNameAsc) checkNames(t, "rel name", relNames(SortRelationshipsMapByName(rels, true)), wantNameDesc) if got := SortColumnsMapByName(nil, false); got == nil || len(got) != 0 { t.Errorf("nil map must give non-nil empty slice") } if len(cols) != 3 { t.Error("input map must not be modified") } }