Files
oh-my-opencode/src/shared/write-file-atomically.test.ts
T
YeonGyu-Kim 7735b2abd5 fix(shared): tolerate EPERM during fsync in writeFileAtomically
Replaces fsyncSync(tempFileDescriptor) with tolerantFsyncSync, allowing
EPERM/EACCES/ENOTSUP/EINVAL during fsync while still propagating real
errors. Adds an optional deps.fsyncSync injection point used solely by
the new EPERM tolerance regression tests.

Without this fix, plugin startup itself can fail on synced folders
because writeFileAtomically is used by config migrations and posthog
activity state — the same EPERM-on-fsync failure pattern reported for
team_create.
2026-05-08 14:09:17 +09:00

90 lines
2.7 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from "bun:test"
import { readFileSync, writeFileSync, existsSync, rmSync, mkdirSync } from "fs"
import { join } from "path"
import { tmpdir } from "os"
import { writeFileAtomically } from "./write-file-atomically"
const testDir = join(tmpdir(), "write-file-atomically-test-" + Date.now())
beforeEach(() => {
mkdirSync(testDir, { recursive: true })
})
afterEach(() => {
rmSync(testDir, { recursive: true, force: true })
})
describe("writeFileAtomically", () => {
it("writes content to a new file", () => {
// given
const filePath = join(testDir, "new-file.txt")
const content = "hello world"
// when
writeFileAtomically(filePath, content)
// then
expect(existsSync(filePath)).toBe(true)
expect(readFileSync(filePath, "utf-8")).toBe(content)
})
it("#given target file exists #when writeFileAtomically called #then overwrites successfully", () => {
// given
const filePath = join(testDir, "existing-file.txt")
const originalContent = "original content"
const newContent = "new content"
writeFileSync(filePath, originalContent, "utf-8")
// when
writeFileAtomically(filePath, newContent)
// then
expect(existsSync(filePath)).toBe(true)
expect(readFileSync(filePath, "utf-8")).toBe(newContent)
expect(existsSync(`${filePath}.tmp`)).toBe(false)
})
it("#given parent directory does not exist #when writeFileAtomically called #then throws", () => {
// given
const filePath = join(testDir, "nonexistent", "deep", "file.txt")
// when/then
expect(() => writeFileAtomically(filePath, "content")).toThrow()
})
it("#given fsync fails with EPERM (synced folder) #when writeFileAtomically called #then write succeeds", () => {
// given
const filePath = join(testDir, "synced-folder.txt")
const content = "content from a synced folder where fsync is rejected"
// when
writeFileAtomically(filePath, content, {
fsyncSync: () => {
const error = new Error("EPERM: operation not permitted, fsync") as NodeJS.ErrnoException
error.code = "EPERM"
throw error
},
})
// then
expect(existsSync(filePath)).toBe(true)
expect(readFileSync(filePath, "utf-8")).toBe(content)
})
it("#given fsync fails with EIO (real I/O error) #when writeFileAtomically called #then propagates the error", () => {
// given
const filePath = join(testDir, "io-error.txt")
// when/then
expect(() =>
writeFileAtomically(filePath, "content", {
fsyncSync: () => {
const error = new Error("EIO: input/output error") as NodeJS.ErrnoException
error.code = "EIO"
throw error
},
}),
).toThrow("EIO")
})
})