Files
brain/tools/ruflo-recall-hook.test.mjs
T

72 lines
2.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
parsePrompt, extractJson, formatRecall, buildHookOutput, rufloBinCandidates,
} from './ruflo-recall-hook.mjs';
test('parsePrompt: extracts the prompt field', () => {
assert.equal(parsePrompt('{"prompt":"hello world"}'), 'hello world');
});
test('parsePrompt: empty string when field is missing', () => {
assert.equal(parsePrompt('{"other":1}'), '');
});
test('parsePrompt: empty string on invalid JSON (fail-open)', () => {
assert.equal(parsePrompt('not json at all'), '');
});
test('parsePrompt: empty string when prompt is not a string', () => {
assert.equal(parsePrompt('{"prompt":123}'), '');
});
test('extractJson: pulls the JSON object out of noisy ruflo stdout', () => {
const stdout =
'[INFO] Searching: "x" (semantic)\n\n' +
'✅ Using sql.js\n' +
'{\n "query": "x",\n "results": []\n}\n';
const obj = extractJson(stdout);
assert.equal(obj.query, 'x');
assert.deepEqual(obj.results, []);
});
test('extractJson: null when there is no JSON', () => {
assert.equal(extractJson('[INFO] only noise, no braces'), null);
});
test('extractJson: null on malformed JSON (fail-open)', () => {
assert.equal(extractJson('noise {not: valid'), null);
});
test('formatRecall: empty string when there are no results', () => {
assert.equal(formatRecall({ results: [] }), '');
assert.equal(formatRecall(null), '');
assert.equal(formatRecall({}), '');
});
test('formatRecall: formats at most 3 results and tags the block', () => {
const out = formatRecall({
results: [
{ key: 'k1', preview: 'p1' },
{ key: 'k2', preview: 'p2' },
{ key: 'k3', preview: 'p3' },
{ key: 'k4', preview: 'p4' },
],
});
assert.ok(out.includes('k1') && out.includes('k3'));
assert.ok(!out.includes('k4'));
assert.ok(out.includes('ruflo memory recall'));
});
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('rufloBinCandidates: RUFLO_RECALL_BIN override wins', () => {
assert.deepEqual(rufloBinCandidates({ RUFLO_RECALL_BIN: 'X:/r.js' }), ['X:/r.js']);
});
test('rufloBinCandidates: builds an APPDATA-based candidate', () => {
const c = rufloBinCandidates({ APPDATA: 'C:/Users/X/AppData/Roaming' });
assert.equal(c.length, 1);
assert.ok(c[0].includes('ruflo') && c[0].endsWith('ruflo.js'));
});
test('rufloBinCandidates: empty array when no env hints', () => {
assert.deepEqual(rufloBinCandidates({}), []);
});