// Copyright 2026 The Xorm Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package xorm import ( "testing" "github.com/stretchr/testify/assert" "xorm.io/xorm/schemas" ) func newTestIndex(name string, indexType int, cols ...string) *schemas.Index { index := schemas.NewIndex(name, indexType) index.AddColumn(cols...) return index } func indexMap(indexes ...*schemas.Index) map[string]*schemas.Index { m := make(map[string]*schemas.Index, len(indexes)) for _, index := range indexes { m[index.Name] = index } return m } func TestMatchIndexes(t *testing.T) { for _, tc := range []struct { name string expected map[string]*schemas.Index existing map[string]*schemas.Index wantClaimed []string wantMissing []string }{ { name: "same definition, both exist", expected: indexMap( newTestIndex("first", schemas.IndexType, "repo_id", "created_at"), newTestIndex("second", schemas.IndexType, "repo_id", "created_at"), ), existing: indexMap( newTestIndex("first", schemas.IndexType, "repo_id", "created_at"), newTestIndex("second", schemas.IndexType, "repo_id", "created_at"), ), wantClaimed: []string{"first", "second"}, }, { name: "same definition, first missing", expected: indexMap( newTestIndex("first", schemas.IndexType, "repo_id", "created_at"), newTestIndex("second", schemas.IndexType, "repo_id", "created_at"), ), existing: indexMap( newTestIndex("second", schemas.IndexType, "repo_id", "created_at"), ), wantClaimed: []string{"second"}, wantMissing: []string{"first"}, }, { name: "same definition, second missing", expected: indexMap( newTestIndex("first", schemas.IndexType, "repo_id", "created_at"), newTestIndex("second", schemas.IndexType, "repo_id", "created_at"), ), existing: indexMap( newTestIndex("first", schemas.IndexType, "repo_id", "created_at"), ), wantClaimed: []string{"first"}, wantMissing: []string{"second"}, }, { name: "same columns in different order", expected: indexMap( newTestIndex("c_u", schemas.IndexType, "user_id", "is_deleted", "created_unix"), newTestIndex("c_u_d", schemas.IndexType, "created_unix", "user_id", "is_deleted"), ), existing: indexMap( newTestIndex("c_u_d", schemas.IndexType, "created_unix", "user_id", "is_deleted"), ), wantClaimed: []string{"c_u_d"}, wantMissing: []string{"c_u"}, }, { name: "renamed index keeps the existing one", expected: indexMap(newTestIndex("new_name", schemas.IndexType, "repo_id")), existing: indexMap(newTestIndex("old_name", schemas.IndexType, "repo_id")), wantClaimed: []string{"old_name"}, }, { name: "changed definition under the same name", expected: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id", "created_at")), existing: indexMap(newTestIndex("repo", schemas.IndexType, "created_at", "repo_id")), wantMissing: []string{"repo"}, }, { name: "index turned into a unique", expected: indexMap(newTestIndex("repo", schemas.UniqueType, "repo_id")), existing: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id")), wantMissing: []string{"repo"}, }, { name: "same name wins over a renamed duplicate", expected: indexMap( newTestIndex("first", schemas.IndexType, "repo_id"), newTestIndex("second", schemas.IndexType, "repo_id"), ), existing: indexMap( newTestIndex("legacy", schemas.IndexType, "repo_id"), newTestIndex("second", schemas.IndexType, "repo_id"), ), wantClaimed: []string{"legacy", "second"}, }, { name: "existing index is claimed only once", expected: indexMap( newTestIndex("a", schemas.IndexType, "repo_id"), newTestIndex("b", schemas.IndexType, "repo_id"), ), existing: indexMap(newTestIndex("legacy", schemas.IndexType, "repo_id")), wantClaimed: []string{"legacy"}, wantMissing: []string{"b"}, }, { name: "stale index is left unclaimed", expected: indexMap(newTestIndex("repo", schemas.IndexType, "repo_id")), existing: indexMap( newTestIndex("repo", schemas.IndexType, "repo_id"), newTestIndex("stale", schemas.IndexType, "owner_id"), ), wantClaimed: []string{"repo"}, }, } { t.Run(tc.name, func(t *testing.T) { for i := 0; i < 20; i++ { claimed, missing := matchIndexes(tc.expected, tc.existing) var claimedNames []string for name, ok := range claimed { if ok { claimedNames = append(claimedNames, name) } } assert.ElementsMatch(t, tc.wantClaimed, claimedNames) assert.ElementsMatch(t, tc.wantMissing, sortedIndexNames(missing)) } }) } }