linkedin-studio/hooks/scripts/__tests__/user-prompt-context.test.mjs

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',
);
});
});