@@ -21,7 +21,7 @@ import {
2121} from "../src/config" ;
2222
2323import * as windowsAcl from "../src/lib/windows-secret-acl" ;
24- import { hardenConfigDir , hardenExistingSecret , saveConfig } from "../src/config" ;
24+ import { hardenConfigDir , hardenExistingSecret , renameAtomicFile , saveConfig } from "../src/config" ;
2525let testDir = "" ;
2626
2727beforeEach ( ( ) => {
@@ -48,6 +48,34 @@ function writeConfig(content: unknown): void {
4848}
4949
5050describe ( "opencodex config defaults" , ( ) => {
51+ test ( "atomic rename retries transient Windows sharing violations" , ( ) => {
52+ const sleeps : number [ ] = [ ] ;
53+ let attempts = 0 ;
54+ renameAtomicFile ( "source.tmp" , "config.json" , {
55+ platform : "win32" ,
56+ rename : ( ) => {
57+ attempts += 1 ;
58+ if ( attempts < 3 ) throw Object . assign ( new Error ( "locked" ) , { code : "EPERM" } ) ;
59+ } ,
60+ sleep : ms => sleeps . push ( ms ) ,
61+ } ) ;
62+
63+ expect ( attempts ) . toBe ( 3 ) ;
64+ expect ( sleeps ) . toEqual ( [ 25 , 50 ] ) ;
65+ } ) ;
66+
67+ test ( "atomic rename does not retry non-transient errors" , ( ) => {
68+ let attempts = 0 ;
69+ expect ( ( ) => renameAtomicFile ( "source.tmp" , "config.json" , {
70+ platform : "win32" ,
71+ rename : ( ) => {
72+ attempts += 1 ;
73+ throw Object . assign ( new Error ( "invalid" ) , { code : "EINVAL" } ) ;
74+ } ,
75+ sleep : ( ) => { } ,
76+ } ) ) . toThrow ( "invalid" ) ;
77+ expect ( attempts ) . toBe ( 1 ) ;
78+ } ) ;
5179 test ( "Codex autostart is enabled by default" , ( ) => {
5280 expect ( getDefaultConfig ( ) . codexAutoStart ) . toBe ( true ) ;
5381 expect ( codexAutoStartEnabled ( { } ) ) . toBe ( true ) ;
0 commit comments