@@ -153,4 +153,162 @@ describe('FlowChatManager initialization', () => {
153153 expect ( storeMocks . store . switchSession ) . toHaveBeenCalledTimes ( 1 ) ;
154154 expect ( storeMocks . store . switchSession ) . toHaveBeenCalledWith ( 'history-1' ) ;
155155 } ) ;
156+
157+ it ( 'does not overwrite a user-selected workspace session after initial history restore completes' , async ( ) => {
158+ const historyRestore = createDeferred < void > ( ) ;
159+ const sessions = new Map < string , any > ( [
160+ [ 'history-1' , createHistoricalSession ( {
161+ sessionId : 'history-1' ,
162+ title : 'Newest history' ,
163+ lastActiveAt : 30 ,
164+ lastFinishedAt : 30 ,
165+ } ) ] ,
166+ [ 'history-2' , createHistoricalSession ( {
167+ sessionId : 'history-2' ,
168+ title : 'User selected history' ,
169+ lastActiveAt : 10 ,
170+ lastFinishedAt : 10 ,
171+ } ) ] ,
172+ ] ) ;
173+ let activeSessionId : string | null = null ;
174+
175+ storeMocks . store = {
176+ registerPersistUnreadCompletionCallback : vi . fn ( ) ,
177+ loadSessionMetadataPage : vi . fn ( async ( ) => ( {
178+ sessions : [ ] ,
179+ totalTopLevelCount : 2 ,
180+ hasMore : false ,
181+ } ) ) ,
182+ getState : vi . fn ( ( ) => ( {
183+ sessions,
184+ activeSessionId,
185+ } ) ) ,
186+ loadSessionHistory : vi . fn ( ( ) => historyRestore . promise ) ,
187+ switchSession : vi . fn ( ( sessionId : string ) => {
188+ activeSessionId = sessionId ;
189+ } ) ,
190+ } ;
191+
192+ const manager = FlowChatManager . getInstance ( ) ;
193+ const initialize = manager . initialize ( 'D:/workspace/BitFun' ) ;
194+
195+ await flushAsyncWork ( ) ;
196+ expect ( storeMocks . store . loadSessionHistory ) . toHaveBeenCalledWith (
197+ 'history-1' ,
198+ 'D:/workspace/BitFun' ,
199+ undefined ,
200+ undefined ,
201+ undefined ,
202+ { deferFullHistoryUntilActive : true } ,
203+ ) ;
204+
205+ activeSessionId = 'history-2' ;
206+ historyRestore . resolve ( ) ;
207+
208+ await expect ( initialize ) . resolves . toBe ( true ) ;
209+
210+ expect ( storeMocks . store . switchSession ) . not . toHaveBeenCalled ( ) ;
211+ expect ( activeSessionId ) . toBe ( 'history-2' ) ;
212+ } ) ;
213+
214+ it ( 'does not let a stale workspace initialization overwrite a newer active workspace' , async ( ) => {
215+ const historyRestore = createDeferred < void > ( ) ;
216+ const sessions = new Map < string , any > ( [
217+ [ 'history-1' , createHistoricalSession ( ) ] ,
218+ [ 'other-1' , createHistoricalSession ( {
219+ sessionId : 'other-1' ,
220+ title : 'Other workspace' ,
221+ workspacePath : 'D:/workspace/Other' ,
222+ lastActiveAt : 40 ,
223+ lastFinishedAt : 40 ,
224+ } ) ] ,
225+ ] ) ;
226+ let activeSessionId : string | null = null ;
227+
228+ storeMocks . store = {
229+ registerPersistUnreadCompletionCallback : vi . fn ( ) ,
230+ loadSessionMetadataPage : vi . fn ( async ( ) => ( {
231+ sessions : [ ] ,
232+ totalTopLevelCount : 1 ,
233+ hasMore : false ,
234+ } ) ) ,
235+ getState : vi . fn ( ( ) => ( {
236+ sessions,
237+ activeSessionId,
238+ } ) ) ,
239+ loadSessionHistory : vi . fn ( ( ) => historyRestore . promise ) ,
240+ switchSession : vi . fn ( ( sessionId : string ) => {
241+ activeSessionId = sessionId ;
242+ } ) ,
243+ } ;
244+
245+ const manager = FlowChatManager . getInstance ( ) ;
246+ const initialize = manager . initialize ( 'D:/workspace/BitFun' ) ;
247+
248+ await flushAsyncWork ( ) ;
249+ activeSessionId = 'other-1' ;
250+ ( manager as unknown as { context : { currentWorkspacePath : string | null } } )
251+ . context . currentWorkspacePath = 'D:/workspace/Other' ;
252+ historyRestore . resolve ( ) ;
253+
254+ await expect ( initialize ) . resolves . toBe ( true ) ;
255+
256+ expect ( storeMocks . store . switchSession ) . not . toHaveBeenCalled ( ) ;
257+ expect ( activeSessionId ) . toBe ( 'other-1' ) ;
258+ expect ( ( manager as unknown as { context : { currentWorkspacePath : string | null } } )
259+ . context . currentWorkspacePath ) . toBe ( 'D:/workspace/Other' ) ;
260+ } ) ;
261+
262+ it ( 'does not let an older workspace initialization switch after a newer workspace initialize starts' , async ( ) => {
263+ const bitfunHistoryRestore = createDeferred < void > ( ) ;
264+ const sessions = new Map < string , any > ( [
265+ [ 'history-1' , createHistoricalSession ( ) ] ,
266+ [ 'other-1' , createHistoricalSession ( {
267+ sessionId : 'other-1' ,
268+ title : 'Other workspace' ,
269+ workspacePath : 'D:/workspace/Other' ,
270+ lastActiveAt : 40 ,
271+ lastFinishedAt : 40 ,
272+ } ) ] ,
273+ ] ) ;
274+ let activeSessionId : string | null = null ;
275+
276+ storeMocks . store = {
277+ registerPersistUnreadCompletionCallback : vi . fn ( ) ,
278+ loadSessionMetadataPage : vi . fn ( async (
279+ workspacePath : string ,
280+ ) => ( {
281+ sessions : [ ] ,
282+ totalTopLevelCount : workspacePath === 'D:/workspace/BitFun' ? 1 : 0 ,
283+ hasMore : false ,
284+ } ) ) ,
285+ getState : vi . fn ( ( ) => ( {
286+ sessions,
287+ activeSessionId,
288+ } ) ) ,
289+ loadSessionHistory : vi . fn ( ( sessionId : string ) => {
290+ if ( sessionId === 'history-1' ) {
291+ return bitfunHistoryRestore . promise ;
292+ }
293+ return Promise . resolve ( ) ;
294+ } ) ,
295+ switchSession : vi . fn ( ( sessionId : string ) => {
296+ activeSessionId = sessionId ;
297+ } ) ,
298+ } ;
299+
300+ const manager = FlowChatManager . getInstance ( ) ;
301+ const bitfunInitialize = manager . initialize ( 'D:/workspace/BitFun' ) ;
302+
303+ await flushAsyncWork ( ) ;
304+ await expect ( manager . initialize ( 'D:/workspace/Other' ) ) . resolves . toBe ( true ) ;
305+
306+ bitfunHistoryRestore . resolve ( ) ;
307+ await expect ( bitfunInitialize ) . resolves . toBe ( true ) ;
308+
309+ expect ( storeMocks . store . switchSession ) . toHaveBeenCalledWith ( 'other-1' ) ;
310+ expect ( storeMocks . store . switchSession ) . not . toHaveBeenCalledWith ( 'history-1' ) ;
311+ expect ( ( manager as unknown as { context : { currentWorkspacePath : string | null } } )
312+ . context . currentWorkspacePath ) . toBe ( 'D:/workspace/Other' ) ;
313+ } ) ;
156314} ) ;
0 commit comments