@@ -18,6 +18,7 @@ import {
1818 snapshotAndClearAccounts ,
1919 clearAccounts ,
2020 getRestoreAssessment ,
21+ withAccountAndFlaggedStorageTransaction ,
2122} from "../lib/storage.js" ;
2223import {
2324 __resetSyncHistoryForTests ,
@@ -989,11 +990,22 @@ describe("storage recovery paths", () => {
989990 } ) ;
990991 }
991992
992- const backupsDir = getNamedBackupsDirectoryPath ( ) ;
993- const secondSnapshotName =
993+ const keptNames = ( await listAccountSnapshots ( ) ) . map ( ( entry ) => entry . name ) . sort ( ) ;
994+ const expectedKeptNames = [
995+ firstSnapshot . name ,
996+ "accounts-codex-cli-sync-snapshot-2026-03-16_00-00-02_000" ,
997+ "accounts-codex-cli-sync-snapshot-2026-03-16_00-00-03_000" ,
998+ "accounts-codex-cli-sync-snapshot-2026-03-16_00-00-04_000" ,
999+ ] . sort ( ) ;
1000+ const expectedPrunedName =
9941001 "accounts-codex-cli-sync-snapshot-2026-03-16_00-00-01_000" ;
995- expect ( existsSync ( firstSnapshot . path ) ) . toBe ( true ) ;
996- expect ( existsSync ( join ( backupsDir , `${ secondSnapshotName } .json` ) ) ) . toBe ( false ) ;
1002+ const backupsDir = getNamedBackupsDirectoryPath ( ) ;
1003+
1004+ expect ( keptNames ) . toEqual ( expectedKeptNames ) ;
1005+ for ( const name of expectedKeptNames ) {
1006+ expect ( existsSync ( join ( backupsDir , `${ name } .json` ) ) ) . toBe ( true ) ;
1007+ }
1008+ expect ( existsSync ( join ( backupsDir , `${ expectedPrunedName } .json` ) ) ) . toBe ( false ) ;
9971009 } ) ;
9981010
9991011 it ( "retains rollback-referenced snapshots when a newer manual sync has no checkpoint" , async ( ) => {
@@ -1071,12 +1083,78 @@ describe("storage recovery paths", () => {
10711083
10721084 const result = await pruneAutoGeneratedSnapshots ( ) ;
10731085 const backupsDir = getNamedBackupsDirectoryPath ( ) ;
1074- const secondSnapshotName =
1075- "accounts-codex-cli-sync-snapshot-2026-03-16_01-00-01_000" ;
1086+ const expectedKeptNames = [
1087+ firstSnapshot . name ,
1088+ "accounts-codex-cli-sync-snapshot-2026-03-16_01-00-02_000" ,
1089+ "accounts-codex-cli-sync-snapshot-2026-03-16_01-00-03_000" ,
1090+ "accounts-codex-cli-sync-snapshot-2026-03-16_01-00-04_000" ,
1091+ ] . sort ( ) ;
1092+ const expectedAlreadyPrunedNames = [
1093+ "accounts-codex-cli-sync-snapshot-2026-03-16_01-00-01_000" ,
1094+ ] ;
1095+
1096+ expect ( result . kept . map ( ( entry ) => entry . name ) . sort ( ) ) . toEqual ( expectedKeptNames ) ;
1097+ expect ( result . pruned ) . toEqual ( [ ] ) ;
1098+ for ( const name of expectedKeptNames ) {
1099+ expect ( existsSync ( join ( backupsDir , `${ name } .json` ) ) ) . toBe ( true ) ;
1100+ }
1101+ for ( const name of expectedAlreadyPrunedNames ) {
1102+ expect ( existsSync ( join ( backupsDir , `${ name } .json` ) ) ) . toBe ( false ) ;
1103+ }
1104+ } ) ;
1105+
1106+ it ( "enforces snapshot retention against the transaction-pinned storage path" , async ( ) => {
1107+ const primaryDir = join ( workDir , "primary" ) ;
1108+ const alternateDir = join ( workDir , "alternate" ) ;
1109+ const primaryStoragePath = join ( primaryDir , "openai-codex-accounts.json" ) ;
1110+ const alternateStoragePath = join ( alternateDir , "openai-codex-accounts.json" ) ;
1111+ await fs . mkdir ( primaryDir , { recursive : true } ) ;
1112+ await fs . mkdir ( alternateDir , { recursive : true } ) ;
1113+ setStoragePathDirect ( primaryStoragePath ) ;
1114+ await saveAccounts ( {
1115+ version : 3 ,
1116+ activeIndex : 0 ,
1117+ accounts : [
1118+ {
1119+ refreshToken : "tx-retention-refresh" ,
1120+ accountId : "tx-retention-account" ,
1121+ addedAt : 1 ,
1122+ lastUsed : 1 ,
1123+ } ,
1124+ ] ,
1125+ } ) ;
1126+
1127+ const baseTime = Date . UTC ( 2026 , 2 , 16 , 3 , 0 , 0 , 0 ) ;
1128+ for ( let index = 0 ; index < 3 ; index += 1 ) {
1129+ await snapshotAccountStorage ( {
1130+ reason : "codex-cli-sync" ,
1131+ now : baseTime + index * 1_000 ,
1132+ } ) ;
1133+ }
1134+
1135+ const primaryBackupsDir = getNamedBackupsDirectoryPath ( ) ;
1136+ await withAccountAndFlaggedStorageTransaction ( async ( current ) => {
1137+ setStoragePathDirect ( alternateStoragePath ) ;
1138+ await snapshotAccountStorage ( {
1139+ reason : "codex-cli-sync" ,
1140+ now : baseTime + 3_000 ,
1141+ storage : current ,
1142+ } ) ;
1143+ } ) ;
10761144
1077- expect ( result . kept . map ( ( entry ) => entry . name ) ) . toContain ( firstSnapshot . name ) ;
1078- expect ( existsSync ( firstSnapshot . path ) ) . toBe ( true ) ;
1079- expect ( existsSync ( join ( backupsDir , `${ secondSnapshotName } .json` ) ) ) . toBe ( false ) ;
1145+ const primarySnapshotNames = ( await fs . readdir ( primaryBackupsDir ) )
1146+ . filter ( ( entry ) => entry . endsWith ( ".json" ) )
1147+ . map ( ( entry ) => entry . slice ( 0 , - ".json" . length ) )
1148+ . sort ( ) ;
1149+ const expectedPrimaryNames = [
1150+ "accounts-codex-cli-sync-snapshot-2026-03-16_03-00-01_000" ,
1151+ "accounts-codex-cli-sync-snapshot-2026-03-16_03-00-02_000" ,
1152+ "accounts-codex-cli-sync-snapshot-2026-03-16_03-00-03_000" ,
1153+ ] . sort ( ) ;
1154+
1155+ expect ( primarySnapshotNames ) . toEqual ( expectedPrimaryNames ) ;
1156+ expect ( existsSync ( primaryStoragePath ) ) . toBe ( true ) ;
1157+ expect ( existsSync ( alternateStoragePath ) ) . toBe ( false ) ;
10801158 } ) ;
10811159
10821160 it ( "falls back to the newest live rollback snapshot when a newer recorded checkpoint file is missing" , async ( ) => {
0 commit comments