@@ -5,7 +5,13 @@ import io.klibs.core.pckg.entity.MavenArtifactEntity
55import io.klibs.core.pckg.dto.MavenCoordinatesDTO
66import io.klibs.core.pckg.repository.MavenArtifactRepository
77import org.junit.jupiter.api.Test
8+ import org.junit.jupiter.api.assertThrows
9+ import org.mockito.Mockito.doReturn
10+ import org.mockito.Mockito.verify
11+ import org.mockito.kotlin.eq
12+ import org.mockito.kotlin.whenever
813import org.springframework.beans.factory.annotation.Autowired
14+ import org.springframework.test.context.bean.override.mockito.MockitoSpyBean
915import org.springframework.test.context.ActiveProfiles
1016import kotlin.test.assertEquals
1117import kotlin.test.assertNotNull
@@ -15,14 +21,14 @@ import kotlin.test.assertTrue
1521class MavenArtifactServiceTest : BaseUnitWithDbLayerTest () {
1622
1723 @Autowired
18- private lateinit var mavenArtifactService : MavenArtifactService
24+ private lateinit var uut : MavenArtifactService
1925
20- @Autowired
26+ @MockitoSpyBean
2127 private lateinit var mavenArtifactRepository: MavenArtifactRepository
2228
2329 @Test
2430 fun `resolveOrCreateAll returns empty map for empty input` () {
25- val result = mavenArtifactService .resolveOrCreateAll(emptySet())
31+ val result = uut .resolveOrCreateAll(emptySet())
2632
2733 assertTrue(result.isEmpty())
2834 assertEquals(0L , mavenArtifactRepository.count())
@@ -36,7 +42,7 @@ class MavenArtifactServiceTest : BaseUnitWithDbLayerTest() {
3642 MavenCoordinatesDTO (" io.klibs" , " beta" , " 1.0.0" ),
3743 )
3844
39- val resolved = mavenArtifactService .resolveOrCreateAll(coords)
45+ val resolved = uut .resolveOrCreateAll(coords)
4046
4147 assertEquals(coords, resolved.keys)
4248 val ids = resolved.values.map { requireNotNull(it.id) }
@@ -64,11 +70,46 @@ class MavenArtifactServiceTest : BaseUnitWithDbLayerTest() {
6470 MavenCoordinatesDTO (" io.klibs" , " alpha" , " 2.0.0" ),
6571 )
6672
67- val firstRun = mavenArtifactService .resolveOrCreateAll(coords)
68- val secondRun = mavenArtifactService .resolveOrCreateAll(coords)
73+ val firstRun = uut .resolveOrCreateAll(coords)
74+ val secondRun = uut .resolveOrCreateAll(coords)
6975
7076 assertEquals(preSavedEntityId, firstRun.getValue(MavenCoordinatesDTO (" io.klibs" , " alpha" , " 1.0.0" )).id)
71- assertEquals(firstRun.mapValues { it.value.id }, secondRun.mapValues { it.value.id }, )
77+ assertEquals(firstRun.mapValues { it.value.id }, secondRun.mapValues { it.value.id })
7278 assertEquals(2L , mavenArtifactRepository.count())
7379 }
80+
81+ @Test
82+ fun `resolveOrCreate recovers existing row when saveIfAbsent loses the race` () {
83+ val coords = MavenCoordinatesDTO (" io.klibs" , " race" , " 1.0.0" )
84+ val concurrentlyInserted = mavenArtifactRepository.save(
85+ MavenArtifactEntity (groupId = coords.groupId, artifactId = coords.artifactId, version = coords.version)
86+ )
87+ val expectedId = requireNotNull(concurrentlyInserted.id)
88+
89+ doReturn(null , concurrentlyInserted)
90+ .whenever(mavenArtifactRepository)
91+ .findByGroupIdAndArtifactIdAndVersion(eq(coords.groupId), eq(coords.artifactId), eq(coords.version))
92+
93+ val result = uut.resolveOrCreate(coords)
94+
95+ assertEquals(expectedId, result.id)
96+ assertEquals(coords.groupId, result.groupId)
97+ assertEquals(coords.artifactId, result.artifactId)
98+ assertEquals(coords.version, result.version)
99+ verify(mavenArtifactRepository).saveIfAbsent(coords.groupId, coords.artifactId, coords.version)
100+ }
101+
102+ @Test
103+ fun `insertOrLookup throws when saveIfAbsent lost race but row cannot be re-read` () {
104+ val coords = MavenCoordinatesDTO (" io.klibs" , " missing" , " 1.0.0" )
105+ doReturn(0L )
106+ .whenever(mavenArtifactRepository)
107+ .saveIfAbsent(eq(coords.groupId), eq(coords.artifactId), eq(coords.version))
108+ doReturn(null )
109+ .whenever(mavenArtifactRepository)
110+ .findByGroupIdAndArtifactIdAndVersion(eq(coords.groupId), eq(coords.artifactId), eq(coords.version))
111+
112+ val ex = assertThrows<IllegalArgumentException > { uut.resolveOrCreate(coords) }
113+ assertTrue(ex.message!! .contains(" maven_artifact row is still missing after upsert" ))
114+ }
74115}
0 commit comments