ktg-plugin-marketplace/plugins/ms-ai-architect/tests/kb-update/test-template-generation.test.mjs

98 lines
4.3 KiB
JavaScript

// tests/kb-update/test-template-generation.test.mjs
// Structural-regex tests for scripts/kb-update/templates/* (Step 8).
// Verifies that each template file exists, contains the documented sentinel
// strings, and exposes the documented placeholder set. No template execution
// or real scheduling occurs in this test — that lives in Wave 6 live-test.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, existsSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const TEMPLATES_DIR = join(__dirname, '..', '..', 'scripts', 'kb-update', 'templates');
const PLIST = join(TEMPLATES_DIR, 'com.fromaitochitta.ms-ai-architect.kb-update.plist');
const SERVICE = join(TEMPLATES_DIR, 'ms-ai-architect-kb-update.service');
const TIMER = join(TEMPLATES_DIR, 'ms-ai-architect-kb-update.timer');
const PS1 = join(TEMPLATES_DIR, 'ms-ai-architect-kb-update.ps1');
const README = join(TEMPLATES_DIR, 'README.md');
function readTpl(p) {
assert.equal(existsSync(p), true, `template missing: ${p}`);
return readFileSync(p, 'utf8');
}
test('plist — exists with required keys and placeholders', () => {
const content = readTpl(PLIST);
assert.match(content, /<key>Label<\/key>/);
assert.match(content, /<key>StartCalendarInterval<\/key>/);
assert.match(content, /<key>ProgramArguments<\/key>/);
assert.match(content, /<key>StandardOutPath<\/key>/);
assert.match(content, /<key>StandardErrorPath<\/key>/);
assert.match(content, /<key>EnvironmentVariables<\/key>/);
assert.match(content, /<key>RunAtLoad<\/key>\s*<false\/>/);
assert.match(content, /\{\{NODE_BIN\}\}/);
assert.match(content, /\{\{PLUGIN_ROOT\}\}/);
assert.match(content, /\{\{LOG_FILE\}\}/);
assert.match(content, /\{\{SCHEDULE_HOUR\}\}/);
assert.match(content, /\{\{SCHEDULE_MINUTE\}\}/);
assert.match(content, /\{\{SCHEDULE_DAY_OF_WEEK\}\}/);
});
test('systemd .timer — exists with OnCalendar and Persistent', () => {
const content = readTpl(TIMER);
assert.match(content, /\[Unit\]/);
assert.match(content, /\[Timer\]/);
assert.match(content, /\[Install\]/);
assert.match(content, /OnCalendar=Wed/);
assert.match(content, /Persistent=true/);
assert.match(content, /WantedBy=timers\.target/);
});
test('systemd .service — exists with [Unit], [Service] and ExecStart', () => {
const content = readTpl(SERVICE);
assert.match(content, /\[Unit\]/);
assert.match(content, /\[Service\]/);
assert.match(content, /ExecStart=/);
assert.match(content, /\{\{NODE_BIN\}\}/);
assert.match(content, /\{\{PLUGIN_ROOT\}\}/);
});
test('PowerShell ps1 — exists with Register-ScheduledTask and InteractiveToken', () => {
const content = readTpl(PS1);
assert.match(content, /Register-ScheduledTask/);
assert.match(content, /InteractiveToken/);
assert.match(content, /New-ScheduledTaskTrigger/);
assert.match(content, /-Weekly/);
assert.match(content, /-DaysOfWeek\s+Wednesday/);
assert.match(content, /\{\{NODE_BIN\}\}/);
assert.match(content, /\{\{PLUGIN_ROOT\}\}/);
});
test('README — exists and references each template by filename', () => {
const content = readTpl(README);
assert.match(content, /com\.fromaitochitta\.ms-ai-architect\.kb-update\.plist/);
assert.match(content, /ms-ai-architect-kb-update\.service/);
assert.match(content, /ms-ai-architect-kb-update\.timer/);
assert.match(content, /ms-ai-architect-kb-update\.ps1/);
});
test('plist + service + ps1 reference NODE_BIN and PLUGIN_ROOT', () => {
// The .timer is a pure trigger — it activates the .service, which is
// the only systemd unit that needs to know the binary + plugin root.
// launchd and Windows put the command directly in the trigger spec, so
// they need both placeholders themselves.
for (const tpl of [PLIST, SERVICE, PS1]) {
const content = readFileSync(tpl, 'utf8');
assert.match(content, /\{\{NODE_BIN\}\}/, `${tpl} missing NODE_BIN placeholder`);
assert.match(content, /\{\{PLUGIN_ROOT\}\}/, `${tpl} missing PLUGIN_ROOT placeholder`);
}
});
test('.timer is placeholder-free literal (Wed 04:23 hardcoded per plan)', () => {
const content = readFileSync(TIMER, 'utf8');
assert.match(content, /OnCalendar=Wed \*-\*-\* 04:23:00/);
assert.doesNotMatch(content, /\{\{[A-Z_]+\}\}/);
});