Skip to content

Commit fc2dc75

Browse files
u1-writer content applied alone onto main tip e487cd7
1 parent e487cd7 commit fc2dc75

4 files changed

Lines changed: 3251 additions & 1 deletion

File tree

‎knip.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
"playwright-ct": false,
1717
"workspaces": {
1818
"src": {
19-
"entry": ["extension.ts!", "extension/api.ts!", "workers/countTokens.ts!"],
19+
"entry": [
20+
"extension.ts!",
21+
"extension/api.ts!",
22+
"workers/countTokens.ts!",
23+
"services/file-safety/safeWriteText.ts!"
24+
],
2025
"project": [
2126
"**/*.ts!",
2227
"!**/__tests__/**!",
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as fs from "fs/promises"
2+
import * as os from "os"
3+
import * as path from "path"
4+
5+
import { safeWriteText } from "../safeWriteText"
6+
7+
// No fs mocks in this file: the point is to assert what a real filesystem ends up
8+
// holding after a publish attempt, which the mocked spec cannot show. The failure is
9+
// provoked with real filesystem semantics rather than with a stubbed call.
10+
describe("safeWriteText against a real filesystem", () => {
11+
let dir: string
12+
13+
beforeEach(async () => {
14+
dir = await fs.mkdtemp(path.join(os.tmpdir(), "safe-write-text-int-"))
15+
})
16+
17+
afterEach(async () => {
18+
await fs.rm(dir, { recursive: true, force: true })
19+
})
20+
21+
it("publishes the new bytes and leaves no staging or backup residue", async () => {
22+
const targetPath = path.join(dir, "target.txt")
23+
await fs.writeFile(targetPath, "old bytes")
24+
25+
// No platform override: the real platform's own durability and ACL steps run, and this
26+
// case is the successful restore. A failed restore now throws DaclRestoreError after
27+
// the commit rename has already happened (the focused unit test "win32 DACL: a failed
28+
// restore is an error and the dump is still unlinked" covers that path), so on a
29+
// machine where icacls cannot put a saved ACL back into a throwaway temp directory
30+
// this publish reports that error instead of resolving, and the residue assertions
31+
// below are reached only where the restore worked.
32+
await safeWriteText(targetPath, "new bytes", { backup: true })
33+
34+
expect(await fs.readFile(targetPath, "utf8")).toBe("new bytes")
35+
expect(await fs.readdir(dir)).toEqual(["target.txt"])
36+
})
37+
38+
it("leaves the target bytes untouched when the commit cannot replace it", async () => {
39+
// A regular file cannot be renamed over a directory, so the backup copy and
40+
// the commit both fail on a real filesystem with no mocking at all.
41+
const targetPath = path.join(dir, "target-dir")
42+
await fs.mkdir(targetPath)
43+
const inside = path.join(targetPath, "payload.txt")
44+
await fs.writeFile(inside, "original bytes")
45+
46+
await expect(safeWriteText(targetPath, "new data", { backup: true })).rejects.toThrow()
47+
48+
// The directory and its content are exactly as they were, and no backup copy
49+
// or staging directory was left behind next to them.
50+
expect(await fs.readFile(inside, "utf8")).toBe("original bytes")
51+
expect(await fs.readdir(dir)).toEqual(["target-dir"])
52+
})
53+
54+
it("cleans up the staging file when the commit rename itself fails", async () => {
55+
// With backup:false the backup step is skipped, so this is the only shape of
56+
// this scenario that reaches Step 4: the rename of the staging FILE over the
57+
// target DIRECTORY fails (EISDIR / ENOTDIR / EPERM depending on platform), and
58+
// the cleanup must remove the staging file. The backup:true case above bails
59+
// out in the backup step and never exercises the commit-failure path.
60+
const targetPath = path.join(dir, "target-dir")
61+
await fs.mkdir(targetPath)
62+
const inside = path.join(targetPath, "payload.txt")
63+
await fs.writeFile(inside, "original bytes")
64+
65+
await expect(safeWriteText(targetPath, "new data", { backup: false })).rejects.toThrow()
66+
67+
expect(await fs.readFile(inside, "utf8")).toBe("original bytes")
68+
expect(await fs.readdir(dir)).toEqual(["target-dir"])
69+
})
70+
})

0 commit comments

Comments
 (0)