impeccable-paths.test.mjs

  1/**
  2 * Tests for project-local Impeccable path resolution.
  3 * Run with: node --test tests/impeccable-paths.test.mjs
  4 */
  5
  6import { describe, it, beforeEach, afterEach } from 'node:test';
  7import assert from 'node:assert/strict';
  8import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
  9import { join } from 'node:path';
 10import { tmpdir } from 'node:os';
 11
 12import {
 13  getDesignSidecarPath,
 14  getLegacyLiveServerPath,
 15  getLiveAnnotationsDir,
 16  getLiveConfigPath,
 17  getLiveServerPath,
 18  getLiveSessionsDir,
 19  readLiveServerInfo,
 20  resolveDesignSidecarPath,
 21  resolveLiveConfigPath,
 22} from '../skill/scripts/impeccable-paths.mjs';
 23
 24describe('impeccable project paths', () => {
 25  let tmp;
 26
 27  beforeEach(() => {
 28    tmp = mkdtempSync(join(tmpdir(), 'impeccable-paths-'));
 29  });
 30
 31  afterEach(() => {
 32    rmSync(tmp, { recursive: true, force: true });
 33  });
 34
 35  it('resolves the generated design sidecar under .impeccable', () => {
 36    assert.equal(getDesignSidecarPath(tmp), join(tmp, '.impeccable', 'design.json'));
 37
 38    mkdirSync(join(tmp, '.impeccable'), { recursive: true });
 39    writeFileSync(join(tmp, 'DESIGN.json'), '{"source":"legacy"}');
 40    writeFileSync(getDesignSidecarPath(tmp), '{"source":"new"}');
 41
 42    assert.equal(resolveDesignSidecarPath(tmp), getDesignSidecarPath(tmp));
 43  });
 44
 45  it('falls back to legacy root DESIGN.json when the new sidecar is missing', () => {
 46    const legacyPath = join(tmp, 'DESIGN.json');
 47    writeFileSync(legacyPath, '{"source":"legacy"}');
 48
 49    assert.equal(resolveDesignSidecarPath(tmp), legacyPath);
 50  });
 51
 52  it('uses .impeccable/live/config.json as the default live config path', () => {
 53    assert.equal(resolveLiveConfigPath({ cwd: tmp, scriptsDir: join(tmp, 'scripts'), env: {} }), getLiveConfigPath(tmp));
 54  });
 55
 56  it('falls back to legacy scripts/config.json when no new live config exists', () => {
 57    const scriptsDir = join(tmp, 'skills', 'impeccable', 'scripts');
 58    mkdirSync(scriptsDir, { recursive: true });
 59    const legacyConfig = join(scriptsDir, 'config.json');
 60    writeFileSync(legacyConfig, '{"files":["index.html"]}');
 61
 62    assert.equal(resolveLiveConfigPath({ cwd: tmp, scriptsDir, env: {} }), legacyConfig);
 63  });
 64
 65  it('lets IMPECCABLE_LIVE_CONFIG override both new and legacy config locations', () => {
 66    const override = join(tmp, 'custom-live-config.json');
 67    mkdirSync(join(tmp, '.impeccable', 'live'), { recursive: true });
 68    writeFileSync(getLiveConfigPath(tmp), '{"source":"new"}');
 69    writeFileSync(override, '{"source":"override"}');
 70
 71    assert.equal(
 72      resolveLiveConfigPath({ cwd: tmp, scriptsDir: join(tmp, 'scripts'), env: { IMPECCABLE_LIVE_CONFIG: 'custom-live-config.json' } }),
 73      override,
 74    );
 75  });
 76
 77  it('places live server, session, and annotation state under .impeccable/live', () => {
 78    assert.equal(getLiveServerPath(tmp), join(tmp, '.impeccable', 'live', 'server.json'));
 79    assert.equal(getLiveSessionsDir(tmp), join(tmp, '.impeccable', 'live', 'sessions'));
 80    assert.equal(getLiveAnnotationsDir(tmp), join(tmp, '.impeccable', 'live', 'annotations'));
 81  });
 82
 83  it('reads new live server state before legacy recovery state', () => {
 84    mkdirSync(join(tmp, '.impeccable', 'live'), { recursive: true });
 85    writeFileSync(getLiveServerPath(tmp), JSON.stringify({ port: 8401, token: 'new' }));
 86    writeFileSync(getLegacyLiveServerPath(tmp), JSON.stringify({ port: 8400, token: 'legacy' }));
 87
 88    const record = readLiveServerInfo(tmp);
 89    assert.equal(record.path, getLiveServerPath(tmp));
 90    assert.equal(record.info.token, 'new');
 91  });
 92
 93  it('reads legacy live server state when the new state file is absent', () => {
 94    writeFileSync(getLegacyLiveServerPath(tmp), JSON.stringify({ port: 8400, token: 'legacy' }));
 95
 96    const record = readLiveServerInfo(tmp);
 97    assert.equal(record.path, getLegacyLiveServerPath(tmp));
 98    assert.equal(record.info.token, 'legacy');
 99  });
100});