chore: complete vitest migration for remaining packages and API calls
- Convert remaining node:test → vitest imports in packages/* and studio/* - Fix mock.callCount() → mock.callCount property access for vitest compat - Fix mock.calls[N].arguments → mock.calls[N] for vitest compat - Update tsconfig.extensions.json to exclude test files from tsc - Harden migrate-to-vitest-all.mjs regex for single quotes and optional semicolons
This commit is contained in:
parent
b62f7b20ec
commit
1de5d5456a
25 changed files with 125 additions and 121 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, afterEach, before, after } from 'node:test';
|
||||
import { describe, it, afterEach, before, after } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, writeFileSync, readFileSync, rmSync, existsSync, mkdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, afterEach } from 'node:test';
|
||||
import { describe, it, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, readFileSync, rmSync, existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* blocker handling, conversation relay, and cleanup.
|
||||
*/
|
||||
|
||||
import { describe, it, mock } from 'node:test';
|
||||
import { vi, describe, it } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { EventBridge } from './event-bridge.js';
|
||||
|
|
@ -20,10 +20,10 @@ import type { SdkAgentEvent, RpcClient, RpcExtensionUIRequest } from '@singulari
|
|||
|
||||
function createMockLogger() {
|
||||
return {
|
||||
debug: mock.fn(() => {}),
|
||||
info: mock.fn(() => {}),
|
||||
warn: mock.fn(() => {}),
|
||||
error: mock.fn(() => {}),
|
||||
debug: vi.fn(() => {}),
|
||||
info: vi.fn(() => {}),
|
||||
warn: vi.fn(() => {}),
|
||||
error: vi.fn(() => {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -31,18 +31,18 @@ function createMockChannelManager() {
|
|||
const sentMessages: unknown[] = [];
|
||||
const mockChannel = {
|
||||
id: 'ch-123',
|
||||
send: mock.fn(async (_payload: unknown) => {
|
||||
send: vi.fn(async (_payload: unknown) => {
|
||||
sentMessages.push(_payload);
|
||||
return { id: 'msg-1' };
|
||||
}),
|
||||
createMessageComponentCollector: mock.fn((_opts?: unknown) => {
|
||||
createMessageComponentCollector: vi.fn((_opts?: unknown) => {
|
||||
const collector = new EventEmitter() as EventEmitter & { stop: (reason?: string) => void };
|
||||
collector.stop = (reason?: string) => collector.emit('end', [], reason ?? 'manual');
|
||||
return collector;
|
||||
}),
|
||||
};
|
||||
return {
|
||||
createProjectChannel: mock.fn(async (_dir: string) => mockChannel),
|
||||
createProjectChannel: vi.fn(async (_dir: string) => mockChannel),
|
||||
_channel: mockChannel,
|
||||
_sentMessages: sentMessages,
|
||||
};
|
||||
|
|
@ -50,8 +50,8 @@ function createMockChannelManager() {
|
|||
|
||||
function createMockClient(): BridgeClient & EventEmitter {
|
||||
const emitter = new EventEmitter();
|
||||
const dmSendFn = mock.fn(async () => ({}));
|
||||
const fetchFn = mock.fn(async (_id: string) => ({ send: dmSendFn }));
|
||||
const dmSendFn = vi.fn(async () => ({}));
|
||||
const fetchFn = vi.fn(async (_id: string) => ({ send: dmSendFn }));
|
||||
(emitter as unknown as Record<string, unknown>).users = { fetch: fetchFn };
|
||||
return Object.assign(emitter, {
|
||||
users: { fetch: fetchFn },
|
||||
|
|
@ -61,11 +61,11 @@ function createMockClient(): BridgeClient & EventEmitter {
|
|||
|
||||
function createMockSessionManager() {
|
||||
const sm = new EventEmitter() as EventEmitter & {
|
||||
getSession: ReturnType<typeof mock.fn>;
|
||||
resolveBlocker: ReturnType<typeof mock.fn>;
|
||||
getSession: ReturnType<typeof vi.fn>;
|
||||
resolveBlocker: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
sm.getSession = mock.fn((_id: string) => undefined as ManagedSession | undefined);
|
||||
sm.resolveBlocker = mock.fn(async (_sid: string, _resp: string) => {});
|
||||
sm.getSession = vi.fn((_id: string) => undefined as ManagedSession | undefined);
|
||||
sm.resolveBlocker = vi.fn(async (_sid: string, _resp: string) => {});
|
||||
return sm;
|
||||
}
|
||||
|
||||
|
|
@ -76,8 +76,8 @@ function createMockSession(overrides?: Partial<ManagedSession>): ManagedSession
|
|||
projectName: 'project',
|
||||
status: 'running' as SessionStatus,
|
||||
client: {
|
||||
steer: mock.fn(async (_msg: string) => {}),
|
||||
prompt: mock.fn(async () => ({})),
|
||||
steer: vi.fn(async (_msg: string) => {}),
|
||||
prompt: vi.fn(async () => ({})),
|
||||
} as unknown as RpcClient,
|
||||
events: [],
|
||||
pendingBlocker: null,
|
||||
|
|
@ -123,8 +123,8 @@ function buildBridge(overrides?: Partial<EventBridgeOptions>) {
|
|||
// ---------------------------------------------------------------------------
|
||||
const tick = () => new Promise<void>((r) => setTimeout(r, 30));
|
||||
|
||||
function mockFn(obj: unknown): { mock: { callCount(): number; calls: Array<{ arguments: unknown[]; result?: unknown }> } } {
|
||||
return obj as { mock: { callCount(): number; calls: Array<{ arguments: unknown[]; result?: unknown }> } };
|
||||
function mockFn(obj: unknown): { mock: { callCount: number; calls: Array<unknown[]> } } {
|
||||
return obj as { mock: { callCount: number; calls: Array<unknown[]> } };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -179,12 +179,12 @@ describe('EventBridge', () => {
|
|||
sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
|
||||
});
|
||||
await tick();
|
||||
assert.equal(mockFn(channelManager.createProjectChannel).mock.callCount(), 1);
|
||||
assert.equal(mockFn(channelManager.createProjectChannel).mock.callCount, 1);
|
||||
});
|
||||
|
||||
it('logs error and skips when channel creation fails', async () => {
|
||||
const failingCm = {
|
||||
createProjectChannel: mock.fn(async () => { throw new Error('API error'); }),
|
||||
createProjectChannel: vi.fn(async () => { throw new Error('API error'); }),
|
||||
};
|
||||
const { bridge, sessionManager, logger } = buildBridge({
|
||||
channelManager: failingCm as unknown as EventBridgeOptions['channelManager'],
|
||||
|
|
@ -194,7 +194,7 @@ describe('EventBridge', () => {
|
|||
sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project',
|
||||
});
|
||||
await tick();
|
||||
assert.ok(mockFn(logger.error).mock.callCount() > 0);
|
||||
assert.ok(mockFn(logger.error).mock.callCount > 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ describe('EventBridge', () => {
|
|||
});
|
||||
await tick();
|
||||
// No errors
|
||||
assert.equal(mockFn(logger.error).mock.callCount(), 0);
|
||||
assert.equal(mockFn(logger.error).mock.callCount, 0);
|
||||
});
|
||||
|
||||
it('filters events based on verbosity', async () => {
|
||||
|
|
@ -239,7 +239,7 @@ describe('EventBridge', () => {
|
|||
event: { type: 'tool_execution_start', name: 'read' } as SdkAgentEvent,
|
||||
});
|
||||
await tick();
|
||||
assert.equal(mockFn(logger.error).mock.callCount(), 0);
|
||||
assert.equal(mockFn(logger.error).mock.callCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -260,7 +260,7 @@ describe('EventBridge', () => {
|
|||
sessionId: 'sess-1', projectDir: '/test/project', projectName: 'my-project', blocker,
|
||||
});
|
||||
await tick();
|
||||
assert.ok(mockFn(channelManager._channel.createMessageComponentCollector).mock.callCount() > 0);
|
||||
assert.ok(mockFn(channelManager._channel.createMessageComponentCollector).mock.callCount > 0);
|
||||
});
|
||||
|
||||
it('sends DM when dm_on_blocker is configured', async () => {
|
||||
|
|
@ -287,7 +287,7 @@ describe('EventBridge', () => {
|
|||
await tick();
|
||||
|
||||
const usersFetch = (client as unknown as Record<string, { fetch: unknown }>).users.fetch;
|
||||
assert.equal(mockFn(usersFetch).mock.callCount(), 1);
|
||||
assert.equal(mockFn(usersFetch).mock.callCount, 1);
|
||||
});
|
||||
|
||||
it('does not send DM when dm_on_blocker is false', async () => {
|
||||
|
|
@ -310,7 +310,7 @@ describe('EventBridge', () => {
|
|||
await tick();
|
||||
|
||||
const usersFetch = (client as unknown as Record<string, { fetch: unknown }>).users.fetch;
|
||||
assert.equal(mockFn(usersFetch).mock.callCount(), 0);
|
||||
assert.equal(mockFn(usersFetch).mock.callCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -340,14 +340,14 @@ describe('EventBridge', () => {
|
|||
const mockInteraction = {
|
||||
customId: 'blocker:blocker-1:confirm:true',
|
||||
user: { id: 'owner-1' },
|
||||
update: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
update: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
collector.emit('collect', mockInteraction);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);
|
||||
const args = mockFn(sessionManager.resolveBlocker).mock.calls[0]!.arguments;
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount, 1);
|
||||
const args = mockFn(sessionManager.resolveBlocker).mock.calls[0]!;
|
||||
assert.equal(args[0], 'sess-1');
|
||||
assert.equal(args[1], 'true');
|
||||
});
|
||||
|
|
@ -376,19 +376,19 @@ describe('EventBridge', () => {
|
|||
const mockInteraction = {
|
||||
customId: 'blocker:blocker-1:confirm:true',
|
||||
user: { id: 'stranger-99' },
|
||||
update: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
update: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
collector.emit('collect', mockInteraction);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 0);
|
||||
assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount, 0);
|
||||
assert.equal(mockFn(mockInteraction.reply).mock.callCount, 1);
|
||||
});
|
||||
|
||||
it('posts error when resolveBlocker throws', async () => {
|
||||
const { bridge, sessionManager, channelManager } = buildBridge();
|
||||
sessionManager.resolveBlocker = mock.fn(async () => { throw new Error('No pending blocker'); });
|
||||
sessionManager.resolveBlocker = vi.fn(async () => { throw new Error('No pending blocker'); });
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -411,14 +411,14 @@ describe('EventBridge', () => {
|
|||
const mockInteraction = {
|
||||
customId: 'blocker:blocker-1:confirm:true',
|
||||
user: { id: 'owner-1' },
|
||||
update: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
update: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
collector.emit('collect', mockInteraction);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(mockInteraction.reply).mock.callCount(), 1);
|
||||
const replyArg = mockFn(mockInteraction.reply).mock.calls[0]!.arguments[0] as Record<string, unknown>;
|
||||
assert.equal(mockFn(mockInteraction.reply).mock.callCount, 1);
|
||||
const replyArg = mockFn(mockInteraction.reply).mock.calls[0]![0] as Record<string, unknown>;
|
||||
assert.ok(String(replyArg.content).includes('Failed to resolve'));
|
||||
});
|
||||
});
|
||||
|
|
@ -427,7 +427,7 @@ describe('EventBridge', () => {
|
|||
it('relays message to session steer when no pending blocker', async () => {
|
||||
const session = createMockSession();
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -439,14 +439,14 @@ describe('EventBridge', () => {
|
|||
author: { id: 'owner-1', bot: false },
|
||||
channelId: 'ch-123',
|
||||
content: 'check the test results',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
client.emit('messageCreate', msg);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount(), 1);
|
||||
assert.equal(mockFn(session.client.steer).mock.calls[0]!.arguments[0], 'check the test results');
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount, 1);
|
||||
assert.equal(mockFn(session.client.steer).mock.calls[0]![0], 'check the test results');
|
||||
});
|
||||
|
||||
it('resolves blocker via relay for input method', async () => {
|
||||
|
|
@ -456,7 +456,7 @@ describe('EventBridge', () => {
|
|||
};
|
||||
const session = createMockSession({ pendingBlocker: blocker, status: 'blocked' });
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -468,20 +468,20 @@ describe('EventBridge', () => {
|
|||
author: { id: 'owner-1', bot: false },
|
||||
channelId: 'ch-123',
|
||||
content: 'my-api-key-value',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
client.emit('messageCreate', msg);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount(), 1);
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.calls[0]!.arguments[1], 'my-api-key-value');
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.callCount, 1);
|
||||
assert.equal(mockFn(sessionManager.resolveBlocker).mock.calls[0]![1], 'my-api-key-value');
|
||||
});
|
||||
|
||||
it('ignores bot messages', async () => {
|
||||
const session = createMockSession();
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -493,36 +493,36 @@ describe('EventBridge', () => {
|
|||
author: { id: 'bot-1', bot: true },
|
||||
channelId: 'ch-123',
|
||||
content: 'automated',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
});
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount, 0);
|
||||
});
|
||||
|
||||
it('ignores messages in non-project channels', async () => {
|
||||
const session = createMockSession();
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
client.emit('messageCreate', {
|
||||
author: { id: 'owner-1', bot: false },
|
||||
channelId: 'random-ch-999',
|
||||
content: 'hello',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
});
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount, 0);
|
||||
});
|
||||
|
||||
it('ignores messages from unauthorized users', async () => {
|
||||
const session = createMockSession();
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -534,21 +534,21 @@ describe('EventBridge', () => {
|
|||
author: { id: 'stranger-99', bot: false },
|
||||
channelId: 'ch-123',
|
||||
content: 'hack the planet',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
});
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount(), 0);
|
||||
assert.equal(mockFn(session.client.steer).mock.callCount, 0);
|
||||
});
|
||||
|
||||
it('posts error when steer fails', async () => {
|
||||
const session = createMockSession();
|
||||
(session.client as unknown as Record<string, unknown>).steer = mock.fn(async () => {
|
||||
(session.client as unknown as Record<string, unknown>).steer = vi.fn(async () => {
|
||||
throw new Error('session dead');
|
||||
});
|
||||
const { bridge, sessionManager, client } = buildBridge();
|
||||
sessionManager.getSession = mock.fn(() => session);
|
||||
sessionManager.getSession = vi.fn(() => session);
|
||||
bridge.start();
|
||||
|
||||
sessionManager.emit('session:started', {
|
||||
|
|
@ -560,13 +560,13 @@ describe('EventBridge', () => {
|
|||
author: { id: 'owner-1', bot: false },
|
||||
channelId: 'ch-123',
|
||||
content: 'try this',
|
||||
react: mock.fn(async () => {}),
|
||||
reply: mock.fn(async () => {}),
|
||||
react: vi.fn(async () => {}),
|
||||
reply: vi.fn(async () => {}),
|
||||
};
|
||||
client.emit('messageCreate', msg);
|
||||
await tick();
|
||||
|
||||
assert.equal(mockFn(msg.reply).mock.callCount(), 1);
|
||||
assert.equal(mockFn(msg.reply).mock.callCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -591,7 +591,7 @@ describe('EventBridge', () => {
|
|||
event: { type: 'tool_execution_start', name: 'read' } as SdkAgentEvent,
|
||||
});
|
||||
await tick();
|
||||
assert.equal(mockFn(logger.error).mock.callCount(), 0);
|
||||
assert.equal(mockFn(logger.error).mock.callCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -612,7 +612,7 @@ describe('EventBridge', () => {
|
|||
|
||||
const infoCalls = mockFn(logger.info).mock.calls;
|
||||
assert.ok(
|
||||
infoCalls.some((c) => String(c.arguments[0]).includes('session error')),
|
||||
infoCalls.some((c) => String(c[0]).includes('session error')),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it } from 'node:test';
|
||||
import { describe, it } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder } from 'discord.js';
|
||||
import type { SdkAgentEvent } from '@singularity-forge/rpc-client';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, beforeEach, afterEach } from 'node:test';
|
||||
import { describe, it, beforeEach, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, statSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, beforeEach, afterEach, mock } from 'node:test';
|
||||
import { vi, describe, it, beforeEach, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { MessageBatcher } from './message-batcher.js';
|
||||
import type { SendPayload, BatcherLogger } from './message-batcher.js';
|
||||
|
|
@ -21,7 +21,7 @@ function fakeEvent(content: string, hasEmbed = false): FormattedEvent {
|
|||
/** Create a tracking send function. */
|
||||
function createSend() {
|
||||
const calls: SendPayload[] = [];
|
||||
const fn = mock.fn(async (payload: SendPayload) => {
|
||||
const fn = vi.fn(async (payload: SendPayload) => {
|
||||
calls.push(payload);
|
||||
});
|
||||
return { fn, calls };
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* allowing tool execution and conversation flow testing without real API calls.
|
||||
*/
|
||||
|
||||
import { describe, it, afterEach } from 'node:test';
|
||||
import { describe, it, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, rmSync, existsSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Tests for the project scanner module.
|
||||
*/
|
||||
|
||||
import { describe, it, afterEach } from 'node:test';
|
||||
import { describe, it, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, chmodSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* and cleanup without spawning real SF processes.
|
||||
*/
|
||||
|
||||
import { describe, it, beforeEach, afterEach } from 'node:test';
|
||||
import { describe, it, beforeEach, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolve, basename } from 'node:path';
|
||||
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, beforeEach } from 'node:test';
|
||||
import { describe, it, beforeEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { VerbosityManager, shouldShowAtLevel } from './verbosity.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// @singularity-forge/mcp-server — Tests for env-writer utilities
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
|
||||
import { describe, it, afterEach } from 'node:test';
|
||||
import { describe, it, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, realpathSync, symlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* 4. Testing CLI path resolution via static method
|
||||
*/
|
||||
|
||||
import { describe, it, beforeEach, afterEach } from 'node:test';
|
||||
import { describe, it, beforeEach, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolve } from 'node:path';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SF MCP Server — knowledge graph reader tests
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
|
||||
import { describe, it, before, after, beforeEach, afterEach } from 'node:test';
|
||||
import { describe, it, before, after, beforeEach, afterEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SF MCP Server — reader tests
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
|
||||
import { describe, it, before, after } from 'node:test';
|
||||
import { describe, it, before, after } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// Tests the secure_env_collect tool registered in createMcpServer.
|
||||
// Uses a mock MCP server to intercept tool registration and elicitInput calls.
|
||||
|
||||
import { describe, it, beforeEach } from 'node:test';
|
||||
import { describe, it, beforeEach } from 'vitest';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ describe("generateSummary — chunked fallback (#2932)", () => {
|
|||
|
||||
// Assert: should have called completeSimple more than once (chunked)
|
||||
assert.ok(
|
||||
mockComplete.mock.callCount() > 1,
|
||||
`Expected multiple calls for chunked summarization, got ${mockComplete.mock.callCount()}`,
|
||||
mockComplete.mock.callCount > 1,
|
||||
`Expected multiple calls for chunked summarization, got ${mockComplete.mock.callCount}`,
|
||||
);
|
||||
|
||||
// First call should be an initial summary, subsequent should be updates
|
||||
|
|
@ -189,7 +189,7 @@ describe("generateSummary — chunked fallback (#2932)", () => {
|
|||
await generateSummary(messages, model, reserveTokens, undefined, undefined, undefined, undefined, mockComplete);
|
||||
|
||||
assert.equal(
|
||||
mockComplete.mock.callCount(),
|
||||
mockComplete.mock.callCount,
|
||||
1,
|
||||
"Should use single-pass summarization when messages fit in context window",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ describe("FallbackResolver — findFallback", () => {
|
|||
|
||||
const fn = authStorage.markProviderExhausted as any;
|
||||
assert.equal(fn.mock.calls.length, 1);
|
||||
assert.equal(fn.mock.calls[0].arguments[0], "zai");
|
||||
assert.equal(fn.mock.calls[0].arguments[1], "rate_limit");
|
||||
assert.equal(fn.mock.calls[0][0], "zai");
|
||||
assert.equal(fn.mock.calls[0][1], "rate_limit");
|
||||
});
|
||||
|
||||
it("skips backed-off providers", async () => {
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|||
// Should have called setModel with the base model
|
||||
const setModelCalls = (deps.agent.setModel as any).mock.calls;
|
||||
assert.equal(setModelCalls.length, 1);
|
||||
assert.equal(setModelCalls[0].arguments[0].id, "claude-opus-4-6");
|
||||
assert.equal(setModelCalls[0][0].id, "claude-opus-4-6");
|
||||
|
||||
// Should have notified about model change
|
||||
assert.equal(onModelChangeFn.mock.calls.length, 1);
|
||||
|
|
@ -343,7 +343,7 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|||
|
||||
const setModelCalls = (deps.agent.setModel as any).mock.calls;
|
||||
assert.equal(setModelCalls.length, 1, "should apply one model downgrade");
|
||||
const downgraded = setModelCalls[0].arguments[0] as Model<Api>;
|
||||
const downgraded = setModelCalls[0][0] as Model<Api>;
|
||||
assert.equal(downgraded.provider, "openrouter");
|
||||
assert.equal(downgraded.id, "openai/gpt-5-pro");
|
||||
assert.equal(downgraded.maxTokens, 297, "expected affordability cap with safety buffer");
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@ function collectTestFiles(dirs) {
|
|||
}
|
||||
|
||||
function migrateImport(content, { hasMockFn, hasMockTimers }) {
|
||||
// Case 1: import test from "node:test";
|
||||
// Case 1: import test from "node:test"; (or single quotes, optional semicolon)
|
||||
content = content.replace(
|
||||
/^import test from "node:test";$/gm,
|
||||
/^import test from ["']node:test["'];?$/gm,
|
||||
"import { test } from 'vitest';",
|
||||
);
|
||||
|
||||
// Case 2: import { ... } from "node:test" (and variants with default import)
|
||||
content = content.replace(
|
||||
/import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+"node:test";?/g,
|
||||
/import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+["']node:test["'];?/g,
|
||||
(match, hasDefault, named) => {
|
||||
const namedList = named
|
||||
.split(",")
|
||||
|
|
@ -83,7 +83,7 @@ let errors = 0;
|
|||
for (const file of files) {
|
||||
try {
|
||||
const content = readFileSync(file, "utf-8");
|
||||
if (!content.includes('from "node:test"')) continue;
|
||||
if (!content.includes('from "node:test"') && !content.includes("from 'node:test'")) continue;
|
||||
|
||||
const hasMockFn = content.includes("mock.fn");
|
||||
const hasMockTimers = content.includes("mock.timers");
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ describe("Post-execution blocking failure retry bypass", () => {
|
|||
|
||||
// Non-execute-task units should return "continue" immediately
|
||||
assert.equal(result, "continue");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 0);
|
||||
});
|
||||
|
||||
test("returns continue when verification passes", async () => {
|
||||
|
|
@ -256,7 +256,7 @@ describe("Post-execution blocking failure retry bypass", () => {
|
|||
|
||||
// When verification passes, should return "continue" and not call pauseAuto
|
||||
assert.equal(result, "continue");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 0);
|
||||
|
||||
// Retry state should be cleared
|
||||
assert.equal(s.pendingVerificationRetry, null);
|
||||
|
|
@ -345,7 +345,7 @@ describe("Post-execution blocking failure retry bypass", () => {
|
|||
const result = await runPostUnitVerification(vctx, pauseAutoMock);
|
||||
|
||||
assert.equal(result, "pause");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 1);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 1);
|
||||
|
||||
const adapter = _getAdapter();
|
||||
const row = adapter
|
||||
|
|
@ -426,7 +426,7 @@ describe("Post-execution retry behavior", () => {
|
|||
|
||||
// When autofix is disabled and verification fails, should pause
|
||||
assert.equal(result, "pause");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 1);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 1);
|
||||
|
||||
// Should NOT set up a retry
|
||||
assert.equal(s.pendingVerificationRetry, null);
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ describe("Pre-execution fail-closed behavior", () => {
|
|||
|
||||
// With valid tasks, pre-exec should pass and not pause
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
0,
|
||||
"pauseAuto should NOT be called when pre-execution checks pass",
|
||||
);
|
||||
|
|
@ -267,7 +267,7 @@ describe("Pre-execution fail-closed behavior", () => {
|
|||
|
||||
// With a blocking failure, pauseAuto should be called
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
1,
|
||||
"pauseAuto should be called when pre-execution checks fail",
|
||||
);
|
||||
|
|
@ -281,7 +281,7 @@ describe("Pre-execution fail-closed behavior", () => {
|
|||
// Verify error notification was shown
|
||||
const notifyCalls = ctx.ui.notify.mock.calls;
|
||||
const errorNotify = notifyCalls.find(
|
||||
(call: { arguments: unknown[] }) => call.arguments[1] === "error",
|
||||
(call: unknown[]) => call[1] === "error",
|
||||
);
|
||||
assert.ok(
|
||||
errorNotify,
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
|
||||
// Verify pauseAuto was called
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
1,
|
||||
"pauseAuto should be called exactly once when pre-execution checks fail with blocking issues",
|
||||
);
|
||||
|
|
@ -327,9 +327,9 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
// Verify UI was notified of the failure
|
||||
const notifyCalls = ctx.ui.notify.mock.calls;
|
||||
const errorNotify = notifyCalls.find(
|
||||
(call: { arguments: unknown[] }) =>
|
||||
call.arguments[1] === "error" &&
|
||||
String(call.arguments[0]).includes("Pre-execution checks failed"),
|
||||
(call: unknown[]) =>
|
||||
call[1] === "error" &&
|
||||
String(call[0]).includes("Pre-execution checks failed"),
|
||||
);
|
||||
assert.ok(
|
||||
errorNotify,
|
||||
|
|
@ -360,7 +360,7 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
|
||||
// Verify pauseAuto was called (strict mode promotes warnings to blocking)
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
1,
|
||||
"pauseAuto should be called when strict mode is enabled and pre-execution returns warn",
|
||||
);
|
||||
|
|
@ -375,9 +375,9 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
// Verify UI was notified of the warning
|
||||
const notifyCalls = ctx.ui.notify.mock.calls;
|
||||
const warnNotify = notifyCalls.find(
|
||||
(call: { arguments: unknown[] }) =>
|
||||
call.arguments[1] === "warning" &&
|
||||
String(call.arguments[0]).includes(
|
||||
(call: unknown[]) =>
|
||||
call[1] === "warning" &&
|
||||
String(call[0]).includes(
|
||||
"Pre-execution checks passed with warnings",
|
||||
),
|
||||
);
|
||||
|
|
@ -410,7 +410,7 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
|
||||
// Verify pauseAuto was NOT called (warnings don't block in non-strict mode)
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
0,
|
||||
"pauseAuto should NOT be called when strict mode is disabled and only warnings exist",
|
||||
);
|
||||
|
|
@ -442,7 +442,7 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
|
||||
// Verify pauseAuto was NOT called (pre-execution checks only run for plan-slice)
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
0,
|
||||
"pauseAuto should NOT be called for non-plan-slice unit types",
|
||||
);
|
||||
|
|
@ -477,7 +477,7 @@ describe("Pre-execution checks → pauseAuto wiring", () => {
|
|||
|
||||
// Verify pauseAuto was NOT called (pre-execution checks disabled)
|
||||
assert.equal(
|
||||
pauseAutoMock.mock.callCount(),
|
||||
pauseAutoMock.mock.callCount,
|
||||
0,
|
||||
"pauseAuto should NOT be called when enhanced_verification_pre is disabled",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -139,9 +139,9 @@ describe("validate-milestone stuck-loop guard (#4094)", () => {
|
|||
);
|
||||
|
||||
assert.equal(result, "pause");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 1);
|
||||
assert.equal(ctx.ui.notify.mock.callCount(), 1);
|
||||
const notifyArgs = ctx.ui.notify.mock.calls[0].arguments;
|
||||
assert.equal(pauseAutoMock.mock.callCount, 1);
|
||||
assert.equal(ctx.ui.notify.mock.callCount, 1);
|
||||
const notifyArgs = ctx.ui.notify.mock.calls[0];
|
||||
assert.match(notifyArgs[0], /needs-remediation/);
|
||||
assert.equal(notifyArgs[1], "error");
|
||||
});
|
||||
|
|
@ -173,7 +173,7 @@ describe("validate-milestone stuck-loop guard (#4094)", () => {
|
|||
);
|
||||
|
||||
assert.equal(result, "pause");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 1);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 1);
|
||||
});
|
||||
|
||||
test("continues when verdict=needs-remediation but a queued remediation slice exists", async () => {
|
||||
|
|
@ -203,7 +203,7 @@ describe("validate-milestone stuck-loop guard (#4094)", () => {
|
|||
);
|
||||
|
||||
assert.equal(result, "continue");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 0);
|
||||
});
|
||||
|
||||
test("continues when verdict is pass", async () => {
|
||||
|
|
@ -227,7 +227,7 @@ describe("validate-milestone stuck-loop guard (#4094)", () => {
|
|||
);
|
||||
|
||||
assert.equal(result, "continue");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 0);
|
||||
});
|
||||
|
||||
test("continues when no VALIDATION file exists yet", async () => {
|
||||
|
|
@ -250,6 +250,6 @@ describe("validate-milestone stuck-loop guard (#4094)", () => {
|
|||
);
|
||||
|
||||
assert.equal(result, "continue");
|
||||
assert.equal(pauseAutoMock.mock.callCount(), 0);
|
||||
assert.equal(pauseAutoMock.mock.callCount, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import test from 'node:test'
|
||||
import { test } from 'vitest';
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@
|
|||
},
|
||||
"include": ["src/resources/extensions"],
|
||||
"exclude": [
|
||||
"src/resources/extensions/vectordrive/tests/**/*.ts"
|
||||
"src/resources/extensions/vectordrive/tests/**/*.ts",
|
||||
"src/resources/extensions/**/tests/**/*.ts",
|
||||
"src/resources/extensions/**/tests/**/*.mjs",
|
||||
"src/tests/**/*.ts",
|
||||
"src/tests/**/*.mjs"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue