11import { vi , describe , it , expect , beforeEach , afterEach } from 'vitest' ;
22import { mkdtempSync , rmSync , mkdirSync } from 'fs' ;
33import { tmpdir } from 'os' ;
4- import { join } from 'path' ;
4+ import { join , resolve } from 'path' ;
5+
6+ /** Resolve a path for cross-platform compatibility (Windows resolves /tmp to D:\tmp) */
7+ const p = ( filePath : string ) => resolve ( filePath ) ;
58
69// ─── Shared test infrastructure ─────────────────────────────────────────────
710
@@ -304,7 +307,7 @@ describe('attachFile', () => {
304307 attachFile ( { threadId : 'store-path' , filePath : '/tmp/test/component.ts' } ) ;
305308 const index = loadIndex ( ) ;
306309
307- expect ( index [ 'store-path' ] . linked_files ) . toContain ( '/tmp/test/component.ts' ) ;
310+ expect ( index [ 'store-path' ] . linked_files ) . toContain ( p ( '/tmp/test/component.ts' ) ) ;
308311 } ) ;
309312
310313 it ( 'returns the filePath in data' , async ( ) => {
@@ -313,7 +316,7 @@ describe('attachFile', () => {
313316
314317 const result = attachFile ( { threadId : 'data-path' , filePath : '/tmp/test/api.ts' } ) ;
315318
316- expect ( result . data ?. filePath ) . toBe ( '/tmp/test/api.ts' ) ;
319+ expect ( result . data ?. filePath ) . toBe ( p ( '/tmp/test/api.ts' ) ) ;
317320 } ) ;
318321
319322 it ( 'returns success:false with THREAD_NOT_FOUND when thread does not exist' , async ( ) => {
@@ -335,7 +338,7 @@ describe('attachFile', () => {
335338
336339 it ( 'is idempotent: returns success when file is already linked' , async ( ) => {
337340 await seedIndex ( {
338- 'idempotent-thread' : makeThread ( { linked_files : [ '/tmp/test/already.ts' ] } ) ,
341+ 'idempotent-thread' : makeThread ( { linked_files : [ p ( '/tmp/test/already.ts' ) ] } ) ,
339342 } ) ;
340343 const { attachFile } = await import ( '../src/core/operations/attach.js' ) ;
341344
@@ -346,7 +349,7 @@ describe('attachFile', () => {
346349
347350 it ( 'returns alreadyLinked:true when file is already linked' , async ( ) => {
348351 await seedIndex ( {
349- 'already-linked' : makeThread ( { linked_files : [ '/tmp/test/linked.ts' ] } ) ,
352+ 'already-linked' : makeThread ( { linked_files : [ p ( '/tmp/test/linked.ts' ) ] } ) ,
350353 } ) ;
351354 const { attachFile } = await import ( '../src/core/operations/attach.js' ) ;
352355
@@ -357,14 +360,14 @@ describe('attachFile', () => {
357360
358361 it ( 'does not duplicate a file that is already linked' , async ( ) => {
359362 await seedIndex ( {
360- 'no-dup' : makeThread ( { linked_files : [ '/tmp/test/once.ts' ] } ) ,
363+ 'no-dup' : makeThread ( { linked_files : [ p ( '/tmp/test/once.ts' ) ] } ) ,
361364 } ) ;
362365 const { attachFile } = await import ( '../src/core/operations/attach.js' ) ;
363366 const { loadIndex } = await import ( '../src/core/storage.js' ) ;
364367
365368 attachFile ( { threadId : 'no-dup' , filePath : '/tmp/test/once.ts' } ) ;
366369 const index = loadIndex ( ) ;
367- const occurrences = index [ 'no-dup' ] . linked_files . filter ( ( f ) => f === '/tmp/test/once.ts' ) ;
370+ const occurrences = index [ 'no-dup' ] . linked_files . filter ( ( f ) => f === p ( '/tmp/test/once.ts' ) ) ;
368371
369372 expect ( occurrences ) . toHaveLength ( 1 ) ;
370373 } ) ;
@@ -376,15 +379,15 @@ describe('attachFile', () => {
376379 const now = new Date ( ) . toISOString ( ) ;
377380 savePending ( {
378381 tracked : [
379- { path : '/tmp/test/tracked.ts' , first_seen : now , last_modified : now , count : 1 } ,
382+ { path : p ( '/tmp/test/tracked.ts' ) , first_seen : now , last_modified : now , count : 1 } ,
380383 ] ,
381384 } ) ;
382385
383386 const { attachFile } = await import ( '../src/core/operations/attach.js' ) ;
384387 attachFile ( { threadId : 'pending-thread' , filePath : '/tmp/test/tracked.ts' } ) ;
385388
386389 const pending = loadPending ( ) ;
387- const stillTracked = pending . tracked . some ( ( f ) => f . path === '/tmp/test/tracked.ts' ) ;
390+ const stillTracked = pending . tracked . some ( ( f ) => f . path === p ( '/tmp/test/tracked.ts' ) ) ;
388391 expect ( stillTracked ) . toBe ( false ) ;
389392 } ) ;
390393
@@ -404,7 +407,7 @@ describe('attachFile', () => {
404407describe ( 'detachFile' , ( ) => {
405408 it ( 'successfully detaches a linked file' , async ( ) => {
406409 await seedIndex ( {
407- 'detach-thread' : makeThread ( { linked_files : [ '/tmp/test/remove-me.ts' ] } ) ,
410+ 'detach-thread' : makeThread ( { linked_files : [ p ( '/tmp/test/remove-me.ts' ) ] } ) ,
408411 } ) ;
409412 const { detachFile } = await import ( '../src/core/operations/attach.js' ) ;
410413
@@ -415,15 +418,15 @@ describe('detachFile', () => {
415418
416419 it ( 'removes the file from linked_files in storage' , async ( ) => {
417420 await seedIndex ( {
418- 'storage-detach' : makeThread ( { linked_files : [ '/tmp/test/gone.ts' ] } ) ,
421+ 'storage-detach' : makeThread ( { linked_files : [ p ( '/tmp/test/gone.ts' ) ] } ) ,
419422 } ) ;
420423 const { detachFile } = await import ( '../src/core/operations/attach.js' ) ;
421424 const { loadIndex } = await import ( '../src/core/storage.js' ) ;
422425
423426 detachFile ( { threadId : 'storage-detach' , filePath : '/tmp/test/gone.ts' } ) ;
424427 const index = loadIndex ( ) ;
425428
426- expect ( index [ 'storage-detach' ] . linked_files ) . not . toContain ( '/tmp/test/gone.ts' ) ;
429+ expect ( index [ 'storage-detach' ] . linked_files ) . not . toContain ( p ( '/tmp/test/gone.ts' ) ) ;
427430 } ) ;
428431
429432 it ( 'returns success:false with THREAD_NOT_FOUND when thread does not exist' , async ( ) => {
@@ -451,21 +454,21 @@ describe('detachFile', () => {
451454
452455 const result = detachFile ( { threadId : 'msg-thread' , filePath : '/tmp/test/stranger.ts' } ) ;
453456
454- expect ( result . message ) . toContain ( '/tmp/test/stranger.ts' ) ;
457+ expect ( result . message ) . toContain ( p ( '/tmp/test/stranger.ts' ) ) ;
455458 } ) ;
456459
457460 it ( 'leaves other linked files untouched when detaching one' , async ( ) => {
458461 await seedIndex ( {
459- 'multi-file' : makeThread ( { linked_files : [ '/tmp/test/keep.ts' , '/tmp/test/remove.ts' ] } ) ,
462+ 'multi-file' : makeThread ( { linked_files : [ p ( '/tmp/test/keep.ts' ) , p ( '/tmp/test/remove.ts' ) ] } ) ,
460463 } ) ;
461464 const { detachFile } = await import ( '../src/core/operations/attach.js' ) ;
462465 const { loadIndex } = await import ( '../src/core/storage.js' ) ;
463466
464467 detachFile ( { threadId : 'multi-file' , filePath : '/tmp/test/remove.ts' } ) ;
465468 const index = loadIndex ( ) ;
466469
467- expect ( index [ 'multi-file' ] . linked_files ) . toContain ( '/tmp/test/keep.ts' ) ;
468- expect ( index [ 'multi-file' ] . linked_files ) . not . toContain ( '/tmp/test/remove.ts' ) ;
470+ expect ( index [ 'multi-file' ] . linked_files ) . toContain ( p ( '/tmp/test/keep.ts' ) ) ;
471+ expect ( index [ 'multi-file' ] . linked_files ) . not . toContain ( p ( '/tmp/test/remove.ts' ) ) ;
469472 } ) ;
470473} ) ;
471474
@@ -498,7 +501,7 @@ describe('explainFile', () => {
498501
499502 it ( 'returns the single thread linked to a file' , async ( ) => {
500503 await seedIndex ( {
501- 'explain-owner' : makeThread ( { linked_files : [ '/tmp/test/owned.ts' ] } ) ,
504+ 'explain-owner' : makeThread ( { linked_files : [ p ( '/tmp/test/owned.ts' ) ] } ) ,
502505 } ) ;
503506 const { explainFile } = await import ( '../src/core/operations/explain.js' ) ;
504507
@@ -510,8 +513,8 @@ describe('explainFile', () => {
510513
511514 it ( 'returns all threads when multiple threads link to the same file' , async ( ) => {
512515 await seedIndex ( {
513- 'thread-a' : makeThread ( { linked_files : [ '/tmp/test/shared.ts' ] } ) ,
514- 'thread-b' : makeThread ( { linked_files : [ '/tmp/test/shared.ts' ] } ) ,
516+ 'thread-a' : makeThread ( { linked_files : [ p ( '/tmp/test/shared.ts' ) ] } ) ,
517+ 'thread-b' : makeThread ( { linked_files : [ p ( '/tmp/test/shared.ts' ) ] } ) ,
515518 } ) ;
516519 const { explainFile } = await import ( '../src/core/operations/explain.js' ) ;
517520
@@ -522,8 +525,8 @@ describe('explainFile', () => {
522525
523526 it ( 'includes correct thread ids when multiple threads link to the same file' , async ( ) => {
524527 await seedIndex ( {
525- 'alpha' : makeThread ( { linked_files : [ '/tmp/test/multi-owned.ts' ] } ) ,
526- 'beta' : makeThread ( { linked_files : [ '/tmp/test/multi-owned.ts' ] } ) ,
528+ 'alpha' : makeThread ( { linked_files : [ p ( '/tmp/test/multi-owned.ts' ) ] } ) ,
529+ 'beta' : makeThread ( { linked_files : [ p ( '/tmp/test/multi-owned.ts' ) ] } ) ,
527530 } ) ;
528531 const { explainFile } = await import ( '../src/core/operations/explain.js' ) ;
529532
@@ -535,7 +538,7 @@ describe('explainFile', () => {
535538
536539 it ( 'includes thread summary in the result' , async ( ) => {
537540 await seedIndex ( {
538- 'summary-thread' : makeThread ( { summary : 'Authentication system' , linked_files : [ '/tmp/test/auth.ts' ] } ) ,
541+ 'summary-thread' : makeThread ( { summary : 'Authentication system' , linked_files : [ p ( '/tmp/test/auth.ts' ) ] } ) ,
539542 } ) ;
540543 const { explainFile } = await import ( '../src/core/operations/explain.js' ) ;
541544
@@ -547,7 +550,7 @@ describe('explainFile', () => {
547550 it ( 'includes the snippets belonging to the matched thread' , async ( ) => {
548551 const snippet = { content : 'Used JWT for stateless auth' , source : 'manual' , timestamp : new Date ( ) . toISOString ( ) } ;
549552 await seedIndex ( {
550- 'snippet-explain' : makeThread ( { snippets : [ snippet ] , linked_files : [ '/tmp/test/jwt.ts' ] } ) ,
553+ 'snippet-explain' : makeThread ( { snippets : [ snippet ] , linked_files : [ p ( '/tmp/test/jwt.ts' ) ] } ) ,
551554 } ) ;
552555 const { explainFile } = await import ( '../src/core/operations/explain.js' ) ;
553556
@@ -562,7 +565,7 @@ describe('explainFile', () => {
562565
563566 const result = explainFile ( '/tmp/test/check-path.ts' ) ;
564567
565- expect ( result . data ?. filePath ) . toBe ( '/tmp/test/check-path.ts' ) ;
568+ expect ( result . data ?. filePath ) . toBe ( p ( '/tmp/test/check-path.ts' ) ) ;
566569 } ) ;
567570} ) ;
568571
@@ -666,13 +669,13 @@ describe('showThread', () => {
666669
667670 it ( 'returns linked_files in the thread data' , async ( ) => {
668671 await seedIndex ( {
669- 'files-show' : makeThread ( { linked_files : [ '/tmp/test/app.ts' , '/tmp/test/db.ts' ] } ) ,
672+ 'files-show' : makeThread ( { linked_files : [ p ( '/tmp/test/app.ts' ) , p ( '/tmp/test/db.ts' ) ] } ) ,
670673 } ) ;
671674 const { showThread } = await import ( '../src/core/operations/show.js' ) ;
672675
673676 const result = showThread ( 'files-show' ) ;
674677
675- expect ( result . data ?. thread . linked_files ) . toEqual ( [ '/tmp/test/app.ts' , '/tmp/test/db.ts' ] ) ;
678+ expect ( result . data ?. thread . linked_files ) . toEqual ( [ p ( '/tmp/test/app.ts' ) , p ( '/tmp/test/db.ts' ) ] ) ;
676679 } ) ;
677680} ) ;
678681
0 commit comments