git.iko.soy/opencode-android
on 0e7d36c help sign in
90 lines 3.6 kB history raw
1package soy.iko.opencode.data.model
3import kotlinx.serialization.json.Json
4import org.junit.Assert.assertEquals
5import org.junit.Assert.assertTrue
6import org.junit.Test
8class TodoPlanTest {
9 private val json = Json { ignoreUnknownKeys = true; isLenient = true }
10 private fun element(s: String) = json.parseToJsonElement(s)
12 @Test
13 fun parsesTodosFromObject() {
14 val todos = parseTodos(
15 element("""{"todos":[{"id":"1","content":"A","status":"completed"},{"id":"2","content":"B","status":"in_progress"}]}"""),
16 )
17 assertEquals(2, todos.size)
18 assertEquals("A", todos[0].content)
19 assertEquals(TodoStatus.COMPLETED, todos[0].statusEnum())
20 assertEquals(TodoStatus.IN_PROGRESS, todos[1].statusEnum())
21 }
23 @Test
24 fun parsesTodosFromBareArray() {
25 val todos = parseTodos(element("""[{"content":"only","status":"pending"}]"""))
26 assertEquals(1, todos.size)
27 assertEquals(TodoStatus.PENDING, todos[0].statusEnum())
28 }
30 @Test
31 fun ignoresUnknownKeysAndUnknownStatus() {
32 val todos = parseTodos(element("""{"todos":[{"content":"x","status":"weird","extra":123}]}"""))
33 assertEquals(1, todos.size)
34 assertEquals("x", todos[0].content)
35 assertEquals(TodoStatus.UNKNOWN, todos[0].statusEnum())
36 }
38 @Test
39 fun returnsEmptyForNullAndGarbage() {
40 assertTrue(parseTodos(null).isEmpty())
41 assertTrue(parseTodos(element(""""just a string"""")).isEmpty())
42 assertTrue(parseTodos(element("""{"nope":true}""")).isEmpty())
43 }
45 @Test
46 fun statusEnumNormalizesAliases() {
47 assertEquals(TodoStatus.IN_PROGRESS, TodoItem(status = "in-progress").statusEnum())
48 assertEquals(TodoStatus.COMPLETED, TodoItem(status = "DONE").statusEnum())
49 assertEquals(TodoStatus.CANCELLED, TodoItem(status = "canceled").statusEnum())
50 assertEquals(TodoStatus.PENDING, TodoItem(status = "").statusEnum())
51 }
53 @Test
54 fun currentPlanPicksLatestNonEmptyTodowrite() {
55 val messages = listOf(
56 message("m1", todoPart("p1", """{"todos":[{"content":"old","status":"pending"}]}""")),
57 message("m2", todoPart("p2", """{"todos":[{"content":"new1","status":"completed"},{"content":"new2","status":"pending"}]}""")),
58 message("m3", TextPart(id = "t", text = "just talking")),
59 )
60 val plan = currentTodoPlan(messages)
61 assertEquals(2, plan.size)
62 assertEquals("new1", plan[0].content)
63 }
65 @Test
66 fun currentPlanSkipsEmptyLatestForPriorNonEmpty() {
67 val messages = listOf(
68 message("m1", todoPart("p1", """{"todos":[{"content":"keep","status":"pending"}]}""")),
69 message("m2", todoPart("p2", """{"todos":[]}""")),
70 )
71 val plan = currentTodoPlan(messages)
72 assertEquals(1, plan.size)
73 assertEquals("keep", plan[0].content)
74 }
76 @Test
77 fun currentPlanEmptyWhenNoTodowrite() {
78 val messages = listOf(
79 message("m1", TextPart(id = "t", text = "hi")),
80 message("m2", ToolPart(id = "b", tool = "bash", state = ToolCompleted(output = "done"))),
81 )
82 assertTrue(currentTodoPlan(messages).isEmpty())
83 }
85 private fun message(id: String, vararg parts: Part) =
86 MessageWithParts(AssistantMessage(id = id, sessionID = "s"), parts.toList())
88 private fun todoPart(id: String, inputJson: String): ToolPart =
89 ToolPart(id = id, tool = "todowrite", state = ToolCompleted(input = element(inputJson)))