const { test, beforeEach, afterEach } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const config = require('../src/config'); const ENV_KEYS = [ 'STOKE_CONFIG_DIR', 'STOKE_CONFIG_FILE', 'FORGEJO_CONFIG_DIR', 'FORGEJO_CONFIG_FILE', 'XDG_CONFIG_HOME', ]; let savedEnv; let tmpDir; beforeEach(() => { savedEnv = {}; for (const key of ENV_KEYS) { savedEnv[key] = process.env[key]; delete process.env[key]; } tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-test-')); }); afterEach(() => { for (const key of ENV_KEYS) { if (savedEnv[key] === undefined) delete process.env[key]; else process.env[key] = savedEnv[key]; } fs.rmSync(tmpDir, { recursive: true, force: true }); }); test('default config path lives under ~/.config/stoke', () => { assert.equal(config.getConfigPath(), path.join(os.homedir(), '.config', 'stoke', 'config.json')); }); test('XDG_CONFIG_HOME replaces ~/.config entirely', () => { process.env.XDG_CONFIG_HOME = tmpDir; assert.equal(config.getConfigPath(), path.join(tmpDir, 'stoke', 'config.json')); }); test('STOKE_CONFIG_FILE overrides everything', () => { process.env.XDG_CONFIG_HOME = '/elsewhere'; process.env.STOKE_CONFIG_FILE = path.join(tmpDir, 'custom.json'); assert.equal(config.getConfigPath(), path.join(tmpDir, 'custom.json')); }); test('config path is resolved lazily (STOKE_CONFIG_FILE set after require)', () => { const first = path.join(tmpDir, 'a.json'); const second = path.join(tmpDir, 'b.json'); process.env.STOKE_CONFIG_FILE = first; assert.equal(config.getConfigPath(), first); process.env.STOKE_CONFIG_FILE = second; assert.equal(config.getConfigPath(), second); }); test('save/load/clear round-trip with restrictive permissions', () => { process.env.STOKE_CONFIG_FILE = path.join(tmpDir, 'nested', 'config.json'); assert.equal(config.loadConfig(), null); const data = { url: 'https://example.test', token: 'abc', tokenId: 1 }; config.saveConfig(data); assert.deepEqual(config.loadConfig(), data); if (process.platform !== 'win32') { const mode = fs.statSync(config.getConfigPath()).mode & 0o777; assert.equal(mode, 0o600); } config.clearConfig(); assert.equal(config.loadConfig(), null); // clearing twice must not throw config.clearConfig(); }); test('loadConfig surfaces corrupt config files as errors', () => { process.env.STOKE_CONFIG_FILE = path.join(tmpDir, 'corrupt.json'); fs.writeFileSync(process.env.STOKE_CONFIG_FILE, 'not-json'); assert.throws(() => config.loadConfig(), /Failed to read config/); });