mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 10:46:12 -04:00
fix(debug): Persist recording session across app restarts
The debug recording trigger file now stores the session directory path and start timestamp. On app restart, the recorder resumes into the same session directory and log file instead of creating a new one, preserving the original start time so the short-recording guard doesn't reset.
This commit is contained in:
+5
@@ -33,5 +33,10 @@ class RecorderModuleStateTest : BaseTest() {
|
||||
fun `currentLogPath is null`() {
|
||||
RecorderModule.State().currentLogPath shouldBe null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `persistedLogDir is null`() {
|
||||
RecorderModule.State().persistedLogDir shouldBe null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package eu.darken.capod.common.debug.recording.core
|
||||
|
||||
import io.kotest.matchers.nulls.shouldBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import testhelpers.BaseTest
|
||||
import java.io.File
|
||||
|
||||
class RecorderModuleTriggerFileTest : BaseTest() {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDir: File
|
||||
|
||||
private val now = 1_700_000_000_000L
|
||||
|
||||
@AfterEach
|
||||
fun cleanup() {
|
||||
tempDir.listFiles()?.forEach { it.deleteRecursively() }
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class ParseTriggerContent {
|
||||
|
||||
@Test
|
||||
fun `valid content returns dir and timestamp`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = "${sessionDir.absolutePath}\n$now"
|
||||
|
||||
val result = RecorderModule.parseTriggerContent(content, now = now)
|
||||
|
||||
result shouldBe (sessionDir to now)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty content returns null`() {
|
||||
RecorderModule.parseTriggerContent("", now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `blank content returns null`() {
|
||||
RecorderModule.parseTriggerContent(" \n ", now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `single line returns null`() {
|
||||
RecorderModule.parseTriggerContent("/some/path", now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-numeric timestamp returns null`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = "${sessionDir.absolutePath}\nnotanumber"
|
||||
|
||||
RecorderModule.parseTriggerContent(content, now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `zero timestamp returns null`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = "${sessionDir.absolutePath}\n0"
|
||||
|
||||
RecorderModule.parseTriggerContent(content, now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `negative timestamp returns null`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = "${sessionDir.absolutePath}\n-1000"
|
||||
|
||||
RecorderModule.parseTriggerContent(content, now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `future timestamp beyond skew returns null`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val futureTs = now + 120_000L
|
||||
val content = "${sessionDir.absolutePath}\n$futureTs"
|
||||
|
||||
RecorderModule.parseTriggerContent(content, now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `future timestamp within skew is accepted`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val nearFutureTs = now + 30_000L
|
||||
val content = "${sessionDir.absolutePath}\n$nearFutureTs"
|
||||
|
||||
val result = RecorderModule.parseTriggerContent(content, now = now)
|
||||
|
||||
result shouldBe (sessionDir to nearFutureTs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-existent directory returns null`() {
|
||||
val content = "/nonexistent/path/capod_session\n$now"
|
||||
|
||||
RecorderModule.parseTriggerContent(content, now = now).shouldBeNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content with trailing whitespace is handled`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = " ${sessionDir.absolutePath}\n$now \n"
|
||||
|
||||
val result = RecorderModule.parseTriggerContent(content, now = now)
|
||||
|
||||
result shouldBe (sessionDir to now)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `old timestamp is accepted`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val oldTs = 1_000L
|
||||
val content = "${sessionDir.absolutePath}\n$oldTs"
|
||||
|
||||
val result = RecorderModule.parseTriggerContent(content, now = now)
|
||||
|
||||
result shouldBe (sessionDir to oldTs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extra lines are ignored`() {
|
||||
val sessionDir = File(tempDir, "capod_session").also { it.mkdirs() }
|
||||
val content = "${sessionDir.absolutePath}\n$now\nextra\ndata"
|
||||
|
||||
val result = RecorderModule.parseTriggerContent(content, now = now)
|
||||
|
||||
result shouldBe (sessionDir to now)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user