Remove :storage-inmemory module, tests, and related code.
ci / JVM build + tests (push) Successful in 6m46s

This commit is contained in:
2026-09-23 03:48:38 +03:00
parent 84f5fd84f3
commit e447525059
105 changed files with 2732 additions and 1978 deletions
@@ -0,0 +1,79 @@
package pw.binom.agentik.reflection.inmemory
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import pw.binom.agentik.reflection.Reflection
import pw.binom.agentik.reflection.ReflectionEvent
import pw.binom.agentik.reflection.ReflectionStore
import kotlin.time.Instant
/**
* Thread-safe Map-импл [ReflectionStore].
*
* `weakSpots` хранятся как List<String> в самой структуре — JSON-сериализация
* делается на уровне SQLDelight в SQLite-импле; здесь просто держим в памяти.
*/
class InMemoryReflectionStore : ReflectionStore {
private val byId: MutableMap<String, Reflection> = mutableMapOf()
private val byConv: MutableMap<String, MutableList<String>> = mutableMapOf()
private val all: MutableList<String> = mutableListOf() // FIFO порядок insert (для listRecent)
private val events = MutableSharedFlow<ReflectionEvent>(extraBufferCapacity = 16)
private val mutex = Mutex()
override suspend fun insert(reflection: Reflection) {
mutex.withLock {
byId[reflection.id] = reflection
byConv.getOrPut(reflection.conversationId ?: "") { mutableListOf() }.add(reflection.id)
all.add(reflection.id)
}
events.tryEmit(ReflectionEvent.Created(reflection))
}
override suspend fun get(id: String): Reflection? {
mutex.withLock { return byId[id] }
}
override suspend fun listRecent(limit: Int): List<Reflection> {
mutex.withLock {
return all.asReversed().asSequence()
.mapNotNull { byId[it] }
.take(limit)
.toList()
}
}
override suspend fun listForConversation(conversationId: String, limit: Int): List<Reflection> {
mutex.withLock {
val ids = byConv[conversationId].orEmpty()
return ids.asReversed().asSequence()
.mapNotNull { byId[it] }
.take(limit)
.toList()
}
}
override suspend fun deleteOlderThan(cutoff: Instant) {
mutex.withLock {
val toRemove = byId.values.filter { it.createdAt < cutoff }.map { it.id }
for (id in toRemove) {
byId.remove(id)
all.remove(id)
byConv.values.forEach { it.remove(id) }
}
}
}
override suspend fun count(): Int {
mutex.withLock { return byId.size }
}
override fun events(): Flow<ReflectionEvent> = events.asSharedFlow()
override fun close() {
// no-op
}
}
@@ -0,0 +1,95 @@
package pw.binom.agentik.reflection.inmemory
import kotlinx.coroutines.test.runTest
import pw.binom.agentik.reflection.Reflection
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.time.Instant
class InMemoryReflectionStoreTest {
private fun refl(
id: String,
conv: String? = null,
ts: Instant = Instant.parse("2026-09-15T10:00:00Z"),
score: Int = 3,
summary: String = "ok",
weak: List<String> = listOf("weak1", "weak2"),
) = Reflection(
id = id,
conversationId = conv,
createdAt = ts,
turnsAnalyzed = 5,
score = score,
summary = summary,
weakSpots = weak,
)
@Test
fun testInsertAndGetRoundtrip() = runTest {
val store = InMemoryReflectionStore()
store.insert(refl("r1"))
val got = store.get("r1")
assertNotNull(got)
assertEquals("r1", got.id)
assertEquals(listOf("weak1", "weak2"), got.weakSpots)
}
@Test
fun testGetReturnsNullForMissing() = runTest {
val store = InMemoryReflectionStore()
assertNull(store.get("nope"))
}
@Test
fun testListRecentSortedByCreatedAtDesc() = runTest {
val store = InMemoryReflectionStore()
val t0 = Instant.parse("2026-09-15T10:00:00Z")
store.insert(refl("r1", ts = t0))
store.insert(refl("r2", ts = t0 + kotlin.time.Duration.parse("PT60S")))
store.insert(refl("r3", ts = t0 + kotlin.time.Duration.parse("PT120S")))
val recent = store.listRecent(limit = 3)
assertEquals(listOf("r3", "r2", "r1"), recent.map { it.id })
}
@Test
fun testListForConversationFilters() = runTest {
val store = InMemoryReflectionStore()
val t = Instant.parse("2026-09-15T10:00:00Z")
store.insert(refl("r1", conv = "c-1"))
store.insert(refl("r2", conv = "c-2"))
store.insert(refl("r3", conv = "c-1"))
val forC1 = store.listForConversation("c-1", limit = 10)
assertEquals(2, forC1.size)
}
@Test
fun testDeleteOlderThanRemoves() = runTest {
val store = InMemoryReflectionStore()
val t0 = Instant.parse("2026-09-15T10:00:00Z")
store.insert(refl("r1", ts = t0))
store.insert(refl("r2", ts = t0 + kotlin.time.Duration.parse("PT1H")))
store.deleteOlderThan(t0 + kotlin.time.Duration.parse("PT30M"))
assertNull(store.get("r1"))
assertNotNull(store.get("r2"))
}
@Test
fun testCountReturnsTotal() = runTest {
val store = InMemoryReflectionStore()
assertEquals(0, store.count())
store.insert(refl("r1"))
store.insert(refl("r2"))
assertEquals(2, store.count())
}
@Test
fun testCloseIdempotent() = runTest {
val store = InMemoryReflectionStore()
store.close()
store.close() // no-op
}
}