@@ -147,4 +147,106 @@ describe("concurrent setState", () => {
147147 expect . arrayContaining ( [ expect . objectContaining ( { _stateID : 3 } ) ] )
148148 ) ;
149149 } ) ;
150+
151+ it ( "should expose lost update when stale write commits last" , async ( ) => {
152+ await Match . create ( match ) ;
153+
154+ const stateA : State = {
155+ ...state ,
156+ ctx : { ...state . ctx , currentPlayer : "102" , turn : 2 } ,
157+ _stateID : 2 ,
158+ } ;
159+ const stateB : State = {
160+ ...state ,
161+ ctx : { ...state . ctx , currentPlayer : "103" , turn : 3 } ,
162+ _stateID : 3 ,
163+ } ;
164+
165+ const logA : LogEntry [ ] = [
166+ {
167+ ...logEntry ,
168+ _stateID : 2 ,
169+ turn : 2 ,
170+ action : {
171+ ...logEntry . action ,
172+ payload : { ...logEntry . action . payload , playerID : "102" } ,
173+ } ,
174+ } ,
175+ ] ;
176+ const logB : LogEntry [ ] = [
177+ {
178+ ...logEntry ,
179+ _stateID : 3 ,
180+ turn : 3 ,
181+ action : {
182+ ...logEntry . action ,
183+ payload : { ...logEntry . action . payload , playerID : "103" } ,
184+ } ,
185+ } ,
186+ ] ;
187+
188+ const originalFindByPk = Match . findByPk . bind ( Match ) ;
189+ const originalUpsert = Match . upsert . bind ( Match ) ;
190+
191+ let findByPkCount = 0 ;
192+ let releaseReads ! : ( ) => void ;
193+ const readsReady = new Promise < void > ( ( resolve ) => {
194+ releaseReads = resolve ;
195+ } ) ;
196+
197+ let releaseStateAWrite ! : ( ) => void ;
198+ const stateAWriteGate = new Promise < void > ( ( resolve ) => {
199+ releaseStateAWrite = resolve ;
200+ } ) ;
201+
202+ const findByPkSpy = jest
203+ . spyOn ( Match , "findByPk" )
204+ . mockImplementation ( async ( ...args : Parameters < typeof Match . findByPk > ) => {
205+ const row = await originalFindByPk ( ...args ) ;
206+ findByPkCount += 1 ;
207+ if ( findByPkCount === 2 ) {
208+ releaseReads ( ) ;
209+ }
210+ await readsReady ;
211+ return row ;
212+ } ) ;
213+
214+ const upsertSpy = jest
215+ . spyOn ( Match , "upsert" )
216+ . mockImplementation ( async ( ...args : Parameters < typeof Match . upsert > ) => {
217+ const values = args [ 0 ] as { state ?: State } ;
218+ const incomingStateID = values . state ?. _stateID ;
219+
220+ if ( incomingStateID === 2 ) {
221+ await stateAWriteGate ;
222+ }
223+
224+ const result = await originalUpsert ( ...args ) ;
225+
226+ if ( incomingStateID === 3 ) {
227+ releaseStateAWrite ( ) ;
228+ }
229+
230+ return result ;
231+ } ) ;
232+
233+ try {
234+ await Promise . all ( [
235+ testStore . db . setState ( match . id ! , stateA , logA ) ,
236+ testStore . db . setState ( match . id ! , stateB , logB ) ,
237+ ] ) ;
238+ } finally {
239+ findByPkSpy . mockRestore ( ) ;
240+ upsertSpy . mockRestore ( ) ;
241+ }
242+
243+ const result = await testStore . db . fetch ( match . id ! , {
244+ state : true ,
245+ log : true ,
246+ } ) ;
247+
248+ // Correct behavior would keep the highest _stateID even if stale write runs last.
249+ // This expectation is intentionally red until setState is fixed.
250+ expect ( result . state ! . _stateID ) . toBe ( 3 ) ;
251+ } ) ;
150252} ) ;
0 commit comments