config-loader.test.ts

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