Skip to content

Commit eb7a786

Browse files
committed
test(model-server): RepositoryMigrationTest is tested for simultaneously changing type and storage
1 parent a334116 commit eb7a786

File tree

1 file changed

+87
-62
lines changed

1 file changed

+87
-62
lines changed

model-server/src/test/kotlin/org/modelix/model/server/RepositoryMigrationTest.kt

Lines changed: 87 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import org.modelix.model.client2.runWriteOnBranch
1212
import org.modelix.model.data.ModelData
1313
import org.modelix.model.data.NodeData
1414
import org.modelix.model.data.asData
15+
import org.modelix.model.lazy.BranchReference
16+
import org.modelix.model.lazy.CLVersion
1517
import org.modelix.model.lazy.RepositoryId
1618
import org.modelix.model.mutable.asModelSingleThreaded
1719
import org.modelix.model.server.api.RepositoryConfig
@@ -25,14 +27,14 @@ import kotlin.test.Test
2527
import kotlin.test.assertEquals
2628

2729
class 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

Comments
 (0)