1package soy.iko.opencode.ui.chat
3import org.junit.Assert.assertEquals
4import org.junit.Assert.assertTrue
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.UserMessage
12class ConversationExportTest {
14 private val sid = "s1"
16 private fun user(id: String, text: String) =
17 MessageWithParts(UserMessage(id, sid), listOf(TextPart(id = "p$id", text = text)))
19 private fun assistant(id: String, text: String) =
20 MessageWithParts(AssistantMessage(id, sid), listOf(TextPart(id = "p$id", text = text)))
23 fun rendersRoleHeadersAndBodies() {
24 val md = buildConversationMarkdown(
25 listOf(user("u1", "Hello"), assistant("a1", "Hi there!")),
28 assertTrue(md.startsWith("# My chat"))
29 assertTrue(md.contains("## You\n\nHello"))
30 assertTrue(md.contains("## opencode\n\nHi there!"))
34 fun omitsTitleWhenBlank() {
35 val md = buildConversationMarkdown(listOf(user("u1", "hi")), title = null)
36 assertTrue(md.startsWith("## You"))
40 fun skipsMessagesWithoutText() {
41 val onlyReasoning = MessageWithParts(
42 AssistantMessage("a1", sid),
43 listOf(ReasoningPart(id = "r1", text = "hmm")),
45 val md = buildConversationMarkdown(listOf(onlyReasoning), title = null)
46 // Reasoning is exported in italics, so it still appears.
47 assertTrue(md.contains("## opencode"))
48 assertTrue(md.contains("_hmm_"))
52 fun emptyConversationProducesJustTheTitle() {
53 // No message bodies to export, but the title header is still emitted.
54 assertEquals("# x", buildConversationMarkdown(emptyList(), title = "x"))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list