65 lines
2.7 KiB
JavaScript
65 lines
2.7 KiB
JavaScript
import { describe, test, afterEach } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
// M0-8 / SC7: the UserPromptSubmit hook must point the voice-profile hint at the
|
|
// REAL artefact in the EXTERNAL data root (getDataRoot('voice-samples')), not the
|
|
// in-plugin placeholder. This hook is a procedural stdin->stdout CLI with no
|
|
// exports, so we test it end-to-end as a subprocess: pipe the prompt JSON, stub
|
|
// LINKEDIN_STUDIO_DATA + STATE_FILE, and inspect the injected systemMessage.
|
|
const hookPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'user-prompt-context.mjs');
|
|
|
|
function runHook(prompt, env) {
|
|
const out = execFileSync('node', [hookPath], {
|
|
input: JSON.stringify({ prompt }),
|
|
env: { ...process.env, ...env },
|
|
encoding: 'utf-8',
|
|
});
|
|
return JSON.parse(out);
|
|
}
|
|
|
|
describe('user-prompt-context — voice hint points at external data root (M0-8/SC7)', () => {
|
|
let dataDir;
|
|
|
|
afterEach(() => {
|
|
if (dataDir && existsSync(dataDir)) rmSync(dataDir, { recursive: true, force: true });
|
|
dataDir = undefined;
|
|
});
|
|
|
|
test('real external voice present → systemMessage references the external voice file', () => {
|
|
dataDir = mkdtempSync(join(tmpdir(), 'lis-upc-'));
|
|
mkdirSync(join(dataDir, 'voice-samples'), { recursive: true });
|
|
writeFileSync(join(dataDir, 'voice-samples', 'authentic-voice-samples.md'), '# My Voice\n\nreal content\n', 'utf-8');
|
|
|
|
const result = runHook('write a linkedin post', {
|
|
LINKEDIN_STUDIO_DATA: dataDir,
|
|
STATE_FILE: join(dataDir, 'no-state.local.md'),
|
|
});
|
|
|
|
assert.ok(result.systemMessage, 'a LinkedIn prompt must produce an enrichment systemMessage');
|
|
assert.ok(
|
|
result.systemMessage.includes(join(dataDir, 'voice-samples', 'authentic-voice-samples.md')),
|
|
'voice hint must point at the external voice file, not the in-plugin placeholder',
|
|
);
|
|
});
|
|
|
|
test('external voice absent → no voice-profile hint injected (graceful degradation)', () => {
|
|
dataDir = mkdtempSync(join(tmpdir(), 'lis-upc-'));
|
|
// no voice-samples dir/file seeded under the external root
|
|
|
|
const result = runHook('write a linkedin post', {
|
|
LINKEDIN_STUDIO_DATA: dataDir,
|
|
STATE_FILE: join(dataDir, 'no-state.local.md'),
|
|
});
|
|
|
|
assert.ok(result.systemMessage, 'still enriches (scorecard reminder), just without the voice hint');
|
|
assert.ok(
|
|
!result.systemMessage.includes('Voice Profile:'),
|
|
'no voice hint when the external voice artefact is absent',
|
|
);
|
|
});
|
|
});
|