config-loader.test.ts

 1import { describe, test, expect, beforeEach, afterEach } from "bun:test";
 2import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from "node:fs";
 3import { tmpdir } from "node:os";
 4import { join } from "node:path";
 5import { ConfigError } from "../src/util/errors.js";
 6import { loadConfig } from "../src/config/loader.js";
 7
 8describe("loadConfig - ConfigError rethrown directly (issue #10)", () => {
 9  let configDir: string;
10  let configPath: string;
11  const originalEnv = { ...process.env };
12
13  beforeEach(() => {
14    configDir = mkdtempSync(join(tmpdir(), "rumilo-cfg-test10-"));
15    const xdgBase = join(configDir, "xdg");
16    const rumiloDir = join(xdgBase, "rumilo");
17    mkdirSync(rumiloDir, { recursive: true });
18    configPath = join(rumiloDir, "config.toml");
19    process.env["XDG_CONFIG_HOME"] = xdgBase;
20  });
21
22  afterEach(() => {
23    process.env = { ...originalEnv };
24    try {
25      rmSync(configDir, { recursive: true, force: true });
26    } catch {}
27  });
28
29  test("ConfigError from validation is rethrown with original message and stack", async () => {
30    // Write invalid config that triggers ConfigError from validatePartialConfig
31    writeFileSync(configPath, `[defaults]\nmodel = 42\n`);
32    try {
33      await loadConfig();
34      throw new Error("should have thrown");
35    } catch (e: any) {
36      expect(e).toBeInstanceOf(ConfigError);
37      // The original message should include the validation details, not be re-wrapped
38      expect(e.message).toContain("/defaults/model");
39      // Stack should reference the validation function, not be a generic re-wrap
40      expect(e.stack).toBeDefined();
41    }
42  });
43
44  test("TOML parse error is wrapped as ConfigError with original message", async () => {
45    writeFileSync(configPath, `[invalid toml !!!`);
46    try {
47      await loadConfig();
48      throw new Error("should have thrown");
49    } catch (e: any) {
50      expect(e).toBeInstanceOf(ConfigError);
51      // Should contain the original TOML parse error message
52      expect(e.message.length).toBeGreaterThan(0);
53    }
54  });
55});