quak backup refuses to run while another backup of the same directory runs, exit 2 (closes #169)
check / check (push) Successful in 3m24s
check / check (push) Successful in 3m24s
lib.backup() takes a lock, backup.lock in its download directory, made with proper-lockfile, before its refresh, and removes it when it ends. A second backup of the directory fails at once with an error naming it. quak backup takes the lock itself before it opens its library and passes lockHeld to the backup, so a refused run sends no request; it prints the error as one line and exits 2. A lock untouched for 10 seconds, left by a run that could not remove it, is taken over. Deviation: yarn.lock was regenerated by yarn add in the pinned node image. Judgement call: a run failing at its refresh leaves the directory, empty. Judgement call: a lock removed mid-run stops that run with an uncaught error, the library's default. Model: opus-5-5
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
readFileSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
utimesSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { join } from "node:path";
|
||||
@@ -703,6 +704,7 @@ describe("backup", () => {
|
||||
" Failed: 0\n",
|
||||
);
|
||||
expect(stdout.text).toBe("");
|
||||
expect(existsSync(join(dir, "backup.lock"))).toBe(false);
|
||||
});
|
||||
|
||||
// The backup opens its library with the precache off: it fetches the
|
||||
@@ -792,7 +794,7 @@ describe("backup", () => {
|
||||
expect(code).toBe(1);
|
||||
expect(runText).toBe("quak: HTTP 503 from server\n");
|
||||
expect(stderr.text).toBe("Starting backup...\nRefreshing library...\n");
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
expect(readdirSync(dir)).toEqual([]);
|
||||
});
|
||||
|
||||
// A real saved session, read back by `loadSession`, whose server answers
|
||||
@@ -823,7 +825,38 @@ describe("backup", () => {
|
||||
`quak: the saved session is no longer valid; run "quak login"\n`,
|
||||
);
|
||||
expect(stderr.text).toBe("Starting backup...\nRefreshing library...\n");
|
||||
expect(existsSync(dir)).toBe(false);
|
||||
expect(readdirSync(dir)).toEqual([]);
|
||||
});
|
||||
|
||||
it("exits 2 with one line naming the directory, sending no request, while another backup of it runs", async () => {
|
||||
const dir = join(root, "backup");
|
||||
// The lock another backup holds. Its modification time is set an hour
|
||||
// ahead, so it stays current however long this test takes.
|
||||
const lock = join(dir, "backup.lock");
|
||||
mkdirSync(lock, { recursive: true });
|
||||
const hourAhead = new Date(Date.now() + 3_600_000);
|
||||
utimesSync(lock, hourAhead, hourAhead);
|
||||
// A client whose refresh never finishes, so a run that started one
|
||||
// before it exits would never return.
|
||||
let requests = 0;
|
||||
const never = (): Promise<never> => {
|
||||
requests++;
|
||||
return new Promise(() => {});
|
||||
};
|
||||
const client = {
|
||||
...fakeClient(),
|
||||
collectionsSince: never,
|
||||
filesSince: never,
|
||||
} as unknown as Client;
|
||||
|
||||
expect(await backupCommand(context(client), dir, {})).toBe(2);
|
||||
expect(requests).toBe(0);
|
||||
expect(stderr.text).toBe(
|
||||
`Starting backup...\nquak: another backup of ${dir} is running\n`,
|
||||
);
|
||||
expect(readdirSync(dir)).toEqual(["backup.lock"]);
|
||||
// Opening the library would have made its cache directory.
|
||||
expect(existsSync(join(root, "cache"))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user