// 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 tests import ( "testing" "github.com/stretchr/testify/require" "xorm.io/xorm/schemas" ) // sameDefinitionIndexes declares two indexes with an identical definition. // Sync must keep one database index per declared index instead of letting // both declarations claim the same database index. type sameDefinitionIndexes struct { Id int64 RepoId int64 CreatedAt int64 } func (*sameDefinitionIndexes) TableIndices() []*schemas.Index { first := schemas.NewIndex("first", schemas.IndexType) first.AddColumn("repo_id", "created_at") second := schemas.NewIndex("second", schemas.IndexType) second.AddColumn("repo_id", "created_at") return []*schemas.Index{first, second} } func assertSameDefinitionIndexes(t *testing.T, bean *sameDefinitionIndexes) { t.Helper() tables, err := testEngine.DBMetas() require.NoError(t, err) require.Len(t, tables, 1) require.Len(t, tables[0].Indexes, 2) for _, expected := range bean.TableIndices() { actual := tables[0].Indexes[expected.Name] require.NotNil(t, actual, "missing index %s", expected.Name) require.Equal(t, expected.Cols, actual.Cols) } } func TestSyncSameDefinitionIndexes(t *testing.T) { require.NoError(t, PrepareEngine()) bean := new(sameDefinitionIndexes) require.NoError(t, testEngine.Sync(bean)) assertSameDefinitionIndexes(t, bean) for i := 0; i < 10; i++ { require.NoError(t, testEngine.Sync(bean)) assertSameDefinitionIndexes(t, bean) } for _, index := range bean.TableIndices() { _, err := testEngine.Exec(testEngine.Dialect().DropIndexSQL(testEngine.TableName(bean), index)) require.NoError(t, err) require.NoError(t, testEngine.Sync(bean)) assertSameDefinitionIndexes(t, bean) } } type renamedIndexBefore struct { Id int64 RepoId int64 `xorm:"index(old_name)"` } func (*renamedIndexBefore) TableName() string { return "renamed_index" } type renamedIndexAfter struct { Id int64 RepoId int64 `xorm:"index(new_name)"` } func (*renamedIndexAfter) TableName() string { return "renamed_index" } // TestSyncRenamedIndexKeepsExisting guards the fallback: an index whose // declared name changed but whose definition did not is kept as it is. func TestSyncRenamedIndexKeepsExisting(t *testing.T) { require.NoError(t, PrepareEngine()) require.NoError(t, testEngine.Sync(new(renamedIndexBefore))) require.NoError(t, testEngine.Sync(new(renamedIndexAfter))) tables, err := testEngine.DBMetas() require.NoError(t, err) require.Len(t, tables, 1) require.Len(t, tables[0].Indexes, 1) require.NotNil(t, tables[0].Indexes["old_name"]) }