timmy-talking-turd/tests/inference-zero-call.test.js
Timmy 9118b69057
All checks were successful
Quality gates / quality (pull_request) Successful in 1m58s
feat: graceful shutdown, disconnect-driven cancellation, and queue acceptance coverage
- server.mjs tracks in-flight chats: browser disconnects abort the
  queued turn (releasing its slot), and SIGTERM/SIGINT cancels every
  in-flight turn so no Hermes subprocess is orphaned.
- vision-service forwards a per-request AbortSignal combined with the
  provider deadline via AbortSignal.any.
- acceptance suites cover overload fallback, urgent bypass under
  saturation, disconnect cleanup, recovery, and shutdown orphan checks.
- zero-call invariant tests prove urgent text never reaches Hermes or
  the vision provider under load.
2026-08-22 23:01:20 +00:00

109 lines
4.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { createHermesAgentService, resolveHermesAgentConfig } from '../src/hermes-agent-service.js';
import { analyzePhoto } from '../src/vision-service.js';
import { OverloadError, createInferenceQueue } from '../src/inference-queue.js';
const origin = 'http://127.0.0.1:4173';
const accessCode = 'test-agent-access-code-2026';
const configured = () => resolveHermesAgentConfig({
TIMMY_AGENT_ENABLED: 'true',
TIMMY_AGENT_ACCESS_TOKEN: accessCode,
TIMMY_PUBLIC_ORIGIN: origin,
TIMMY_AGENT_WORKDIR: '/tmp/timmy-agent-workspace',
});
function deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
return { promise, resolve, reject };
}
test('urgent messages intercept before Hermes even when the queue is saturated', async () => {
let clock = 0;
let hermesCalls = 0;
const gate = deferred();
const service = createHermesAgentService({
config: { ...configured(), maxQueueDepth: 0 },
randomToken: () => 'urgent-cookie',
now: () => clock,
runTurn: async () => {
hermesCalls += 1;
return gate.promise;
},
});
await service.unlock({ origin, accessCode });
// Saturate the single slot so any queued path would be rejected as overloaded.
const active = service.chat({ origin, cookieToken: 'urgent-cookie', payload: { message: 'normal question', ledger: [] } });
await new Promise(resolve => setImmediate(resolve));
assert.equal(hermesCalls, 1);
const urgent = await service.chat({ origin, cookieToken: 'urgent-cookie', payload: { message: 'I have rectal bleeding', ledger: [] } });
assert.equal(hermesCalls, 1, 'Hermes was called exactly once — only for the non-urgent request');
assert.equal(urgent.safetyOverride, true);
assert.match(urgent.reply, /medical help/i);
gate.resolve({ reply: 'late', sessionId: 'urgent_session_01' });
assert.match((await active).reply, /late/);
});
test('vision analysis is bounded by the shared queue and overload returns the manual-fallback copy', async () => {
let clock = 0;
let providerCalls = 0;
const gates = [];
const queue = createInferenceQueue({ concurrency: 1, maxQueueDepth: 1, requestTimeoutMs: 60_000, now: () => clock });
const visionFetch = async (url, options) => {
providerCalls += 1;
const gate = deferred();
gates.push(gate);
options.signal?.addEventListener('abort', () => gate.reject(new Error('aborted')));
return new Promise((resolve, reject) => {
gate.promise.then(() => resolve({ ok: true, json: async () => ({ choices: [{ message: { content: JSON.stringify({ isStool: true, bristolType: 4, color: 'brown', confidence: 0.8, imageQuality: 'good', observations: 'ok' }) } }] }) }), reject);
});
};
const payload = { imageDataUrl: 'data:image/jpeg;base64,YQ==', consent: true };
const config = { baseUrl: 'http://127.0.0.1:8645/v1', apiKey: 'test-key-not-real', model: 'm' };
const first = queue.run(signal => analyzePhoto({ payload, fetchImpl: visionFetch, config, signal }));
while (providerCalls < 1) await Promise.resolve();
const second = queue.run(signal => analyzePhoto({ payload, fetchImpl: visionFetch, config, signal }));
await Promise.resolve();
await assert.rejects(() => queue.run((signal) => analyzePhoto({ payload, fetchImpl: visionFetch, config, signal })), error => {
assert.ok(error instanceof OverloadError);
assert.match(error.message, /continue manually/i);
assert.doesNotMatch(error.message, /api|key|token|127\.0\.0\.1|fetch/i);
return true;
}, 'third request beyond depth is overloaded with sanitized copy');
assert.equal(providerCalls, 1, 'overloaded request never reached the provider');
gates[0].resolve('go');
const result = await Promise.race([first, new Promise(resolve => setTimeout(() => resolve('pending'), 100))]);
assert.notEqual(result, 'pending');
});
test('vision requests carry cancellation so disconnected clients release provider work', async () => {
let capturedSignal;
const controller = new AbortController();
const gate = deferred();
const fetchImpl = async (url, options) => {
capturedSignal = options.signal;
options.signal?.addEventListener('abort', () => gate.reject(new Error('aborted')));
return gate.promise;
};
const payload = { imageDataUrl: 'data:image/jpeg;base64,YQ==', consent: true };
const config = { baseUrl: 'http://127.0.0.1:8645/v1', apiKey: 'test-key-not-real', model: 'm' };
const attempt = analyzePhoto({ payload, fetchImpl, config, signal: controller.signal });
while (!capturedSignal) await Promise.resolve();
controller.abort(new Error('client disconnected'));
await assert.rejects(() => attempt, /continue manually|unavailable/i);
assert.equal(capturedSignal.aborted, true, 'the provider fetch observed the cancellation');
});