1package soy.iko.opencode.data.repo
3import org.junit.Assert.assertEquals
4import org.junit.Assert.assertFalse
5import org.junit.Assert.assertTrue
7import soy.iko.opencode.data.model.AssistantMessage
8import soy.iko.opencode.data.model.MessagePartRemoved
9import soy.iko.opencode.data.model.MessagePartUpdated
10import soy.iko.opencode.data.model.MessageRemoved
11import soy.iko.opencode.data.model.MessageUpdated
12import soy.iko.opencode.data.model.MessageWithParts
13import soy.iko.opencode.data.model.SessionError
14import soy.iko.opencode.data.model.SessionIdle
15import soy.iko.opencode.data.model.TextPart
16import soy.iko.opencode.data.model.UserMessage
18class MessageStoreTest {
20 private val session = "s1"
22 private fun msgUpdated(id: String, sid: String = session) =
23 MessageUpdated(MessageUpdated.Props(AssistantMessage(id = id, sessionID = sid)))
25 private fun partUpdated(messageId: String, partId: String, text: String, sid: String = session) =
27 MessagePartUpdated.Props(
28 part = TextPart(id = partId, messageID = messageId, sessionID = sid, text = text),
30 messageID = messageId,
34 private fun partRemoved(messageId: String, partId: String, sid: String = session) =
36 MessagePartRemoved.Props(sessionID = sid, messageID = messageId, partID = partId),
39 private fun messageRemoved(messageId: String, sid: String = session) =
40 MessageRemoved(MessageRemoved.Props(sessionID = sid, messageID = messageId))
45 fun seedPopulatesSnapshot() {
46 val store = MessageStore()
49 MessageWithParts(UserMessage("u1", session)),
50 MessageWithParts(AssistantMessage("a1", session)),
53 assertEquals(listOf("u1", "a1"), store.snapshot().map { it.info.id })
56 // --- MessageUpdated ---
59 fun messageUpdatedInsertsNewMessage() {
60 val store = MessageStore()
61 val changed = store.reduce(session, msgUpdated("m1"))
63 assertEquals("m1", store.snapshot().single().info.id)
67 fun messageUpdatedOverwritesExistingInfo() {
68 val store = MessageStore()
69 store.reduce(session, msgUpdated("m1"))
70 val changed = store.reduce(session, msgUpdated("m1"))
72 assertEquals(1, store.snapshot().size)
76 fun messageUpdatedIgnoredForOtherSession() {
77 val store = MessageStore()
78 val changed = store.reduce(session, msgUpdated("m1", sid = "other"))
80 assertTrue(store.snapshot().isEmpty())
83 // --- MessagePartUpdated ---
86 fun partUpdatedAppendsToExistingMessage() {
87 val store = MessageStore()
88 store.reduce(session, msgUpdated("m1"))
89 val changed = store.reduce(session, partUpdated("m1", "p1", "hello"))
91 val parts = store.snapshot().single().parts
92 assertEquals(1, parts.size)
93 assertEquals("hello", (parts[0] as TextPart).text)
97 fun partUpdatedCreatesSyntheticMessageWhenAbsent() {
98 val store = MessageStore()
99 store.reduce(session, partUpdated("m1", "p1", "hello"))
100 val msg = store.snapshot().single()
101 assertEquals("m1", msg.info.id)
102 assertEquals("hello", (msg.parts.single() as TextPart).text)
106 fun partUpdatedReplacesPartWithSameId() {
107 val store = MessageStore()
108 store.reduce(session, partUpdated("m1", "p1", "hello"))
109 store.reduce(session, partUpdated("m1", "p1", "hello world"))
110 val parts = store.snapshot().single().parts
111 assertEquals(1, parts.size)
112 assertEquals("hello world", (parts[0] as TextPart).text)
116 fun partUpdatedPreservesInsertionOrderAcrossParts() {
117 val store = MessageStore()
118 store.reduce(session, partUpdated("m1", "p1", "one"))
119 store.reduce(session, partUpdated("m1", "p2", "two"))
120 store.reduce(session, partUpdated("m1", "p3", "three"))
121 val parts = store.snapshot().single().parts
122 assertEquals(listOf("p1", "p2", "p3"), parts.map { it.id })
126 fun partUpdatedIgnoredForOtherSession() {
127 val store = MessageStore()
128 val changed = store.reduce(session, partUpdated("m1", "p1", "x", sid = "other"))
130 assertTrue(store.snapshot().isEmpty())
133 // --- MessagePartRemoved ---
136 fun partRemovedDeletesMatchingPart() {
137 val store = MessageStore()
138 store.reduce(session, partUpdated("m1", "p1", "one"))
139 store.reduce(session, partUpdated("m1", "p2", "two"))
140 val changed = store.reduce(session, partRemoved("m1", "p1"))
142 val parts = store.snapshot().single().parts
143 assertEquals(listOf("p2"), parts.map { it.id })
147 fun partRemovedReturnsFalseWhenPartNotFound() {
148 val store = MessageStore()
149 store.reduce(session, partUpdated("m1", "p1", "one"))
150 val changed = store.reduce(session, partRemoved("m1", "p2"))
154 // --- MessageRemoved ---
157 fun messageRemovedDeletesWholeMessage() {
158 val store = MessageStore()
159 store.reduce(session, msgUpdated("m1"))
160 store.reduce(session, msgUpdated("m2"))
161 val changed = store.reduce(session, messageRemoved("m1"))
163 assertEquals(listOf("m2"), store.snapshot().map { it.info.id })
167 fun messageRemovedReturnsFalseWhenMissing() {
168 val store = MessageStore()
169 val changed = store.reduce(session, messageRemoved("nope"))
173 // --- unrelated events ---
176 fun unrelatedEventsDoNotMutateState() {
177 val store = MessageStore()
178 store.reduce(session, msgUpdated("m1"))
179 val idle = store.reduce(session, SessionIdle(SessionIdle.Props(sessionID = session)))
180 val error = store.reduce(session, SessionError(SessionError.Props(sessionID = session)))
183 assertEquals(listOf("m1"), store.snapshot().map { it.info.id })
186 // --- end-to-end ordering ---
189 fun fullReductionProducesOrderedMessagesWithParts() {
190 val store = MessageStore()
191 store.reduce(session, msgUpdated("u1"))
192 store.reduce(session, msgUpdated("a1"))
193 store.reduce(session, partUpdated("a1", "t1", "draft"))
194 store.reduce(session, partUpdated("a1", "t1", "final"))
195 store.reduce(session, partUpdated("a1", "t2", "second"))
197 val snapshot = store.snapshot()
198 assertEquals(listOf("u1", "a1"), snapshot.map { it.info.id })
200 assertEquals(listOf("t1", "t2"), a1.parts.map { it.id })
201 assertEquals("final", (a1.parts[0] as TextPart).text)
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