@@ -13,6 +13,20 @@ function wrapperFor(store: ReturnType<typeof createStore>) {
1313 } ;
1414}
1515
16+ function createDeferred < T > ( ) {
17+ let resolve ! : ( value : T ) => void ;
18+ let reject ! : ( error : unknown ) => void ;
19+ const promise = new Promise < T > ( ( nextResolve , nextReject ) => {
20+ resolve = nextResolve ;
21+ reject = nextReject ;
22+ } ) ;
23+
24+ return {
25+ promise,
26+ resolve,
27+ reject,
28+ } ;
29+ }
1630describe ( "usePersistWorkspaceLastViewedTarget" , ( ) => {
1731 it ( "does not suppress a retry for the same target after a failed write" , async ( ) => {
1832 const store = createStore ( ) ;
@@ -60,4 +74,94 @@ describe("usePersistWorkspaceLastViewedTarget", () => {
6074 undefined
6175 ) ;
6276 } ) ;
77+ it ( "does not roll back a newer target when an older write fails out of order" , async ( ) => {
78+ const store = createStore ( ) ;
79+ const firstWrite = createDeferred < { workspaceId : string ; updatedAt : number } > ( ) ;
80+ const secondWrite = createDeferred < { workspaceId : string ; updatedAt : number } > ( ) ;
81+ const sendCommand = vi
82+ . fn ( )
83+ . mockImplementationOnce ( ( ) => firstWrite . promise )
84+ . mockImplementationOnce ( ( ) => secondWrite . promise ) ;
85+
86+ store . set ( wsClientAtom , {
87+ sendCommand,
88+ subscribe : vi . fn ( ( ) => ( ) => { } ) ,
89+ } as never ) ;
90+ store . set ( lastViewedTargetAtom , null ) ;
91+
92+ const { result } = renderHook ( ( ) => usePersistWorkspaceLastViewedTarget ( ) , {
93+ wrapper : wrapperFor ( store ) ,
94+ } ) ;
95+
96+ let firstRequest ! : Promise < unknown > ;
97+ let secondRequest ! : Promise < unknown > ;
98+
99+ act ( ( ) => {
100+ firstRequest = result . current ( { workspaceId : "ws-1" } ) ;
101+ secondRequest = result . current ( { workspaceId : "ws-2" } ) ;
102+ } ) ;
103+
104+ expect ( store . get ( lastViewedTargetAtom ) ?. workspaceId ) . toBe ( "ws-2" ) ;
105+
106+ await act ( async ( ) => {
107+ secondWrite . resolve ( { workspaceId : "ws-2" , updatedAt : 22 } ) ;
108+ await secondRequest ;
109+ } ) ;
110+
111+ expect ( store . get ( lastViewedTargetAtom ) ?. workspaceId ) . toBe ( "ws-2" ) ;
112+
113+ await act ( async ( ) => {
114+ firstWrite . reject (
115+ new CommandResultError ( {
116+ code : "write_failed" ,
117+ message : "failed" ,
118+ } )
119+ ) ;
120+ await firstRequest ;
121+ } ) ;
122+
123+ expect ( store . get ( lastViewedTargetAtom ) ?. workspaceId ) . toBe ( "ws-2" ) ;
124+ } ) ;
125+
126+ it ( "does not overwrite a newer target when an older write succeeds out of order" , async ( ) => {
127+ const store = createStore ( ) ;
128+ const firstWrite = createDeferred < { workspaceId : string ; updatedAt : number } > ( ) ;
129+ const secondWrite = createDeferred < { workspaceId : string ; updatedAt : number } > ( ) ;
130+ const sendCommand = vi
131+ . fn ( )
132+ . mockImplementationOnce ( ( ) => firstWrite . promise )
133+ . mockImplementationOnce ( ( ) => secondWrite . promise ) ;
134+
135+ store . set ( wsClientAtom , {
136+ sendCommand,
137+ subscribe : vi . fn ( ( ) => ( ) => { } ) ,
138+ } as never ) ;
139+ store . set ( lastViewedTargetAtom , null ) ;
140+
141+ const { result } = renderHook ( ( ) => usePersistWorkspaceLastViewedTarget ( ) , {
142+ wrapper : wrapperFor ( store ) ,
143+ } ) ;
144+
145+ let firstRequest ! : Promise < unknown > ;
146+ let secondRequest ! : Promise < unknown > ;
147+
148+ act ( ( ) => {
149+ firstRequest = result . current ( { workspaceId : "ws-1" } ) ;
150+ secondRequest = result . current ( { workspaceId : "ws-2" } ) ;
151+ } ) ;
152+
153+ await act ( async ( ) => {
154+ secondWrite . resolve ( { workspaceId : "ws-2" , updatedAt : 22 } ) ;
155+ await secondRequest ;
156+ } ) ;
157+
158+ expect ( store . get ( lastViewedTargetAtom ) ?. workspaceId ) . toBe ( "ws-2" ) ;
159+
160+ await act ( async ( ) => {
161+ firstWrite . resolve ( { workspaceId : "ws-1" , updatedAt : 11 } ) ;
162+ await firstRequest ;
163+ } ) ;
164+
165+ expect ( store . get ( lastViewedTargetAtom ) ?. workspaceId ) . toBe ( "ws-2" ) ;
166+ } ) ;
63167} ) ;
0 commit comments