Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78dd2b8ebf |
+3
-11
@@ -23,10 +23,7 @@
|
||||
// download or a failed symlink is caught, recorded in `failures.json` with a
|
||||
// classification, a running attempt count, and the last-tried time, and the run
|
||||
// continues. `result.failed` — and thus the CLI's exit code — stays non-zero
|
||||
// while any failure remains unresolved and clears once every one succeeds. Each
|
||||
// run reconciles the ledger against the files it attempted, so an entry for a
|
||||
// file that has since left the library (deleted) or this run's scope is dropped
|
||||
// rather than counted forever, which would poison a scheduled backup's exit code.
|
||||
// while any failure remains unresolved and clears once every one succeeds.
|
||||
|
||||
import {
|
||||
copyFileSync,
|
||||
@@ -401,13 +398,8 @@ export const runBackup = async (
|
||||
);
|
||||
}
|
||||
|
||||
// Reconcile the ledger against what this run actually attempted: an entry
|
||||
// survives only for a file that failed this run. A file that succeeded had
|
||||
// its failure resolved; a file gone from the library (deleted) or outside
|
||||
// this run's scope is not something this run can resolve, so keeping its
|
||||
// stale entry would keep the exit code non-zero forever — a single
|
||||
// since-deleted photo would fail every future scheduled backup.
|
||||
for (const fileID of [...ledger.keys()]) {
|
||||
// A file in scope that did not fail this run has no unresolved failure.
|
||||
for (const fileID of distinct.keys()) {
|
||||
if (!failedThisRun.has(fileID)) ledger.delete(fileID);
|
||||
}
|
||||
saveLedger(ledgerPath, ledger);
|
||||
|
||||
@@ -147,28 +147,6 @@ const readLedger = (
|
||||
): { files: Record<string, Record<string, unknown>> } =>
|
||||
JSON.parse(readFileSync(join(outDir, "failures.json"), "utf-8"));
|
||||
|
||||
// Write a durable ledger holding one prior failure, to exercise pruning of
|
||||
// entries the current run cannot resolve.
|
||||
const seedLedger = (outDir: string, fileID: number, title: string): void => {
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(outDir, "failures.json"),
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
files: {
|
||||
[String(fileID)]: {
|
||||
fileID,
|
||||
title,
|
||||
classification: "transient",
|
||||
attempts: 1,
|
||||
lastTriedAt: Date.now(),
|
||||
error: "HTTP 500 from server",
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
root = mkdtempSync(join(tmpdir(), "quak-backup-test-"));
|
||||
});
|
||||
@@ -399,47 +377,6 @@ describe("lib.backup", () => {
|
||||
lib.close();
|
||||
});
|
||||
|
||||
it("prunes a ledger entry for a file no longer in the library and exits zero", async () => {
|
||||
const source = stubSource();
|
||||
const lib = await openLibrary(source);
|
||||
const outDir = join(root, "backup");
|
||||
|
||||
// A prior failure for a file that has since left the library (deleted
|
||||
// from the account). This run has no way to resolve it, so it must not
|
||||
// keep the exit code non-zero forever.
|
||||
seedLedger(outDir, 999, "gone.jpg");
|
||||
|
||||
const result = await lib.backup({ downloadDirectory: outDir });
|
||||
|
||||
// Everything still present is backed up cleanly, and the stale entry is
|
||||
// dropped rather than counted.
|
||||
expect(result.downloaded).toBe(3);
|
||||
expect(result.failed).toBe(0);
|
||||
expect(existsSync(join(outDir, "failures.json"))).toBe(false);
|
||||
lib.close();
|
||||
});
|
||||
|
||||
it("prunes an out-of-scope ledger entry on a scoped run and exits zero", async () => {
|
||||
const source = stubSource();
|
||||
const lib = await openLibrary(source);
|
||||
const outDir = join(root, "backup");
|
||||
|
||||
// A prior failure for a Vacation file; this run is scoped to Work and
|
||||
// never attempts it, so it must not poison the scoped run's exit code.
|
||||
seedLedger(outDir, 100, "beach.jpg");
|
||||
|
||||
const result = await lib.backup({
|
||||
downloadDirectory: outDir,
|
||||
onlyAlbumNames: ["Work"],
|
||||
});
|
||||
|
||||
expect(result.totalFiles).toBe(1);
|
||||
expect(result.failed).toBe(0);
|
||||
expect(existsSync(join(outDir, "originals", "200.png"))).toBe(true);
|
||||
expect(existsSync(join(outDir, "failures.json"))).toBe(false);
|
||||
lib.close();
|
||||
});
|
||||
|
||||
it("also stores thumbnails when includeThumbnails is set", async () => {
|
||||
const source = stubSource();
|
||||
const lib = await openLibrary(source);
|
||||
|
||||
Reference in New Issue
Block a user