Address review: safer save, stricter load and id parsing
- save() follows symlinks, keeps permissions, uses a unique temp file and cleans it up on failure
- load() rejects entries that aren't {id, text, done}
- add() finds the max id with reduce (no spread limit)
- CLI accepts only plain digits as ids and reports save errors readably
- Ignore *.tmp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba17376132
commit
8dc5194438
5 files changed
+95
-13
No files matched your search
+20
-2
@@ -1,9 +1,9 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdtempSync, writeFileSync, readFileSync, existsSync } from "node:fs";
|
||||
import { mkdtempSync, writeFileSync, readFileSync, existsSync, chmodSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const CLI = fileURLToPath(new URL("../src/cli.js", import.meta.url));
|
||||
@@ -36,6 +36,11 @@ test("add with no text fails without saving", () => {
|
||||
for (const [label, args, message] of [
|
||||
["missing id", [], /Invalid id: \(missing\)/],
|
||||
["non-numeric id", ["abc"], /Invalid id: abc/],
|
||||
["hex id", ["0x1"], /Invalid id: 0x1/],
|
||||
["exponent id", ["1e0"], /Invalid id: 1e0/],
|
||||
["decimal id", ["1.0"], /Invalid id: 1\.0/],
|
||||
["padded id", [" 1 "], /Invalid id: 1 /],
|
||||
["empty id", [""], /Invalid id: \n/],
|
||||
["unknown id", ["99"], /No todo with id 99/],
|
||||
]) {
|
||||
for (const cmd of ["done", "rm"]) {
|
||||
@@ -59,6 +64,19 @@ test("unknown command prints usage even when the file is corrupt", () => {
|
||||
assert.match(res.stderr, /Unknown command: --help\nUsage:/);
|
||||
});
|
||||
|
||||
test("a failed save gives a readable error instead of a stack trace", () => {
|
||||
const file = tempFile();
|
||||
chmodSync(dirname(file), 0o500);
|
||||
try {
|
||||
const res = run(file, "add", "a");
|
||||
assert.equal(res.status, 1);
|
||||
assert.match(res.stderr, /^Could not save /);
|
||||
assert.doesNotMatch(res.stderr, /at .*\.js:\d+/);
|
||||
} finally {
|
||||
chmodSync(dirname(file), 0o700);
|
||||
}
|
||||
});
|
||||
|
||||
test("a corrupt file gives a readable error instead of a stack trace", () => {
|
||||
const file = tempFile();
|
||||
writeFileSync(file, "{ not json");
|
||||
|
||||
Reference in new issue
Block a user