feat: implement Tier 3 features — lane queue, credential redaction, token dashboard, xAI, Voyage AI
- Lane Queue: per-session FIFO queue in gateway replacing reject-when-busy (9 tests) - Credential Redaction: redactConfig() expanded to cover 18+ secret fields (16 tests) - Web UI Token Dashboard: system.tokenUsage endpoint + Usage page with summary cards - xAI (Grok) Provider: OpenAI-compatible client with model pricing - Voyage AI Embeddings: new embedding provider with configurable dimensions (5 tests) - Update gap analysis: 90→95 match (70%→74%), Tier 3 section marked DONE - Update state.json: test count 1001→1034, add tier3_completion entry Total: 1034 tests passing across 85 files, typecheck clean
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { LaneQueue } from './lane-queue.js';
|
||||
|
||||
describe('LaneQueue', () => {
|
||||
it('executes a single item immediately', async () => {
|
||||
const queue = new LaneQueue();
|
||||
const result = await queue.enqueue('lane-a', async () => 42);
|
||||
expect(result).toBe(42);
|
||||
});
|
||||
|
||||
it('serialises items within the same lane', async () => {
|
||||
const queue = new LaneQueue();
|
||||
const order: number[] = [];
|
||||
|
||||
// Create a deferred to control timing
|
||||
let resolveFirst!: () => void;
|
||||
const firstBlocks = new Promise<void>((r) => { resolveFirst = r; });
|
||||
|
||||
const p1 = queue.enqueue('lane-a', async () => {
|
||||
order.push(1);
|
||||
await firstBlocks;
|
||||
order.push(2);
|
||||
return 'first';
|
||||
});
|
||||
|
||||
const p2 = queue.enqueue('lane-a', async () => {
|
||||
order.push(3);
|
||||
return 'second';
|
||||
});
|
||||
|
||||
const p3 = queue.enqueue('lane-a', async () => {
|
||||
order.push(4);
|
||||
return 'third';
|
||||
});
|
||||
|
||||
// Only item 1 should have started
|
||||
expect(order).toEqual([1]);
|
||||
expect(queue.queueLength('lane-a')).toBe(2);
|
||||
expect(queue.isProcessing('lane-a')).toBe(true);
|
||||
|
||||
// Release the first item
|
||||
resolveFirst();
|
||||
const results = await Promise.all([p1, p2, p3]);
|
||||
|
||||
expect(results).toEqual(['first', 'second', 'third']);
|
||||
expect(order).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('runs independent lanes in parallel', async () => {
|
||||
const queue = new LaneQueue();
|
||||
const running: string[] = [];
|
||||
|
||||
let resolveA!: () => void;
|
||||
const blocksA = new Promise<void>((r) => { resolveA = r; });
|
||||
let resolveB!: () => void;
|
||||
const blocksB = new Promise<void>((r) => { resolveB = r; });
|
||||
|
||||
const pA = queue.enqueue('lane-a', async () => {
|
||||
running.push('a-start');
|
||||
await blocksA;
|
||||
running.push('a-end');
|
||||
return 'A';
|
||||
});
|
||||
|
||||
const pB = queue.enqueue('lane-b', async () => {
|
||||
running.push('b-start');
|
||||
await blocksB;
|
||||
running.push('b-end');
|
||||
return 'B';
|
||||
});
|
||||
|
||||
// Both should have started concurrently
|
||||
// Wait a tick for async execution
|
||||
await new Promise<void>((r) => queueMicrotask(r));
|
||||
expect(running).toContain('a-start');
|
||||
expect(running).toContain('b-start');
|
||||
|
||||
resolveA();
|
||||
resolveB();
|
||||
const [rA, rB] = await Promise.all([pA, pB]);
|
||||
|
||||
expect(rA).toBe('A');
|
||||
expect(rB).toBe('B');
|
||||
});
|
||||
|
||||
it('error in one item does not block the next', async () => {
|
||||
const queue = new LaneQueue();
|
||||
|
||||
let resolveFirst!: () => void;
|
||||
const firstBlocks = new Promise<void>((r) => { resolveFirst = r; });
|
||||
|
||||
const p1 = queue.enqueue('lane-a', async () => {
|
||||
await firstBlocks;
|
||||
throw new Error('boom');
|
||||
});
|
||||
|
||||
const p2 = queue.enqueue('lane-a', async () => 'recovered');
|
||||
|
||||
resolveFirst();
|
||||
|
||||
await expect(p1).rejects.toThrow('boom');
|
||||
const result = await p2;
|
||||
expect(result).toBe('recovered');
|
||||
});
|
||||
|
||||
it('cancel rejects pending items but does not affect active', async () => {
|
||||
const queue = new LaneQueue();
|
||||
|
||||
let resolveFirst!: () => void;
|
||||
const firstBlocks = new Promise<void>((r) => { resolveFirst = r; });
|
||||
|
||||
const p1 = queue.enqueue('lane-a', async () => {
|
||||
await firstBlocks;
|
||||
return 'active';
|
||||
});
|
||||
|
||||
const p2 = queue.enqueue('lane-a', async () => 'pending-1');
|
||||
const p3 = queue.enqueue('lane-a', async () => 'pending-2');
|
||||
|
||||
expect(queue.queueLength('lane-a')).toBe(2);
|
||||
|
||||
// Cancel pending items
|
||||
queue.cancel('lane-a');
|
||||
expect(queue.queueLength('lane-a')).toBe(0);
|
||||
|
||||
// Active work should still complete
|
||||
resolveFirst();
|
||||
const result = await p1;
|
||||
expect(result).toBe('active');
|
||||
|
||||
// Pending items should have been rejected
|
||||
await expect(p2).rejects.toThrow('Lane cancelled');
|
||||
await expect(p3).rejects.toThrow('Lane cancelled');
|
||||
});
|
||||
|
||||
it('reports queue length correctly', async () => {
|
||||
const queue = new LaneQueue();
|
||||
|
||||
expect(queue.queueLength('lane-a')).toBe(0);
|
||||
expect(queue.isProcessing('lane-a')).toBe(false);
|
||||
|
||||
let resolveFirst!: () => void;
|
||||
const firstBlocks = new Promise<void>((r) => { resolveFirst = r; });
|
||||
|
||||
const p1 = queue.enqueue('lane-a', async () => {
|
||||
await firstBlocks;
|
||||
return 'done';
|
||||
});
|
||||
|
||||
// Active work, no pending
|
||||
expect(queue.isProcessing('lane-a')).toBe(true);
|
||||
expect(queue.queueLength('lane-a')).toBe(0);
|
||||
|
||||
const p2 = queue.enqueue('lane-a', async () => 'queued-1');
|
||||
expect(queue.queueLength('lane-a')).toBe(1);
|
||||
|
||||
const p3 = queue.enqueue('lane-a', async () => 'queued-2');
|
||||
expect(queue.queueLength('lane-a')).toBe(2);
|
||||
|
||||
resolveFirst();
|
||||
await Promise.all([p1, p2, p3]);
|
||||
|
||||
// After all done, lane should be cleaned up
|
||||
expect(queue.isProcessing('lane-a')).toBe(false);
|
||||
expect(queue.queueLength('lane-a')).toBe(0);
|
||||
});
|
||||
|
||||
it('cleans up empty lanes after completion', async () => {
|
||||
const queue = new LaneQueue();
|
||||
|
||||
await queue.enqueue('lane-a', async () => 'done');
|
||||
|
||||
// Lane should be cleaned up (isProcessing returns false, queueLength 0)
|
||||
expect(queue.isProcessing('lane-a')).toBe(false);
|
||||
expect(queue.queueLength('lane-a')).toBe(0);
|
||||
});
|
||||
|
||||
it('cancel on non-existent lane is a no-op', () => {
|
||||
const queue = new LaneQueue();
|
||||
// Should not throw
|
||||
queue.cancel('no-such-lane');
|
||||
expect(queue.queueLength('no-such-lane')).toBe(0);
|
||||
});
|
||||
|
||||
it('can enqueue new work after a lane completes', async () => {
|
||||
const queue = new LaneQueue();
|
||||
|
||||
const r1 = await queue.enqueue('lane-a', async () => 'first');
|
||||
expect(r1).toBe('first');
|
||||
|
||||
const r2 = await queue.enqueue('lane-a', async () => 'second');
|
||||
expect(r2).toBe('second');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user