@@ -12,6 +12,8 @@ import org.modelix.model.client2.runWriteOnBranch
1212import org.modelix.model.data.ModelData
1313import org.modelix.model.data.NodeData
1414import org.modelix.model.data.asData
15+ import org.modelix.model.lazy.BranchReference
16+ import org.modelix.model.lazy.CLVersion
1517import org.modelix.model.lazy.RepositoryId
1618import org.modelix.model.mutable.asModelSingleThreaded
1719import org.modelix.model.server.api.RepositoryConfig
@@ -25,14 +27,14 @@ import kotlin.test.Test
2527import kotlin.test.assertEquals
2628
2729class RepositoryMigrationTest {
28- val config = RepositoryConfig (
30+ private val config = RepositoryConfig (
2931 nodeIdType = NodeIdType .INT64 ,
3032 modelId = " d9330ca8-2145-4d1f-9b50-8f5aed1804cf" ,
3133 repositoryId = " d9330ca8-2145-4d1f-9b50-8f5aed1804cf" ,
3234 repositoryName = " my-repo" ,
3335 )
3436
35- val modelData = ModelData (
37+ private val modelData = ModelData (
3638 root = NodeData (
3739 id = PNodeReference (config.modelId, ITree .ROOT_ID ).serialize(),
3840 children = listOf (
@@ -55,7 +57,7 @@ class RepositoryMigrationTest {
5557 // A repository configured with int64 IDs is expected to generate new IDs and store the provided ID in the
5658 // #originalRef# property.
5759 // language=json
58- val expectedImportData = NodeData .fromJson(
60+ private val expectedImportData = NodeData .fromJson(
5961 """
6062 {
6163 "id": "modelix:d9330ca8-2145-4d1f-9b50-8f5aed1804cf/1",
@@ -90,37 +92,58 @@ class RepositoryMigrationTest {
9092 """ ,
9193 ).toJson()
9294
93- @Test
94- fun `migrate int64 to string IDs` () = runTest {
95- val repositoryManager = RepositoriesManager (InMemoryStoreClient ())
96-
97- @OptIn(RequiresTransaction ::class )
98- val version1 = repositoryManager.getTransactionManager().runWrite {
99- val emptyVersion = repositoryManager.createRepository(
100- config,
101- null ,
102- )
103- emptyVersion.runWrite(IdGenerator .newInstance(456 ), author = null ) {
95+ // Helper functions to reduce duplication
96+ @OptIn(RequiresTransaction ::class )
97+ private fun createRepositoryWithData (
98+ repositoryManager : RepositoriesManager ,
99+ config : RepositoryConfig ,
100+ ): CLVersion {
101+ return repositoryManager.getTransactionManager().runWrite {
102+ val emptyVersion = repositoryManager.createRepository(config, null )
103+ val versionWithData = emptyVersion.runWrite(IdGenerator .newInstance(456 ), author = null ) {
104104 modelData.load(it)
105- }!! .also { repositoryManager.mergeChanges(RepositoryId (config.repositoryId).getBranchReference(), it.getContentHash()) }
105+ }!! as CLVersion
106+ repositoryManager.mergeChanges(RepositoryId (config.repositoryId).getBranchReference(), versionWithData.getContentHash())
107+ versionWithData
106108 }
109+ }
107110
108- assertEquals(
109- expectedImportData,
110- version1.getModelTree().asModelSingleThreaded().getRootNode().asData().toJson(),
111- )
111+ @OptIn(RequiresTransaction ::class )
112+ private fun migrateRepository (
113+ repositoryManager : RepositoriesManager ,
114+ newConfig : RepositoryConfig ,
115+ ): CLVersion {
116+ return repositoryManager.getTransactionManager().runWrite {
117+ repositoryManager.migrateRepository(newConfig, null )
118+ repositoryManager.getVersion(RepositoryId (newConfig.repositoryId).getBranchReference())!!
119+ }
120+ }
112121
113- @OptIn(RequiresTransaction ::class )
114- val version2 = repositoryManager.getTransactionManager().runWrite {
115- repositoryManager.migrateRepository(
116- config.copy(nodeIdType = NodeIdType .STRING ),
117- null ,
118- )
119- repositoryManager.getVersion(RepositoryId (config.repositoryId).getBranchReference())!!
122+ @OptIn(RequiresTransaction ::class )
123+ private fun getRepositoryConfig (
124+ repositoryManager : RepositoriesManager ,
125+ repositoryId : RepositoryId ,
126+ branchRef : BranchReference ,
127+ ): RepositoryConfig {
128+ return repositoryManager.getTransactionManager().runRead {
129+ repositoryManager.getConfig(repositoryId, branchRef)
120130 }
131+ }
132+
133+ private fun extractNodeData (version : CLVersion ): String {
134+ return version.getModelTree().asModelSingleThreaded().getRootNode().asData().toJson()
135+ }
136+
137+ @Test
138+ fun `migrate int64 to string IDs` () = runTest {
139+ val repositoryManager = RepositoriesManager (InMemoryStoreClient ())
140+
141+ val version1 = createRepositoryWithData(repositoryManager, config)
142+ assertEquals(expectedImportData, extractNodeData(version1))
121143
144+ val version2 = migrateRepository(repositoryManager, config.copy(nodeIdType = NodeIdType .STRING ))
122145 // After migration the repository should use the IDs that were provided in the import data.
123- assertEquals(modelData.root.toJson(), version2.getModelTree().asModelSingleThreaded().getRootNode().asData().toJson( ))
146+ assertEquals(modelData.root.toJson(), extractNodeData(version2 ))
124147 }
125148
126149 @Test
@@ -138,7 +161,7 @@ class RepositoryMigrationTest {
138161
139162 val repositoryId = RepositoryId (config.repositoryId)
140163 val modelClient = ModelClientV2 .builder().url(" http://localhost/v2" ).client(client).build().also { it.init () }
141- val version0 = modelClient.initRepository(config)
164+ modelClient.initRepository(config)
142165 modelClient.runWriteOnBranch(repositoryId.getBranchReference()) {
143166 modelData.load(it)
144167 }
@@ -167,49 +190,51 @@ class RepositoryMigrationTest {
167190
168191 // Create repository with global storage (legacyGlobalStorage = true)
169192 val globalConfig = config.copy(legacyGlobalStorage = true )
170-
171- @OptIn(RequiresTransaction ::class )
172- val version1 = repositoryManager.getTransactionManager().runWrite {
173- val emptyVersion = repositoryManager.createRepository(globalConfig, null )
174- emptyVersion.runWrite(IdGenerator .newInstance(456 ), author = null ) {
175- modelData.load(it)
176- }!! .also {
177- repositoryManager.mergeChanges(branchRef, it.getContentHash())
178- }
179- }
193+ createRepositoryWithData(repositoryManager, globalConfig)
180194
181195 // Verify initial config has global storage
182- @OptIn(RequiresTransaction ::class )
183- val configBeforeMigration = repositoryManager.getTransactionManager().runRead {
184- repositoryManager.getConfig(repositoryId, branchRef)
185- }
196+ val configBeforeMigration = getRepositoryConfig(repositoryManager, repositoryId, branchRef)
186197 assertEquals(true , configBeforeMigration.legacyGlobalStorage, " Repository should start with global storage" )
187198
188199 // Migrate to isolated storage
189- @OptIn(RequiresTransaction ::class )
190- repositoryManager.getTransactionManager().runWrite {
191- repositoryManager.migrateRepository(
192- globalConfig.copy(legacyGlobalStorage = false ),
193- null ,
194- )
195- }
200+ val version2 = migrateRepository(repositoryManager, globalConfig.copy(legacyGlobalStorage = false ))
196201
197202 // Verify config after migration has isolated storage
198- @OptIn(RequiresTransaction ::class )
199- val configAfterMigration = repositoryManager.getTransactionManager().runRead {
200- repositoryManager.getConfig(repositoryId, branchRef)
201- }
203+ val configAfterMigration = getRepositoryConfig(repositoryManager, repositoryId, branchRef)
202204 assertEquals(false , configAfterMigration.legacyGlobalStorage, " Repository should have isolated storage after migration" )
203205
204206 // Verify data is preserved after migration
205- @OptIn(RequiresTransaction ::class )
206- val version2 = repositoryManager.getTransactionManager().runRead {
207- repositoryManager.getVersion(branchRef)!!
208- }
209- assertEquals(
210- expectedImportData,
211- version2.getModelTree().asModelSingleThreaded().getRootNode().asData().toJson(),
212- " Data should be preserved after migration" ,
213- )
207+ assertEquals(expectedImportData, extractNodeData(version2), " Data should be preserved after migration" )
208+ }
209+
210+ @Test
211+ fun `migrate int64 to string IDs and global to isolated storage simultaneously` () = runTest {
212+ val repositoryManager = RepositoriesManager (InMemoryStoreClient ())
213+ val repositoryId = RepositoryId (config.repositoryId)
214+ val branchRef = repositoryId.getBranchReference()
215+
216+ // Create repository with int64 IDs and global storage
217+ val initialConfig = config.copy(nodeIdType = NodeIdType .INT64 , legacyGlobalStorage = true )
218+ val version1 = createRepositoryWithData(repositoryManager, initialConfig)
219+
220+ // Verify initial configuration
221+ val configBeforeMigration = getRepositoryConfig(repositoryManager, repositoryId, branchRef)
222+ assertEquals(NodeIdType .INT64 , configBeforeMigration.nodeIdType, " Repository should start with INT64 IDs" )
223+ assertEquals(true , configBeforeMigration.legacyGlobalStorage, " Repository should start with global storage" )
224+
225+ // Verify initial data has generated IDs
226+ assertEquals(expectedImportData, extractNodeData(version1))
227+
228+ // Migrate both: int64 -> string IDs AND global -> isolated storage
229+ val targetConfig = config.copy(nodeIdType = NodeIdType .STRING , legacyGlobalStorage = false )
230+ val version2 = migrateRepository(repositoryManager, targetConfig)
231+
232+ // Verify config after migration
233+ val configAfterMigration = getRepositoryConfig(repositoryManager, repositoryId, branchRef)
234+ assertEquals(NodeIdType .STRING , configAfterMigration.nodeIdType, " Repository should have STRING IDs after migration" )
235+ assertEquals(false , configAfterMigration.legacyGlobalStorage, " Repository should have isolated storage after migration" )
236+
237+ // After migration the repository should use the original IDs from import data
238+ assertEquals(modelData.root.toJson(), extractNodeData(version2), " Data should use original IDs after migration" )
214239 }
215240}
0 commit comments