Files
mcpctl/src/mcplocal/tests/pause-queue.test.ts

203 lines
5.9 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, beforeEach } from 'vitest';
import { pauseQueue } from '../src/proxymodel/pause-queue.js';
beforeEach(() => {
// Reset state between tests
pauseQueue.setPaused(false);
});
describe('PauseQueue', () => {
it('returns transformed content immediately when not paused', async () => {
const result = await pauseQueue.enqueue({
sessionId: 's1',
projectName: 'proj',
contentType: 'toolResult',
sourceName: 'test/tool',
original: 'raw content',
transformed: 'processed content',
});
expect(result).toBe('processed content');
expect(pauseQueue.size).toBe(0);
});
it('holds content when paused', async () => {
pauseQueue.setPaused(true);
let resolved = false;
const promise = pauseQueue.enqueue({
sessionId: 's1',
projectName: 'proj',
contentType: 'toolResult',
sourceName: 'test/tool',
original: 'raw',
transformed: 'processed',
}).then((r) => { resolved = true; return r; });
// Should not resolve immediately
await new Promise((r) => setTimeout(r, 50));
expect(resolved).toBe(false);
expect(pauseQueue.size).toBe(1);
// Release it
const items = pauseQueue.getItems();
expect(items).toHaveLength(1);
pauseQueue.releaseOne(items[0]!.id);
const result = await promise;
expect(result).toBe('processed');
expect(resolved).toBe(true);
expect(pauseQueue.size).toBe(0);
});
it('editAndRelease sends edited content', async () => {
pauseQueue.setPaused(true);
const promise = pauseQueue.enqueue({
sessionId: 's1',
projectName: 'proj',
contentType: 'toolResult',
sourceName: 'test/tool',
original: 'raw',
transformed: 'auto-processed',
});
const items = pauseQueue.getItems();
pauseQueue.editAndRelease(items[0]!.id, 'manually-edited');
const result = await promise;
expect(result).toBe('manually-edited');
});
it('dropOne sends empty content', async () => {
pauseQueue.setPaused(true);
const promise = pauseQueue.enqueue({
sessionId: 's1',
projectName: 'proj',
contentType: 'toolResult',
sourceName: 'test/tool',
original: 'raw',
transformed: 'processed',
});
const items = pauseQueue.getItems();
pauseQueue.dropOne(items[0]!.id);
const result = await promise;
expect(result).toBe('');
});
it('setPaused(false) releases all queued items', async () => {
pauseQueue.setPaused(true);
const p1 = pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool1', original: 'r1', transformed: 't1',
});
const p2 = pauseQueue.enqueue({
sessionId: 's2', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool2', original: 'r2', transformed: 't2',
});
expect(pauseQueue.size).toBe(2);
// Resume releases all
pauseQueue.setPaused(false);
const [r1, r2] = await Promise.all([p1, p2]);
expect(r1).toBe('t1');
expect(r2).toBe('t2');
expect(pauseQueue.size).toBe(0);
});
it('releaseAll releases all items and returns count', async () => {
pauseQueue.setPaused(true);
const p1 = pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool1', original: 'r1', transformed: 't1',
});
const p2 = pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool2', original: 'r2', transformed: 't2',
});
const count = pauseQueue.releaseAll();
expect(count).toBe(2);
const [r1, r2] = await Promise.all([p1, p2]);
expect(r1).toBe('t1');
expect(r2).toBe('t2');
});
it('returns false for operations on non-existent IDs', () => {
expect(pauseQueue.releaseOne('nonexistent')).toBe(false);
expect(pauseQueue.editAndRelease('nonexistent', 'x')).toBe(false);
expect(pauseQueue.dropOne('nonexistent')).toBe(false);
});
it('getItems strips internal resolve function', async () => {
pauseQueue.setPaused(true);
pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool', original: 'raw', transformed: 'processed',
});
const items = pauseQueue.getItems();
expect(items).toHaveLength(1);
const item = items[0]!;
expect(item.id).toBeTruthy();
expect(item.sessionId).toBe('s1');
expect(item.projectName).toBe('proj');
expect(item.contentType).toBe('toolResult');
expect(item.sourceName).toBe('tool');
expect(item.original).toBe('raw');
expect(item.transformed).toBe('processed');
expect(item.timestamp).toBeGreaterThan(0);
// Must not expose resolve
expect((item as Record<string, unknown>)['resolve']).toBeUndefined();
// Cleanup
pauseQueue.releaseAll();
});
it('notifies subscribers on enqueue and release', async () => {
const notifications: number[] = [];
const unsub = pauseQueue.subscribe((items) => notifications.push(items.length));
pauseQueue.setPaused(true);
pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool', original: 'r', transformed: 't',
});
expect(notifications).toContain(1);
pauseQueue.releaseAll();
expect(notifications).toContain(0);
unsub();
});
it('each item gets a unique ID', async () => {
pauseQueue.setPaused(true);
pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool1', original: 'r1', transformed: 't1',
});
pauseQueue.enqueue({
sessionId: 's1', projectName: 'proj', contentType: 'toolResult',
sourceName: 'tool2', original: 'r2', transformed: 't2',
});
const items = pauseQueue.getItems();
expect(items).toHaveLength(2);
expect(items[0]!.id).not.toBe(items[1]!.id);
pauseQueue.releaseAll();
});
});