package coffee.liz.rookery import coffee.liz.rookery.importer.CsTimerImport import coffee.liz.rookery.importer.Importer import coffee.liz.rookery.importer.LetterPairGymImport import coffee.liz.rookery.model.Event import coffee.liz.rookery.model.Penalty import coffee.liz.rookery.store.MemoryBlobStore import coffee.liz.rookery.store.MemoryLogStorage import coffee.liz.rookery.store.Repo import coffee.liz.rookery.store.Store import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class ImportTest { private val csTimer = """ {"session1":[ [[0,114010],"Uw' L2 D L' R2 Dw D' R' F D Bw2 B2 Lw2 D2 Rw' Dw2 Lw' U' Dw' F2 B2 Rw D' Dw2 Lw2 R2 B2 Dw' Fw' Rw B2 Lw2 Uw' D F' Bw2 U Uw Bw2 F' R Dw2 Uw Rw' Dw D2 U Uw Lw' B2 F R' Uw2 B' R Lw2 B Lw L2 B'","",1794373390], [[0,25058],"B' R' F2 U2 L2 F2 U2 B' L2 B2 U2 F' R2 U' F2 L D' L' B L2 F","",1794526179,["U'@0 L'@200","333"]], [[2000,14039],"R U F","nice",1794526518], [[-1,14245],"D2 F","",1794534484]], "session2":[[[0,4000],"R U R' U'","",1794534500]], "properties":{"sessionData":"{\"1\":{\"name\":1,\"opt\":{}},\"2\":{\"name\":\"pyra\",\"opt\":{\"scrType\":\"pyrso\"}}}","giiMacMap":"{\"null\":\"AA:BB:CC:DD:EE:FF\"}"}} """.trimIndent() @Test fun parsesCsTimer() { val r = CsTimerImport.parse(csTimer) assertEquals(5, r.solves.size) val events = r.solves.map { it.event } assertEquals(listOf(Event.E555, Event.E333, Event.E333, Event.E333, Event.PYRAM), events) assertEquals(listOf(Penalty.NONE, Penalty.NONE, Penalty.PLUS2, Penalty.DNF), r.solves.take(4).map { it.penalty }) assertEquals("U'@0 L'@200", r.solves[1].moves) assertEquals(1794373390_000, r.solves[0].at) assertEquals(3, r.sessions.size) assertTrue(r.sessions.any { it.name == "csTimer 1 (5x5)" }) assertEquals("AA:BB:CC:DD:EE:FF", r.devices.single().mac) } @Test fun importIsIdempotent() { val repo = Repo(Store("t", MemoryLogStorage(), MemoryBlobStore()) { 1 }) Importer(repo).csTimer(csTimer) Importer(repo).csTimer(csTimer) assertEquals(5, repo.solves().size) assertEquals(3, repo.sessions().size) assertEquals(1, repo.devices().size) } private val gym = """ {"app":"letter-pair-gym","v":1,"at":"2026-11-13T03:28:27.903Z", "settings":{"edgeScheme":"ABCDQRSTEFGHIJKLMNOPUVWX","eBuf":2}, "words":{"AB":"abacus","AD":"adelie","XW":""}, "notes":{"AD":"adelie penguin"}, "wordHist":[{"d":1794519658205,"from":"Peg","p":"PG","to":"Postgres"}], "stats":{"AB":{"k":1,"l":[689],"m":5,"ms":61704.39,"n":11,"r":"gsmmsg","s":4}}, "memoHist":[{"d":1794505722395,"ms":154339,"ok":8,"p":10}], "solves":[], "images":{"AB":"images/AB - abacus.jpg","AD":"images/AD - missing.jpg"}} """.trimIndent() @Test fun importsLetterPairGym() { val parsed = LetterPairGymImport.parse(gym) assertEquals(mapOf("AB" to "abacus", "AD" to "adelie"), parsed.words) assertEquals(listOf(689L), parsed.stats.getValue("AB").latenciesMs) val repo = Repo(Store("t", MemoryLogStorage(), MemoryBlobStore()) { 1 }) val summary = Importer(repo).letterPairGym(gym) { path -> if (path.startsWith("images/AB")) byteArrayOf(1, 2, 3) else null } val pairs = repo.letterPairs() assertEquals("abacus", pairs.getValue("AB").primary) assertEquals("adelie penguin", pairs.getValue("AD").note) assertTrue(pairs.getValue("AB").image!!.endsWith(".jpg")) assertEquals(1, summary.warnings.size) assertEquals("Postgres", repo.wordHistory().single().to) assertEquals(1, repo.memoSessions().size) } }