git.iko.soy/opencode-android
on 017cb9a help sign in
opencode-android/app/src/test/java/soy/iko/opencode/ui/chat/ConversationExportTest.kt
90 lines 3.2 kB history raw
1package soy.iko.opencode.ui.chat
3import org.junit.Assert.assertEquals
4import org.junit.Assert.assertTrue
5import org.junit.Test
6import soy.iko.opencode.data.model.AssistantMessage
7import soy.iko.opencode.data.model.MessageWithParts
8import soy.iko.opencode.data.model.ReasoningPart
9import soy.iko.opencode.data.model.TextPart
10import soy.iko.opencode.data.model.ToolCompleted
11import soy.iko.opencode.data.model.ToolPart
12import soy.iko.opencode.data.model.UserMessage
14class ConversationExportTest {
16 private val sid = "s1"
18 private fun user(id: String, text: String) =
19 MessageWithParts(UserMessage(id, sid), listOf(TextPart(id = "p$id", text = text)))
21 private fun assistant(id: String, text: String) =
22 MessageWithParts(AssistantMessage(id, sid), listOf(TextPart(id = "p$id", text = text)))
24 @Test
25 fun rendersRoleHeadersAndBodies() {
26 val md = buildConversationMarkdown(
27 listOf(user("u1", "Hello"), assistant("a1", "Hi there!")),
28 title = "My chat",
29 )
30 assertTrue(md.startsWith("# My chat"))
31 assertTrue(md.contains("## You\n\nHello"))
32 assertTrue(md.contains("## opencode\n\nHi there!"))
33 }
35 @Test
36 fun omitsTitleWhenBlank() {
37 val md = buildConversationMarkdown(listOf(user("u1", "hi")), title = null)
38 assertTrue(md.startsWith("## You"))
39 }
41 @Test
42 fun skipsMessagesWithoutText() {
43 val onlyReasoning = MessageWithParts(
44 AssistantMessage("a1", sid),
45 listOf(ReasoningPart(id = "r1", text = "hmm")),
46 )
47 val md = buildConversationMarkdown(listOf(onlyReasoning), title = null)
48 // Reasoning is exported in italics, so it still appears.
49 assertTrue(md.contains("## opencode"))
50 assertTrue(md.contains("_hmm_"))
51 }
53 @Test
54 fun emptyConversationProducesJustTheTitle() {
55 // No message bodies to export, but the title header is still emitted.
56 assertEquals("# x", buildConversationMarkdown(emptyList(), title = "x"))
57 }
59 @Test
60 fun includesToolCallSummary() {
61 val assistant = MessageWithParts(
62 AssistantMessage("a1", sid),
63 listOf(
64 TextPart(id = "t1", text = "Done"),
65 ToolPart(id = "tool1", tool = "read", state = ToolCompleted(output = "file contents")),
66 ),
67 )
68 val md = buildConversationMarkdown(listOf(user("u1", "read the file"), assistant), title = null)
69 assertTrue(md.contains("## opencode"))
70 assertTrue("tool name should appear", md.contains("**read**"))
71 assertTrue("tool output should appear", md.contains("file contents"))
72 }
74 @Test
75 fun includesToolCallWithTitle() {
76 val assistant = MessageWithParts(
77 AssistantMessage("a1", sid),
78 listOf(
79 ToolPart(
80 id = "tool1",
81 tool = "edit",
82 state = ToolCompleted(title = "Editing src/main.kt", output = "ok"),
83 ),
84 ),
85 )
86 val md = buildConversationMarkdown(listOf(assistant), title = null)
87 assertTrue(md.contains("**edit**"))
88 assertTrue(md.contains("Editing src/main.kt"))
89 }