@@ -4,16 +4,19 @@ import { clearAccountsEntry } from "../lib/storage/account-clear-entry.js";
44describe ( "account clear entry" , ( ) => {
55 it ( "delegates clear through the storage lock and backup resolver" , async ( ) => {
66 const clearAccountStorageArtifacts = vi . fn ( async ( ) => undefined ) ;
7+ const withStorageLock = vi . fn ( async ( fn : ( ) => Promise < void > ) => fn ( ) ) ;
78 await clearAccountsEntry ( {
89 path : "/tmp/accounts.json" ,
9- withStorageLock : async ( fn ) => fn ( ) ,
10+ withStorageLock,
1011 resetMarkerPath : "/tmp/accounts.reset-intent" ,
1112 walPath : "/tmp/accounts.wal" ,
1213 getBackupPaths : async ( ) => [ "/tmp/accounts.json.bak" ] ,
1314 clearAccountStorageArtifacts,
1415 logError : vi . fn ( ) ,
1516 } ) ;
1617
18+ expect ( withStorageLock ) . toHaveBeenCalledOnce ( ) ;
19+ expect ( clearAccountStorageArtifacts ) . toHaveBeenCalledTimes ( 1 ) ;
1720 expect ( clearAccountStorageArtifacts ) . toHaveBeenCalledWith ( {
1821 path : "/tmp/accounts.json" ,
1922 resetMarkerPath : "/tmp/accounts.reset-intent" ,
@@ -22,4 +25,92 @@ describe("account clear entry", () => {
2225 logError : expect . any ( Function ) ,
2326 } ) ;
2427 } ) ;
28+
29+ it ( "serializes concurrent clears through the shared storage lock" , async ( ) => {
30+ const events : string [ ] = [ ] ;
31+ let releaseFirst : ( ( ) => void ) | undefined ;
32+ const firstGate = new Promise < void > ( ( resolve ) => {
33+ releaseFirst = resolve ;
34+ } ) ;
35+ let queue = Promise . resolve ( ) ;
36+ const withStorageLock = vi . fn ( async < T > ( fn : ( ) => Promise < T > ) => {
37+ const run = queue . then ( fn ) ;
38+ queue = run . then (
39+ ( ) => undefined ,
40+ ( ) => undefined ,
41+ ) ;
42+ return run ;
43+ } ) ;
44+ const clearAccountStorageArtifacts = vi . fn (
45+ async ( { path } : { path : string } ) => {
46+ events . push ( `start:${ path } ` ) ;
47+ if ( path === "/tmp/first.json" ) {
48+ await firstGate ;
49+ }
50+ events . push ( `end:${ path } ` ) ;
51+ } ,
52+ ) ;
53+
54+ const firstCall = clearAccountsEntry ( {
55+ path : "/tmp/first.json" ,
56+ withStorageLock,
57+ resetMarkerPath : "/tmp/first.reset-intent" ,
58+ walPath : "/tmp/first.wal" ,
59+ getBackupPaths : async ( ) => [ "/tmp/first.json.bak" ] ,
60+ clearAccountStorageArtifacts,
61+ logError : vi . fn ( ) ,
62+ } ) ;
63+ const secondCall = clearAccountsEntry ( {
64+ path : "/tmp/second.json" ,
65+ withStorageLock,
66+ resetMarkerPath : "/tmp/second.reset-intent" ,
67+ walPath : "/tmp/second.wal" ,
68+ getBackupPaths : async ( ) => [ "/tmp/second.json.bak" ] ,
69+ clearAccountStorageArtifacts,
70+ logError : vi . fn ( ) ,
71+ } ) ;
72+
73+ await Promise . resolve ( ) ;
74+ await Promise . resolve ( ) ;
75+
76+ expect ( events ) . toEqual ( [ "start:/tmp/first.json" ] ) ;
77+ releaseFirst ?.( ) ;
78+ await Promise . all ( [ firstCall , secondCall ] ) ;
79+
80+ expect ( withStorageLock ) . toHaveBeenCalledTimes ( 2 ) ;
81+ expect ( events ) . toEqual ( [
82+ "start:/tmp/first.json" ,
83+ "end:/tmp/first.json" ,
84+ "start:/tmp/second.json" ,
85+ "end:/tmp/second.json" ,
86+ ] ) ;
87+ } ) ;
88+
89+ it ( "preserves windows-style paths when clearing storage artifacts" , async ( ) => {
90+ const windowsPath = "C:\\codex\\accounts.json" ;
91+ const resetMarkerPath = "C:\\codex\\accounts.reset-intent" ;
92+ const walPath = "C:\\codex\\accounts.wal" ;
93+ const backupPath = "C:\\codex\\accounts.json.bak" ;
94+ const withStorageLock = vi . fn ( async ( fn : ( ) => Promise < void > ) => fn ( ) ) ;
95+ const clearAccountStorageArtifacts = vi . fn ( async ( ) => undefined ) ;
96+
97+ await clearAccountsEntry ( {
98+ path : windowsPath ,
99+ withStorageLock,
100+ resetMarkerPath,
101+ walPath,
102+ getBackupPaths : async ( ) => [ backupPath ] ,
103+ clearAccountStorageArtifacts,
104+ logError : vi . fn ( ) ,
105+ } ) ;
106+
107+ expect ( withStorageLock ) . toHaveBeenCalledOnce ( ) ;
108+ expect ( clearAccountStorageArtifacts ) . toHaveBeenCalledWith ( {
109+ path : windowsPath ,
110+ resetMarkerPath,
111+ walPath,
112+ backupPaths : [ backupPath ] ,
113+ logError : expect . any ( Function ) ,
114+ } ) ;
115+ } ) ;
25116} ) ;
0 commit comments