@@ -808,7 +808,9 @@ describe("codex-multi-auth sync", () => {
808808 skipped : 0 ,
809809 total : 4 ,
810810 } ) ;
811- expect ( rmSpy ) . toHaveBeenCalledTimes ( 3 ) ;
811+ expect (
812+ rmSpy . mock . calls . filter ( ( [ path ] ) => String ( path ) . includes ( "oc-chatgpt-multi-auth-sync-" ) ) ,
813+ ) . toHaveLength ( 3 ) ;
812814 expect ( vi . mocked ( loggerModule . logWarn ) ) . not . toHaveBeenCalledWith (
813815 expect . stringContaining ( "Failed to remove temporary codex sync directory" ) ,
814816 ) ;
@@ -961,7 +963,9 @@ describe("codex-multi-auth sync", () => {
961963 }
962964 } ) ;
963965
964- it ( "retries stale temp sweep once on transient Windows lock errors" , async ( ) => {
966+ it . each ( [ "EBUSY" , "ENOTEMPTY" , "EAGAIN" ] as const ) (
967+ "retries stale temp sweep once on transient Windows %s cleanup errors" ,
968+ async ( code ) => {
965969 const rootDir = join ( process . cwd ( ) , ".tmp-codex-multi-auth" ) ;
966970 const fakeHome = await fs . promises . mkdtemp ( join ( os . tmpdir ( ) , "codex-sync-home-" ) ) ;
967971 process . env . CODEX_MULTI_AUTH_DIR = rootDir ;
@@ -987,7 +991,7 @@ describe("codex-multi-auth sync", () => {
987991 const rmSpy = vi . spyOn ( fs . promises , "rm" ) . mockImplementation ( async ( path , options ) => {
988992 if ( ! staleSweepBlocked && String ( path ) === staleDir ) {
989993 staleSweepBlocked = true ;
990- throw Object . assign ( new Error ( "busy" ) , { code : "EBUSY" } ) ;
994+ throw Object . assign ( new Error ( "busy" ) , { code } ) ;
991995 }
992996 return originalRm ( path , options as never ) ;
993997 } ) ;
@@ -1701,6 +1705,67 @@ describe("codex-multi-auth sync", () => {
17011705 } ,
17021706 ) ;
17031707
1708+ it ( "annotates overlap cleanup failures with the written backup path" , async ( ) => {
1709+ const storageModule = await import ( "../lib/storage.js" ) ;
1710+ const persist = vi . fn ( async ( ) => {
1711+ throw new Error ( "persist failed" ) ;
1712+ } ) ;
1713+ vi . mocked ( storageModule . withAccountStorageTransaction ) . mockImplementationOnce ( async ( handler ) =>
1714+ handler (
1715+ {
1716+ version : 3 ,
1717+ activeIndex : 0 ,
1718+ activeIndexByFamily : { } ,
1719+ accounts : [
1720+ {
1721+ accountId : "org-local" ,
1722+ organizationId : "org-local" ,
1723+ accountIdSource : "org" ,
1724+ email : "shared@example.com" ,
1725+ refreshToken : "rt-local" ,
1726+ addedAt : 5 ,
1727+ lastUsed : 5 ,
1728+ } ,
1729+ {
1730+ accountTags : [ "codex-multi-auth-sync" ] ,
1731+ email : "shared@example.com" ,
1732+ refreshToken : "rt-sync" ,
1733+ addedAt : 4 ,
1734+ lastUsed : 4 ,
1735+ } ,
1736+ ] ,
1737+ } ,
1738+ persist ,
1739+ ) ,
1740+ ) ;
1741+ const mkdirSpy = vi . spyOn ( fs . promises , "mkdir" ) . mockResolvedValue ( undefined ) ;
1742+ const writeSpy = vi . spyOn ( fs . promises , "writeFile" ) . mockResolvedValue ( undefined ) ;
1743+ const renameSpy = vi . spyOn ( fs . promises , "rename" ) . mockResolvedValue ( undefined ) ;
1744+
1745+ try {
1746+ const { cleanupCodexMultiAuthSyncedOverlaps } = await import ( "../lib/codex-multi-auth-sync.js" ) ;
1747+ let thrown : unknown ;
1748+ try {
1749+ await cleanupCodexMultiAuthSyncedOverlaps ( "/tmp/overlap-cleanup-backup.json" ) ;
1750+ } catch ( error ) {
1751+ thrown = error ;
1752+ }
1753+
1754+ expect ( mkdirSpy ) . toHaveBeenCalledWith ( "/tmp" , { recursive : true } ) ;
1755+ expect ( writeSpy ) . toHaveBeenCalled ( ) ;
1756+ expect ( renameSpy ) . toHaveBeenCalled ( ) ;
1757+ expect ( thrown ) . toBeInstanceOf ( Error ) ;
1758+ expect ( thrown ) . toMatchObject ( {
1759+ message : "persist failed" ,
1760+ backupPath : "/tmp/overlap-cleanup-backup.json" ,
1761+ } ) ;
1762+ } finally {
1763+ mkdirSpy . mockRestore ( ) ;
1764+ writeSpy . mockRestore ( ) ;
1765+ renameSpy . mockRestore ( ) ;
1766+ }
1767+ } ) ;
1768+
17041769
17051770 it ( "limits overlap cleanup to accounts tagged from codex-multi-auth sync" , async ( ) => {
17061771 const storageModule = await import ( "../lib/storage.js" ) ;
0 commit comments