x402-toolpick / tree / test/needlog.test.js ยท raw (this view has old GitHub links rewritten for the mirror; raw is byte-exact)
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { normalizeNeed, classify, NeedLog } from '../src/needlog.js';
import { VERSION_STR } from '../src/version.js';
import { ENGINE_VERSION } from '../src/engine.js';
import { makeApp, BASE } from './helpers.js';

function fakeCounter() {
  const rows = new Map(); const calls = { add: 0 };
  const stub = {
    async addNeeds(p) { calls.add++; for (const [d, qs] of Object.entries(p)) for (const [q, c] of Object.entries(qs)) { const k = `${d}|${q}`; const e = rows.get(k) || { n: 0, no_match: 0, low_match: 0, no_ours: 0 }; for (const f of Object.keys(e)) e[f] += c[f]; rows.set(k, e); } },
    async topNeeds(since) { const m = new Map(); for (const [k, e] of rows) { const [d, q] = k.split('|'); if (d < since) continue; const x = m.get(q) || { q, n: 0, no_match: 0, low_match: 0, no_ours: 0 }; for (const f of ['n', 'no_match', 'low_match', 'no_ours']) x[f] += e[f]; m.set(q, x); } return [...m.values()]; },
  };
  return { idFromName: () => 'id', get: () => stub, rows, calls };
}

test('single version string: 0.1.2 in version.js, engine label and package.json', () => {
  assert.equal(VERSION_STR, '0.1.2');
  assert.equal(ENGINE_VERSION, 'toolpick-0.1.2');
  assert.equal(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version, VERSION_STR);
});

test('normalizeNeed: lowercase, strips emails, URLs, addresses, keys and long numbers, caps at 120', () => {
  assert.equal(normalizeNeed('  Web   SEARCH  '), 'web search');
  assert.equal(normalizeNeed('email alice@example.com about https://evil.example/x?a=1 now'), 'email about now');
  assert.equal(normalizeNeed('check 0x3c1A1E01e790Cd326495631B3b38Bd7068F08d6F balance'), 'check balance');
  assert.equal(normalizeNeed('sol FH73WbbUe2UyNzfy72MJs5nT3NxyDf9GrqE7kzjL2Jte risk'), 'sol risk');
  assert.equal(normalizeNeed('call +1 555 123 4567 or id 123456789'), 'call + or id');
  assert.equal(normalizeNeed('lookup vitalik.eth and www.foo.io/page'), 'lookup and');
  assert.equal(normalizeNeed('key sk_live_ABCDEFGHIJKLMNOPQRSTUVWX12 leak'), 'key leak');
  assert.equal(normalizeNeed('weather in 2026'), 'weather in 2026');
  const long = normalizeNeed('word '.repeat(60));
  assert.ok(long.length <= 120 && !long.endsWith(' '));
  assert.equal(normalizeNeed('a'), '');
});

test('classify: no match, weak match, none of ours', () => {
  assert.deepEqual(classify(null, { code: 'no_matches' }), { no_match: 1, low_match: 0, no_ours: 1 });
  assert.equal(classify(null, { code: 'index_not_ready' }), null);
  assert.deepEqual(classify({ results: [{ relevance: 0.3 }] }), { no_match: 0, low_match: 1, no_ours: 1 });
  assert.deepEqual(classify({ results: [{ relevance: 0.9, ours: true }] }), { no_match: 0, low_match: 0, no_ours: 0 });
});

test('NeedLog batches aggregate counts only and top() applies the min-count floor', async () => {
  const counter = fakeCounter(); let t = Date.parse('2026-10-23T12:00:00Z');
  const log = new NeedLog(counter, { flushMs: 0, now: () => t });
  for (let i = 0; i < 3; i++) log.record('lite', 'Satellite imagery for 0x3c1A1E01e790Cd326495631B3b38Bd7068F08d6F', null, { code: 'no_matches' });
  log.record('pick', 'web search', { results: [{ relevance: 0.9, ours: false }] });
  log.record('market', 'ignored', null, { code: 'no_matches' });
  await new Promise((r) => setTimeout(r, 5));
  const stored = JSON.stringify([...counter.rows]);
  assert.doesNotMatch(stored, /0x3c1a|ip|wallet/i);
  const top = await log.top({});
  assert.deepEqual(top.needs.map((x) => x.need), ['satellite imagery for']);
  assert.equal(top.needs[0].no_match, 3); assert.equal(top.needs[0].unmet, 3);
  assert.equal((await log.top({ minCount: 1 })).needs.length, 2);
});

test('app: paid/preview/MCP queries are logged (smoke excluded); 400 still before the paywall; bare probe 402; /v1/needs free', async () => {
  const counter = fakeCounter(); const needLog = new NeedLog(counter, { flushMs: 0 });
  const { fetchApp } = await makeApp({ appOpts: { needLog } });
  assert.equal((await fetchApp(`${BASE}/v1/lite?need=`)).status, 400);
  assert.equal((await fetchApp(`${BASE}/v1/pick`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{"need":""}' })).status, 400);
  assert.equal((await fetchApp(`${BASE}/v1/lite`)).status, 402);
  assert.equal((await fetchApp(`${BASE}/v1/lite?need=web%20search`)).status, 402); // unpaid: never reaches the engine, not logged
  for (let i = 0; i < 3; i++) await fetchApp(`${BASE}/v1/preview?need=quantum%20weather%20oracle%20zzz`);
  await fetchApp(`${BASE}/v1/preview?need=quantum%20weather%20oracle%20zzz`, { headers: { 'x-toolpick-smoke': '1' } });
  await fetchApp(`${BASE}/mcp`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: 'find_paid_api', arguments: { need: 'web search' } } }) });
  await new Promise((r) => setTimeout(r, 10));
  const top = await needLog.top({ minCount: 1 });
  const q = top.needs.find((x) => x.need === 'quantum weather oracle zzz');
  assert.ok(q, JSON.stringify(top)); assert.equal(q.n, 3); assert.equal(q.no_match, 3);
  const r = await fetchApp(`${BASE}/v1/needs`); assert.equal(r.status, 200);
  const j = await r.json(); assert.equal(j.min_count, 3); assert.equal(j.needs[0].need, 'quantum weather oracle zzz');
  const info = await (await fetchApp(`${BASE}/`, { headers: { accept: 'application/json' } })).json();
  assert.match(info.policy.storage, /anonymized daily aggregate counts/); assert.ok(info.free.includes('/v1/needs'));
});

agentgate-source: code mirror on Cloudflare Pages. All repos