Skip to content

Commit 34e74a1

Browse files
author
Slyrian
committed
fix(tests): mock os.homedir() to prevent tests from deleting user config
Tests were writing to and deleting the real ~/.config/opencode/.everynotify.json because they used os.homedir() directly. Running `bun test` would destroy the user's global configuration file. Now both config and integration tests use a spied homedir pointing to a temp directory, fully isolating test I/O.
1 parent be36b26 commit 34e74a1

2 files changed

Lines changed: 52 additions & 40 deletions

File tree

src/__tests__/config.test.ts

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
/// <reference types="bun" />
2-
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test";
2+
import {
3+
describe,
4+
it,
5+
expect,
6+
beforeEach,
7+
afterEach,
8+
beforeAll,
9+
afterAll,
10+
mock,
11+
spyOn,
12+
} from "bun:test";
313
import * as fs from "fs";
414
import * as path from "path";
515
import * as os from "os";
@@ -9,7 +19,24 @@ import type { EverynotifyConfig } from "../types";
919
const mockConsoleError = mock(() => {});
1020
let originalConsoleError: typeof console.error;
1121

22+
/**
23+
* Fake homedir used by all tests in this file.
24+
* Prevents tests from reading/writing the real ~/.config/opencode/.everynotify.json
25+
*/
26+
let fakeHomeDir: string;
27+
let homedirSpy: ReturnType<typeof spyOn>;
28+
1229
describe("Config Loader", () => {
30+
beforeAll(() => {
31+
fakeHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), "everynotify-home-"));
32+
homedirSpy = spyOn(os, "homedir").mockReturnValue(fakeHomeDir);
33+
});
34+
35+
afterAll(() => {
36+
homedirSpy.mockRestore();
37+
fs.rmSync(fakeHomeDir, { recursive: true, force: true });
38+
});
39+
1340
beforeEach(() => {
1441
mockConsoleError.mockClear();
1542
originalConsoleError = console.error;
@@ -18,6 +45,15 @@ describe("Config Loader", () => {
1845

1946
afterEach(() => {
2047
console.error = originalConsoleError;
48+
const fakeGlobalConfig = path.join(
49+
fakeHomeDir,
50+
".config",
51+
"opencode",
52+
".everynotify.json",
53+
);
54+
if (fs.existsSync(fakeGlobalConfig)) {
55+
fs.rmSync(fakeGlobalConfig, { force: true });
56+
}
2157
});
2258

2359
describe("DEFAULT_CONFIG", () => {
@@ -57,41 +93,19 @@ describe("Config Loader", () => {
5793

5894
describe("loadConfig", () => {
5995
it("should return defaults when no config files exist", () => {
60-
// Temporarily remove global config if it exists to isolate this test
61-
const globalPath = getConfigPath("global", "");
62-
let backup: string | null = null;
63-
try {
64-
if (fs.existsSync(globalPath)) {
65-
backup = fs.readFileSync(globalPath, "utf-8");
66-
fs.unlinkSync(globalPath);
67-
}
68-
69-
const config = loadConfig("/nonexistent/directory/12345");
70-
expect(config.pushover.enabled).toBe(false);
71-
expect(config.telegram.enabled).toBe(false);
72-
expect(config.slack.enabled).toBe(false);
73-
expect(config.discord.enabled).toBe(false);
74-
} finally {
75-
if (backup !== null) {
76-
fs.writeFileSync(globalPath, backup);
77-
}
78-
}
96+
const config = loadConfig("/nonexistent/directory/12345");
97+
expect(config.pushover.enabled).toBe(false);
98+
expect(config.telegram.enabled).toBe(false);
99+
expect(config.slack.enabled).toBe(false);
100+
expect(config.discord.enabled).toBe(false);
79101
});
80102

81103
it("should log warning when all services disabled", () => {
82-
// Temporarily remove global config if it exists to isolate this test
83-
const globalPath = getConfigPath("global", "");
84-
let backup: string | null = null;
85104
const originalError = console.error;
86105
const logs: string[] = [];
87106
console.error = (msg: string) => logs.push(msg);
88107

89108
try {
90-
if (fs.existsSync(globalPath)) {
91-
backup = fs.readFileSync(globalPath, "utf-8");
92-
fs.unlinkSync(globalPath);
93-
}
94-
95109
loadConfig("/nonexistent/directory/12345");
96110

97111
expect(
@@ -101,9 +115,6 @@ describe("Config Loader", () => {
101115
).toBe(true);
102116
} finally {
103117
console.error = originalError;
104-
if (backup !== null) {
105-
fs.writeFileSync(globalPath, backup);
106-
}
107118
}
108119
});
109120

@@ -134,9 +145,6 @@ describe("Config Loader", () => {
134145
expect(config.telegram.enabled).toBe(false);
135146
} finally {
136147
fs.rmSync(tempDir, { recursive: true, force: true });
137-
fs.rmSync(path.join(globalConfigDir, ".everynotify.json"), {
138-
force: true,
139-
});
140148
}
141149
});
142150

@@ -185,9 +193,6 @@ describe("Config Loader", () => {
185193
expect(config.telegram.botToken).toBe("global-bot");
186194
} finally {
187195
fs.rmSync(tempDir, { recursive: true, force: true });
188-
fs.rmSync(path.join(globalConfigDir, ".everynotify.json"), {
189-
force: true,
190-
});
191196
}
192197
});
193198

@@ -247,9 +252,6 @@ describe("Config Loader", () => {
247252
expect(config.pushover.enabled).toBe(false);
248253
} finally {
249254
fs.rmSync(tempDir, { recursive: true, force: true });
250-
fs.rmSync(path.join(globalConfigDir, ".everynotify.json"), {
251-
force: true,
252-
});
253255
}
254256
});
255257

src/__tests__/integration.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ import {
1717
beforeEach,
1818
beforeAll,
1919
afterAll,
20+
spyOn,
2021
} from "bun:test";
2122
import * as fs from "fs";
2223
import * as path from "path";
2324
import * as os from "os";
2425
import type { NotificationPayload } from "../types";
2526

2627
let tempDir: string;
28+
let fakeHomeDir: string;
29+
let homedirSpy: ReturnType<typeof spyOn>;
2730
let mockPushoverSend: any;
2831
let mockTelegramSend: any;
2932
let mockSlackSend: any;
@@ -32,6 +35,9 @@ let EverynotifyPlugin: any;
3235

3336
describe("EverynotifyPlugin Integration", () => {
3437
beforeAll(async () => {
38+
fakeHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), "everynotify-home-"));
39+
homedirSpy = spyOn(os, "homedir").mockReturnValue(fakeHomeDir);
40+
3541
tempDir = fs.mkdtempSync(
3642
path.join(os.tmpdir(), "everynotify-integration-"),
3743
);
@@ -99,9 +105,13 @@ describe("EverynotifyPlugin Integration", () => {
99105
});
100106

101107
afterAll(() => {
108+
homedirSpy.mockRestore();
102109
if (tempDir) {
103110
fs.rmSync(tempDir, { recursive: true, force: true });
104111
}
112+
if (fakeHomeDir) {
113+
fs.rmSync(fakeHomeDir, { recursive: true, force: true });
114+
}
105115
});
106116

107117
beforeEach(() => {

0 commit comments

Comments
 (0)