Skip to content

Commit 320a794

Browse files
committed
test: cover flagged storage retry follow-ups
1 parent 47da311 commit 320a794

2 files changed

Lines changed: 182 additions & 2 deletions

File tree

test/codex-bin-wrapper.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,11 +572,9 @@ describe("codex bin wrapper", () => {
572572
' \' fs.writeFileSync(path.join(target, \"accounts.json\"), \"{\\\\\"accounts\\\\\":[\\\\\"external-during-retry\\\\\"]}\\\\n\", \"utf8\");\',',
573573
' \' fs.writeFileSync(path.join(target, \".codex-global-state.json\"), \"{\\\\\"last\\\\\":\\\\\"external-during-retry\\\\\"}\\\\n\", \"utf8\");\',',
574574
' \' process.exit(0);\',',
575-
' \' return;\',',
576575
' \' }\',',
577576
' \' if (Date.now() - startedAt > 5000) {\',',
578577
' \' process.exit(2);\',',
579-
' \' return;\',',
580578
' \' }\',',
581579
' \' setTimeout(waitForMarker, 5);\',',
582580
' \'};\',',

test/storage-flagged.test.ts

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,4 +785,186 @@ describe("flagged storage extracted helpers", () => {
785785
await removeWithRetry(fixtureRoot, { recursive: true, force: true });
786786
}
787787
});
788+
789+
it("retries transient backup read locks before recovering flagged storage", async () => {
790+
const { loadFlaggedAccountsState } = await import(
791+
"../lib/storage/flagged-storage-io.js"
792+
);
793+
const fixtureRoot = join(
794+
tmpdir(),
795+
`codex-flagged-io-${Math.random().toString(36).slice(2)}`,
796+
);
797+
const flaggedPath = join(fixtureRoot, "flagged.json");
798+
const backupPath = `${flaggedPath}.bak`;
799+
const resetMarkerPath = `${flaggedPath}.reset`;
800+
const originalReadFile = fs.readFile.bind(fs);
801+
const logError = vi.fn();
802+
const logInfo = vi.fn();
803+
const persistRecoveredBackup = vi.fn(async () => true);
804+
let backupReadAttempts = 0;
805+
806+
try {
807+
await fs.mkdir(fixtureRoot, { recursive: true });
808+
await fs.writeFile(
809+
backupPath,
810+
JSON.stringify({
811+
version: 1,
812+
accounts: [
813+
{
814+
refreshToken: "backup-token",
815+
flaggedAt: 1,
816+
addedAt: 1,
817+
lastUsed: 1,
818+
},
819+
],
820+
}),
821+
"utf8",
822+
);
823+
824+
const readSpy = vi
825+
.spyOn(fs, "readFile")
826+
.mockImplementation(async (...args) => {
827+
const [targetPath] = args;
828+
if (targetPath === backupPath) {
829+
backupReadAttempts += 1;
830+
if (backupReadAttempts === 1) {
831+
const error = new Error("EBUSY backup read") as NodeJS.ErrnoException;
832+
error.code = "EBUSY";
833+
throw error;
834+
}
835+
}
836+
return originalReadFile(...args);
837+
});
838+
839+
await expect(
840+
loadFlaggedAccountsState({
841+
path: flaggedPath,
842+
legacyPath: `${flaggedPath}.legacy`,
843+
resetMarkerPath,
844+
normalizeFlaggedStorage: (data) => data as never,
845+
persistRecoveredBackup,
846+
saveFlaggedAccounts: vi.fn(async () => {}),
847+
logError,
848+
logInfo,
849+
}),
850+
).resolves.toEqual({
851+
version: 1,
852+
accounts: [
853+
{
854+
refreshToken: "backup-token",
855+
flaggedAt: 1,
856+
addedAt: 1,
857+
lastUsed: 1,
858+
},
859+
],
860+
});
861+
expect(backupReadAttempts).toBe(2);
862+
expect(persistRecoveredBackup).toHaveBeenCalledTimes(1);
863+
expect(logInfo).toHaveBeenCalledWith(
864+
"Recovered flagged account storage from backup",
865+
expect.objectContaining({ from: backupPath, to: flaggedPath, accounts: 1 }),
866+
);
867+
expect(logError).not.toHaveBeenCalled();
868+
869+
readSpy.mockRestore();
870+
} finally {
871+
await removeWithRetry(fixtureRoot, { recursive: true, force: true });
872+
}
873+
});
874+
875+
it("retries transient legacy read locks before migrating flagged storage", async () => {
876+
const { loadFlaggedAccountsState } = await import(
877+
"../lib/storage/flagged-storage-io.js"
878+
);
879+
const fixtureRoot = join(
880+
tmpdir(),
881+
`codex-flagged-io-${Math.random().toString(36).slice(2)}`,
882+
);
883+
const flaggedPath = join(fixtureRoot, "flagged.json");
884+
const legacyPath = `${flaggedPath}.legacy`;
885+
const resetMarkerPath = `${flaggedPath}.reset`;
886+
const originalReadFile = fs.readFile.bind(fs);
887+
const logError = vi.fn();
888+
const logInfo = vi.fn();
889+
const saveFlaggedAccounts = vi.fn(async () => {});
890+
let legacyReadAttempts = 0;
891+
892+
try {
893+
await fs.mkdir(fixtureRoot, { recursive: true });
894+
await fs.writeFile(
895+
legacyPath,
896+
JSON.stringify({
897+
version: 1,
898+
accounts: [
899+
{
900+
refreshToken: "legacy-token",
901+
flaggedAt: 2,
902+
addedAt: 2,
903+
lastUsed: 2,
904+
},
905+
],
906+
}),
907+
"utf8",
908+
);
909+
910+
const readSpy = vi
911+
.spyOn(fs, "readFile")
912+
.mockImplementation(async (...args) => {
913+
const [targetPath] = args;
914+
if (targetPath === legacyPath) {
915+
legacyReadAttempts += 1;
916+
if (legacyReadAttempts === 1) {
917+
const error = new Error("EBUSY legacy read") as NodeJS.ErrnoException;
918+
error.code = "EBUSY";
919+
throw error;
920+
}
921+
}
922+
return originalReadFile(...args);
923+
});
924+
925+
await expect(
926+
loadFlaggedAccountsState({
927+
path: flaggedPath,
928+
legacyPath,
929+
resetMarkerPath,
930+
normalizeFlaggedStorage: (data) => data as never,
931+
persistRecoveredBackup: vi.fn(async () => true),
932+
saveFlaggedAccounts,
933+
logError,
934+
logInfo,
935+
}),
936+
).resolves.toEqual({
937+
version: 1,
938+
accounts: [
939+
{
940+
refreshToken: "legacy-token",
941+
flaggedAt: 2,
942+
addedAt: 2,
943+
lastUsed: 2,
944+
},
945+
],
946+
});
947+
expect(legacyReadAttempts).toBe(2);
948+
expect(saveFlaggedAccounts).toHaveBeenCalledWith({
949+
version: 1,
950+
accounts: [
951+
{
952+
refreshToken: "legacy-token",
953+
flaggedAt: 2,
954+
addedAt: 2,
955+
lastUsed: 2,
956+
},
957+
],
958+
});
959+
expect(logInfo).toHaveBeenCalledWith(
960+
"Migrated legacy flagged account storage",
961+
expect.objectContaining({ from: legacyPath, to: flaggedPath, accounts: 1 }),
962+
);
963+
expect(logError).not.toHaveBeenCalled();
964+
965+
readSpy.mockRestore();
966+
} finally {
967+
await removeWithRetry(fixtureRoot, { recursive: true, force: true });
968+
}
969+
});
788970
});

0 commit comments

Comments
 (0)