397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
85 lines
3.7 KiB
JavaScript
85 lines
3.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import {
|
|
parsePrompt, detectQueen, isDiscussion, buildDirective, buildHookOutput,
|
|
} from './ruflo-queen-hook.mjs';
|
|
|
|
const HOOK = fileURLToPath(new URL('./ruflo-queen-hook.mjs', import.meta.url));
|
|
|
|
/** Run the hook as a subprocess with the given stdin; return stdout. */
|
|
function runHook(stdinJson) {
|
|
return execFileSync(process.execPath, [HOOK], { input: stdinJson, encoding: 'utf8' });
|
|
}
|
|
|
|
test('detectQueen: matches English "queen" as a whole word', () => {
|
|
assert.equal(detectQueen('queen, fix the bug'), true);
|
|
});
|
|
test('detectQueen: matches Russian "королеву" (case form)', () => {
|
|
assert.equal(detectQueen('сделай это через королеву'), true);
|
|
});
|
|
test('detectQueen: case-insensitive (КОРОЛЕВА)', () => {
|
|
assert.equal(detectQueen('КОРОЛЕВА, подними рой'), true);
|
|
});
|
|
test('detectQueen: word-boundary — "queens" is NOT a match', () => {
|
|
assert.equal(detectQueen('several queens'), false);
|
|
});
|
|
test('detectQueen: no trigger — "очередь" is not "королева"', () => {
|
|
assert.equal(detectQueen('почини очередь задач'), false);
|
|
});
|
|
test('detectQueen: non-string input -> false', () => {
|
|
assert.equal(detectQueen(123), false);
|
|
});
|
|
|
|
test('isDiscussion: "что такое queen?" is meta-discussion', () => {
|
|
assert.equal(isDiscussion('что такое queen?'), true);
|
|
});
|
|
test('isDiscussion: "как работает триггер queen" is meta-discussion', () => {
|
|
assert.equal(isDiscussion('как работает триггер queen'), true);
|
|
});
|
|
test('isDiscussion: "не используй queen сейчас" is meta-discussion', () => {
|
|
assert.equal(isDiscussion('не используй queen сейчас'), true);
|
|
});
|
|
test('isDiscussion: a real queen task is NOT discussion (even a question)', () => {
|
|
assert.equal(detectQueen('queen, почему падает тест?'), true);
|
|
assert.equal(isDiscussion('queen, почему падает тест?'), false);
|
|
});
|
|
test('isDiscussion: no trigger -> false', () => {
|
|
assert.equal(isDiscussion('обычная задача без триггера'), false);
|
|
});
|
|
test('isDiscussion: "например" does NOT falsely match the "пример" guard', () => {
|
|
assert.equal(detectQueen('например queen запустит рой'), true);
|
|
assert.equal(isDiscussion('например queen запустит рой'), false);
|
|
});
|
|
|
|
test('buildDirective: contains §14 reference and the spawn command', () => {
|
|
const d = buildDirective();
|
|
assert.ok(d.includes('Pravila §14'));
|
|
assert.ok(d.includes('hive-mind spawn --claude'));
|
|
});
|
|
|
|
test('buildHookOutput: correct UserPromptSubmit payload shape', () => {
|
|
const o = buildHookOutput('some context');
|
|
assert.equal(o.hookSpecificOutput.hookEventName, 'UserPromptSubmit');
|
|
assert.equal(o.hookSpecificOutput.additionalContext, 'some context');
|
|
});
|
|
|
|
test('parsePrompt: extracts the prompt field', () => {
|
|
assert.equal(parsePrompt('{"prompt":"hello"}'), 'hello');
|
|
});
|
|
test('parsePrompt: empty string on invalid JSON (fail-open)', () => {
|
|
assert.equal(parsePrompt('{битый json'), '');
|
|
});
|
|
|
|
test('main: queen prompt -> stdout carries the directive', () => {
|
|
const out = runHook(JSON.stringify({ prompt: 'queen, do the thing' }));
|
|
assert.ok(out.includes('RUFLO QUEEN TRIGGER'));
|
|
});
|
|
test('main: non-queen prompt -> empty stdout', () => {
|
|
assert.equal(runHook(JSON.stringify({ prompt: 'just a normal task' })).trim(), '');
|
|
});
|
|
test('main: broken stdin -> empty stdout, exit 0 (fail-open)', () => {
|
|
assert.equal(runHook('{not json').trim(), '');
|
|
});
|