import { describe, it, expect } from 'vitest'; import { execFileSync } from 'node:child_process'; import { mkdtempSync, copyFileSync, readFileSync, statSync, existsSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { parse as parseYaml } from 'yaml'; const REPO = resolve(import.meta.dirname, '..'); const SETUP = join(REPO, 'scripts', 'setup.mjs'); // Run the wizard in --yes mode inside `dir`. extraArgs lets a case add --force. function runYes(dir, env, extraArgs = []) { execFileSync('node', [SETUP, '--yes', ...extraArgs], { cwd: dir, env: { ...process.env, ...env }, stdio: 'pipe', }); } describe('setup.mjs --yes smoke', () => { it('writes a minimal config.yaml (0600) and .env PORT for a direct setup', () => { const dir = mkdtempSync(join(tmpdir(), 'setup-smoke-')); copyFileSync(join(REPO, 'config.yaml.example'), join(dir, 'config.yaml.example')); runYes(dir, { SETUP_LLM_ENDPOINT: 'http://localhost:11434/v1', SETUP_MODEL: 'qwen3:32b', PORT: '9876' }); const cfg = parseYaml(readFileSync(join(dir, 'config.yaml'), 'utf-8')); expect(cfg.config_version).toBe(2); expect(cfg.llm.workers).toHaveLength(1); expect(cfg.llm.workers[0]).toMatchObject({ connection_type: 'direct', endpoint: 'http://localhost:11434/v1', model: 'qwen3:32b' }); expect(readFileSync(join(dir, '.env'), 'utf-8')).toContain('PORT=9876'); if (process.platform !== 'win32') { expect((statSync(join(dir, 'config.yaml')).mode & 0o777).toString(8)).toBe('600'); } }); it('writes api_key for a gateway setup and backs up an existing config (with --force)', () => { const dir = mkdtempSync(join(tmpdir(), 'setup-smoke-')); copyFileSync(join(REPO, 'config.yaml.example'), join(dir, 'config.yaml')); // pre-existing -> forces backup runYes(dir, { SETUP_CONNECTION_TYPE: 'aao_gateway', SETUP_LLM_ENDPOINT: 'http://g:9876/v1', SETUP_LLM_API_KEY: 'sk-aao-secret123', SETUP_MODEL: 'm', }, ['--force']); const cfg = parseYaml(readFileSync(join(dir, 'config.yaml'), 'utf-8')); expect(cfg.llm.workers[0].api_key).toBe('sk-aao-secret123'); expect(existsSync(join(dir, 'config.yaml.bak'))).toBe(true); if (process.platform !== 'win32') { expect((statSync(join(dir, 'config.yaml.bak')).mode & 0o777).toString(8)).toBe('600'); } }); it('exits non-zero when required env is missing', () => { const dir = mkdtempSync(join(tmpdir(), 'setup-smoke-')); expect(() => runYes(dir, { SETUP_LLM_ENDPOINT: 'http://h/v1' /* no SETUP_MODEL */ })).toThrow(); }); it('refuses to overwrite an existing config without --force', () => { const dir = mkdtempSync(join(tmpdir(), 'setup-smoke-')); copyFileSync(join(REPO, 'config.yaml.example'), join(dir, 'config.yaml')); expect(() => runYes(dir, { SETUP_LLM_ENDPOINT: 'http://h/v1', SETUP_MODEL: 'm' })).toThrow(); }); });