Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
3fd9ac767f
commit
ceb4813218
9 files changed
+443
-17
No files matched your search
@@ -6,6 +6,7 @@ import io.mockk.unmockkAll
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@@ -700,4 +701,49 @@ class EventParserTest {
|
||||
assertEquals("terminal", toolEvent.name)
|
||||
assertEquals("sess-replay-2", toolEvent.sessionId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseTodoUpdated_returnsTodoUpdatedEvent() {
|
||||
val response =
|
||||
createJsonRpcResponse(
|
||||
jsonrpc = "2.0",
|
||||
id = null,
|
||||
result = null,
|
||||
error = null,
|
||||
method = "event",
|
||||
params =
|
||||
mapOf(
|
||||
"type" to "todo.updated",
|
||||
"session_id" to "sess-abc",
|
||||
"payload" to
|
||||
mapOf(
|
||||
"revision" to 5,
|
||||
"todos" to
|
||||
listOf(
|
||||
mapOf("id" to "1", "content" to "Parent task", "status" to "in_progress"),
|
||||
mapOf(
|
||||
"id" to "2",
|
||||
"content" to "Sub task",
|
||||
"status" to "pending",
|
||||
"parent" to "1",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val event = EventParser.parse(response)
|
||||
assertTrue(event is WsEvent.TodoUpdated)
|
||||
val todoUpdated = event as WsEvent.TodoUpdated
|
||||
assertEquals("sess-abc", todoUpdated.sessionId)
|
||||
assertEquals(5, todoUpdated.revision)
|
||||
assertEquals(2, todoUpdated.todos.size)
|
||||
assertEquals("1", todoUpdated.todos[0].id)
|
||||
assertEquals("Parent task", todoUpdated.todos[0].content)
|
||||
assertEquals(null, todoUpdated.todos[0].parent)
|
||||
assertFalse(todoUpdated.todos[0].isSubtask)
|
||||
assertEquals("2", todoUpdated.todos[1].id)
|
||||
assertEquals("Sub task", todoUpdated.todos[1].content)
|
||||
assertEquals("1", todoUpdated.todos[1].parent)
|
||||
assertTrue(todoUpdated.todos[1].isSubtask)
|
||||
}
|
||||
}
|
||||
@@ -979,4 +979,88 @@ class ChatWsEventReducerTest {
|
||||
assertEquals(1, result.state.compressionCount)
|
||||
assertEquals(null, result.state.usedContextTokens)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTodoUpdated_updatesStateTodos() {
|
||||
val state = ChatUiState(currentSessionId = "session-1")
|
||||
val todos =
|
||||
listOf(
|
||||
TodoItem(id = "1", content = "Main task", status = "in_progress"),
|
||||
TodoItem(id = "2", content = "Subtask 1", status = "completed", parent = "1"),
|
||||
TodoItem(id = "3", content = "Subtask 2", status = "pending", parent = "1"),
|
||||
)
|
||||
val event = WsEvent.TodoUpdated(todos = todos, revision = 1, sessionId = "session-1")
|
||||
|
||||
val result =
|
||||
ChatWsEventReducer.reduce(
|
||||
state = state,
|
||||
streamingState = StreamingState(),
|
||||
event = event,
|
||||
currentSessionId = "session-1",
|
||||
)
|
||||
|
||||
assertEquals(3, result.state.todos.size)
|
||||
assertEquals("Main task", result.state.todos[0].content)
|
||||
assertEquals(null, result.state.todos[0].parent)
|
||||
assertEquals("1", result.state.todos[1].parent)
|
||||
assertTrue(result.state.todos[1].isCompleted)
|
||||
assertTrue(result.state.todos[1].isSubtask)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTodoUpdated_differentSession_isIgnored() {
|
||||
val state =
|
||||
ChatUiState(
|
||||
currentSessionId = "session-1",
|
||||
todos = listOf(TodoItem(id = "orig", content = "Original", status = "pending")),
|
||||
)
|
||||
val event =
|
||||
WsEvent.TodoUpdated(
|
||||
todos = listOf(TodoItem(id = "new", content = "New", status = "in_progress")),
|
||||
revision = 1,
|
||||
sessionId = "session-other",
|
||||
)
|
||||
|
||||
val result =
|
||||
ChatWsEventReducer.reduce(
|
||||
state = state,
|
||||
streamingState = StreamingState(),
|
||||
event = event,
|
||||
currentSessionId = "session-1",
|
||||
)
|
||||
|
||||
assertEquals(1, result.state.todos.size)
|
||||
assertEquals("Original", result.state.todos[0].content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExtractTodosFromMap_preservesParentHierarchy() {
|
||||
val payload =
|
||||
mapOf(
|
||||
"todos" to
|
||||
listOf(
|
||||
mapOf("id" to "t1", "content" to "Root", "status" to "in_progress"),
|
||||
mapOf("id" to "t2", "content" to "Child", "status" to "pending", "parent" to "t1"),
|
||||
),
|
||||
)
|
||||
val extracted = extractTodosFromMap(payload)
|
||||
assertEquals(2, extracted?.size)
|
||||
assertEquals("t1", extracted?.get(0)?.id)
|
||||
assertEquals(null, extracted?.get(0)?.parent)
|
||||
assertEquals("t2", extracted?.get(1)?.id)
|
||||
assertEquals("t1", extracted?.get(1)?.parent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExtractTodosFromJson_preservesParentHierarchy() {
|
||||
val json =
|
||||
"""{"todos":[{"id":"a","content":"Task A","status":"done"},""" +
|
||||
"""{"id":"b","content":"Task B","status":"pending","parent":"a"}]}"""
|
||||
val extracted = extractTodosFromJson(json)
|
||||
assertEquals(2, extracted?.size)
|
||||
assertEquals("a", extracted?.get(0)?.id)
|
||||
assertEquals(null, extracted?.get(0)?.parent)
|
||||
assertEquals("b", extracted?.get(1)?.id)
|
||||
assertEquals("a", extracted?.get(1)?.parent)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.m57.hermescontrol.ui.chat
|
||||
|
||||
import com.m57.hermescontrol.ui.chat.components.buildTodoTree
|
||||
import com.m57.hermescontrol.ui.chat.components.computeChipDisplay
|
||||
import com.m57.hermescontrol.ui.chat.components.flattenTodoTree
|
||||
import com.m57.hermescontrol.ui.chat.components.getAllDescendantTodos
|
||||
import com.m57.hermescontrol.ui.chat.components.shouldShowProgressChip
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
@@ -142,4 +145,72 @@ class TaskProgressChipTest {
|
||||
)
|
||||
assertTrue(hydrateTodosFromMessages(messages).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `buildTodoTree constructs correct multi-level hierarchy`() {
|
||||
val todos =
|
||||
listOf(
|
||||
TodoItem(id = "1", content = "Root 1", status = "in_progress"),
|
||||
TodoItem(id = "1.1", content = "Child 1.1", status = "completed", parent = "1"),
|
||||
TodoItem(id = "1.2", content = "Child 1.2", status = "pending", parent = "1"),
|
||||
TodoItem(id = "1.2.1", content = "Grandchild 1.2.1", status = "pending", parent = "1.2"),
|
||||
TodoItem(id = "2", content = "Root 2", status = "pending"),
|
||||
)
|
||||
|
||||
val tree = buildTodoTree(todos)
|
||||
assertEquals(2, tree.size)
|
||||
assertEquals("1", tree[0].item.id)
|
||||
assertEquals(0, tree[0].depth)
|
||||
assertEquals(2, tree[0].children.size)
|
||||
assertEquals("1.1", tree[0].children[0].item.id)
|
||||
assertEquals(1, tree[0].children[0].depth)
|
||||
assertEquals("1.2", tree[0].children[1].item.id)
|
||||
assertEquals(1, tree[0].children[1].depth)
|
||||
assertEquals(1, tree[0].children[1].children.size)
|
||||
assertEquals(
|
||||
"1.2.1",
|
||||
tree[0]
|
||||
.children[1]
|
||||
.children[0]
|
||||
.item.id,
|
||||
)
|
||||
assertEquals(2, tree[0].children[1].children[0].depth)
|
||||
assertEquals("2", tree[1].item.id)
|
||||
assertEquals(0, tree[1].depth)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `flattenTodoTree collapses and expands subtasks correctly`() {
|
||||
val todos =
|
||||
listOf(
|
||||
TodoItem(id = "1", content = "Root 1", status = "in_progress"),
|
||||
TodoItem(id = "1.1", content = "Child 1.1", status = "completed", parent = "1"),
|
||||
TodoItem(id = "1.2", content = "Child 1.2", status = "pending", parent = "1"),
|
||||
TodoItem(id = "2", content = "Root 2", status = "pending"),
|
||||
)
|
||||
val tree = buildTodoTree(todos)
|
||||
|
||||
// Expanded state
|
||||
val expandedRows = flattenTodoTree(tree, collapsedParentIds = emptySet())
|
||||
assertEquals(4, expandedRows.size)
|
||||
assertEquals("1", expandedRows[0].todo.id)
|
||||
assertTrue(expandedRows[0].hasChildren)
|
||||
assertTrue(expandedRows[0].isExpanded)
|
||||
assertEquals(1, expandedRows[0].completedSubtasksCount)
|
||||
assertEquals(2, expandedRows[0].totalSubtasksCount)
|
||||
assertEquals("1.1", expandedRows[1].todo.id)
|
||||
assertEquals(1, expandedRows[1].depth)
|
||||
assertEquals("1.2", expandedRows[2].todo.id)
|
||||
assertEquals(1, expandedRows[2].depth)
|
||||
assertEquals("2", expandedRows[3].todo.id)
|
||||
assertEquals(0, expandedRows[3].depth)
|
||||
|
||||
// Collapsed state
|
||||
val collapsedRows = flattenTodoTree(tree, collapsedParentIds = setOf("1"))
|
||||
assertEquals(2, collapsedRows.size)
|
||||
assertEquals("1", collapsedRows[0].todo.id)
|
||||
assertTrue(collapsedRows[0].hasChildren)
|
||||
assertFalse(collapsedRows[0].isExpanded)
|
||||
assertEquals("2", collapsedRows[1].todo.id)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user