import { describe, expect, it } from "vitest" ;
import { createConfigIO } from "./io.js" ;
function makeEaccesFs(configPath: string) {
const eaccesErr = Object.assign(new Error(`EACCES: permission denied, open '${configPath}' `), {
code: "EACCES" ,
});
return {
existsSync: (p: string) => p === configPath,
readFileSync: (p: string): string => {
if (p === configPath) {
throw eaccesErr;
}
throw new Error(`unexpected readFileSync: ${p}`);
},
promises: {
readFile: () => Promise.reject(eaccesErr),
mkdir: () => Promise.resolve(),
writeFile: () => Promise.resolve(),
appendFile: () => Promise.resolve(),
},
} as unknown as typeof import ("node:fs" );
}
describe("config io EACCES handling" , () => {
it("returns a helpful error message when config file is not readable (EACCES)" , async () => {
const configPath = "/data/.openclaw/openclaw.json" ;
const errors: string[] = [];
const io = createConfigIO({
configPath,
fs: makeEaccesFs(configPath),
logger: {
error: (msg: unknown) => errors.push(String(msg)),
warn: () => {},
},
});
const snapshot = await io.readConfigFileSnapshot();
expect(snapshot.valid).toBe(false );
expect(snapshot.issues).toHaveLength(1 );
expect(snapshot.issues[0 ].message).toContain("EACCES" );
expect(snapshot.issues[0 ].message).toContain("chown" );
expect(snapshot.issues[0 ].message).toContain(configPath);
// Should also emit to the logger
expect(errors.some((e) => e.includes("chown" ))).toBe(true );
});
it("includes configPath in the chown hint for the correct remediation command" , async () => {
const configPath = "/home/myuser/.openclaw/openclaw.json" ;
const io = createConfigIO({
configPath,
fs: makeEaccesFs(configPath),
logger: { error: () => {}, warn: () => {} },
});
const snapshot = await io.readConfigFileSnapshot();
expect(snapshot.issues[0 ].message).toContain(configPath);
expect(snapshot.issues[0 ].message).toContain("container" );
});
});
Messung V0.5 in Prozent C=99 H=100 G=99
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-06)
¤
*© Formatika GbR, Deutschland