Files
RedditBot/tests/config-persistence.test.ts
T

98 lines
3.8 KiB
TypeScript

import Database from 'better-sqlite3';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { ConfigStore } from '../src/config/persistence.js';
import { loadConfig } from '../src/config/index.js';
import { runMigrations } from '../src/database/client.js';
import { getTestConfig } from './testUtils.js';
describe('ConfigStore', () => {
let db: Database.Database;
let store: ConfigStore;
beforeEach(() => {
db = new Database(':memory:');
runMigrations(db);
store = new ConfigStore(db);
});
afterEach(() => db.close());
it('round-trips application configuration through SQLite', () => {
const config = getTestConfig({
rateLimit: { minDelayBetweenMessagesMs: 42, maxMessagesPerHour: 7, jitterMs: 3 },
});
store.save(config);
const loaded = store.load({
OPENROUTER_API_KEY: 'environment-key',
WEB_DASHBOARD_TOKEN: 'environment-dashboard-token',
DATABASE_PATH: './data/from-environment.db',
});
expect(loaded?.rateLimit).toEqual(config.rateLimit);
expect(loaded?.openrouter.apiKey).toBe('environment-key');
expect(loaded?.web.authToken).toBe('environment-dashboard-token');
expect(loaded?.database.path).toBe('./data/from-environment.db');
});
it('never stores secrets in the database record', () => {
const config = getTestConfig();
config.openrouter.apiKey = 'database-must-not-contain-this';
config.web.authToken = 'also-secret';
config.web.agentReadToken = 'read-agent-secret';
config.web.agentControlToken = 'control-agent-secret';
store.save(config);
const row = db.prepare('SELECT config_json FROM app_config WHERE id = 1').get() as { config_json: string };
expect(row.config_json).not.toContain('database-must-not-contain-this');
expect(row.config_json).not.toContain('also-secret');
expect(row.config_json).not.toContain('read-agent-secret');
expect(row.config_json).not.toContain('control-agent-secret');
expect(JSON.parse(row.config_json)).not.toHaveProperty('database');
});
it('updates the singleton record instead of appending versions', () => {
store.save(getTestConfig());
store.save(getTestConfig({ browser: { sessionDir: './data/browser-session', dryRun: true } }));
expect(db.prepare('SELECT COUNT(*) AS count FROM app_config').get()).toEqual({ count: 1 });
expect(store.load({
OPENROUTER_API_KEY: 'key',
WEB_DASHBOARD_TOKEN: 'dashboard-token',
})?.browser.dryRun).toBe(true);
});
it('imports legacy YAML once and then starts without the file', () => {
const originalDirectory = process.cwd();
const tempDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'redditbot-config-import-'));
const previousApiKey = process.env.OPENROUTER_API_KEY;
const previousDashboardToken = process.env.WEB_DASHBOARD_TOKEN;
try {
process.chdir(tempDirectory);
process.env.OPENROUTER_API_KEY = 'environment-key';
process.env.WEB_DASHBOARD_TOKEN = 'environment-dashboard-token';
fs.writeFileSync('config.yaml', `
reddit:
subreddits: [example]
openrouter:
imageAnalysisPrompt: analyze
messageGenerationPrompt: compose
`);
expect(loadConfig(store).reddit.subreddits[0].name).toBe('example');
fs.rmSync('config.yaml');
expect(loadConfig(store).reddit.subreddits[0].name).toBe('example');
} finally {
process.chdir(originalDirectory);
fs.rmSync(tempDirectory, { recursive: true, force: true });
if (previousApiKey === undefined) delete process.env.OPENROUTER_API_KEY;
else process.env.OPENROUTER_API_KEY = previousApiKey;
if (previousDashboardToken === undefined) delete process.env.WEB_DASHBOARD_TOKEN;
else process.env.WEB_DASHBOARD_TOKEN = previousDashboardToken;
}
});
});