@@ -143,20 +143,58 @@ describe("SessionGoal", () => {
143143 } ) ,
144144 )
145145
146- it . effect ( "todo.updated replaces the whole list and replay is a no-op" , ( ) =>
146+ it . effect ( "todo.updated replaces the whole list and a replayed event is a no-op" , ( ) =>
147147 Effect . gen ( function * ( ) {
148148 const { db, bus } = yield * setup
149- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 1 , todos } )
149+ yield * bus . publish ( SessionEvent . Goal . Updated , { sessionID, snapshot : snapshot ( 1 ) } )
150+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos } )
150151 expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
151152 { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
152153 { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
153154 ] )
154155
155- yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 1 , todos } )
156+ // The same revision arrives twice; only the first one advances the goal.
157+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos : [ todos [ 0 ] ] } )
156158 expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toHaveLength ( 2 )
157159 } ) ,
158160 )
159161
162+ it . effect ( "todo.updated never rewinds the goal revision, so a stale verdict stays rejected" , ( ) =>
163+ Effect . gen ( function * ( ) {
164+ const { db, bus } = yield * setup
165+ yield * bus . publish ( SessionEvent . Goal . Updated , { sessionID, snapshot : snapshot ( 1 ) } )
166+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos } )
167+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 3 , todos } )
168+
169+ // A late list event carrying an older revision must not move the goal back to a
170+ // number a verdict judged against the previous list still carries.
171+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 2 , todos : [ todos [ 0 ] ] } )
172+ expect ( yield * SessionGoal . read ( db , sessionID ) ) . toMatchObject ( { revision : 3 } )
173+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toHaveLength ( 2 )
174+
175+ yield * bus . publish ( SessionEvent . Todo . Verified , {
176+ sessionID,
177+ revision : 2 ,
178+ items : [ { position : 0 , status : "completed" , feedback : "stale" } ] ,
179+ } )
180+ expect ( ( yield * rounds ( db ) ) ?. rounds ) . toBe ( 0 )
181+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
182+ { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
183+ { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
184+ ] )
185+ } ) ,
186+ )
187+
188+ it . effect ( "todo.updated without a goal row is dropped" , ( ) =>
189+ Effect . gen ( function * ( ) {
190+ const { db, bus } = yield * setup
191+ // A list only exists under a goal, so an event without one leaves no state.
192+ yield * bus . publish ( SessionEvent . Todo . Updated , { sessionID, revision : 1 , todos } )
193+ expect ( yield * SessionGoal . read ( db , sessionID ) ) . toBeUndefined ( )
194+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [ ] )
195+ } ) ,
196+ )
197+
160198 it . effect ( "todo.verified applies a fresh verdict" , ( ) =>
161199 Effect . gen ( function * ( ) {
162200 const { db, bus } = yield * setup
@@ -210,10 +248,7 @@ describe("SessionGoal", () => {
210248 items : [ { position : 0 , status : "completed" , feedback : "ok" } ] ,
211249 } )
212250 expect ( yield * SessionGoal . read ( db , sessionID ) ) . toBeUndefined ( )
213- expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [
214- { position : 0 , content : "first" , status : "pending" , acceptance : "tests pass" , feedback : undefined } ,
215- { position : 1 , content : "second" , status : "in_progress" , acceptance : undefined , feedback : undefined } ,
216- ] )
251+ expect ( yield * SessionGoal . readTodo ( db , sessionID ) ) . toEqual ( [ ] )
217252 } ) ,
218253 )
219254} )
0 commit comments