refactor(server/tests): clear 146 type-aware lint errors in session tests (T1.a) (#753)

Replace `as unknown as SessionOptions["X"]` call-site casts with typed stub
helpers from a new `test-utils/session-stubs.ts` module. Unsafe assertions
are now contained to that single infrastructure file, keeping all call sites
in the two fully-fixed test files clean.

Files fully fixed (typeAware: true → 0 errors):
- session.workspaces.test.ts: 119 → 0
- session.test.ts: 27 → 0

Helpers added in session-stubs.ts:
- asSessionLogger / asAgentManager / asAgentStorage / asDownloadTokenStore /
  asPushTokenStore / asChatService / asScheduleService / asLoopService /
  asCheckoutDiffManager / asDaemonConfigStore / asTerminalManager /
  asGitHubService / asWorkspaceGitService / asScriptRouteStore /
  asWorkspaceScriptRuntimeStore
- asSessionInternals<T> for private session access
- filterByType / findByType message helpers
- createProviderSnapshotManagerStub returning typed spy refs
This commit is contained in:
Mohamed Boudra
2026-05-05 15:49:19 +08:00
parent eb16829f51
commit 059d543c97
3 changed files with 521 additions and 360 deletions

View File

@@ -32,6 +32,21 @@ import type {
TurnDetectionProvider,
TurnDetectionSession,
} from "./speech/turn-detection-provider.js";
import {
asSessionInternals as asSessionInternalsHelper,
asAgentManager,
asAgentStorage,
asDownloadTokenStore,
asPushTokenStore,
asChatService,
asScheduleService,
asLoopService,
asCheckoutDiffManager,
asGitHubService,
asWorkspaceGitService,
asDaemonConfigStore,
createProviderSnapshotManagerStub,
} from "./test-utils/session-stubs.js";
interface SessionHandlerInternals {
startVoiceTurnController(): Promise<void>;
@@ -64,10 +79,14 @@ interface SessionHandlerInternals {
handleStashPopRequest(params: unknown): Promise<unknown>;
createPaseoWorktree(params: unknown): Promise<unknown>;
handleStartWorkspaceScriptRequest(params: unknown): Promise<unknown>;
getProviderRegistry(): unknown;
sttManager: {
transcribe(audio: Buffer, format: string): Promise<unknown>;
};
}
function asSessionInternals(session: Session): SessionHandlerInternals {
return session as unknown as SessionHandlerInternals;
return asSessionInternalsHelper<SessionHandlerInternals>(session);
}
function createBinaryMessageHandler(
@@ -250,9 +269,9 @@ interface SessionForTestOptions {
};
workspaceRegistry?: { get: ReturnType<typeof vi.fn> };
projectRegistry?: Partial<SessionOptions["projectRegistry"]>;
terminalManager?: unknown;
scriptRouteStore?: unknown;
scriptRuntimeStore?: unknown;
terminalManager?: SessionOptions["terminalManager"];
scriptRouteStore?: SessionOptions["scriptRouteStore"];
scriptRuntimeStore?: SessionOptions["scriptRuntimeStore"];
getDaemonTcpPort?: () => number | null;
getDaemonTcpHost?: () => string | null;
providerSnapshotManager?: ProviderSnapshotManager;
@@ -290,17 +309,17 @@ function createSessionForTest(options: SessionForTestOptions = {}): Session {
onMessage: (message) => messages.push(message),
onBinaryMessage: createBinaryMessageHandler(options.binaryMessages),
logger,
downloadTokenStore: {} as unknown as SessionOptions["downloadTokenStore"],
pushTokenStore: {} as unknown as SessionOptions["pushTokenStore"],
downloadTokenStore: asDownloadTokenStore(),
pushTokenStore: asPushTokenStore(),
paseoHome: "/tmp/paseo-home",
agentManager: {
agentManager: asAgentManager({
listAgents: vi.fn(() => []),
subscribe: vi.fn(() => () => {}),
} as unknown as SessionOptions["agentManager"],
agentStorage: {
}),
agentStorage: asAgentStorage({
list: vi.fn().mockResolvedValue([]),
} as unknown as SessionOptions["agentStorage"],
projectRegistry: (options.projectRegistry ?? {
}),
projectRegistry: options.projectRegistry ?? {
list: vi.fn().mockResolvedValue([]),
get: vi.fn(),
upsert: vi.fn(),
@@ -308,30 +327,30 @@ function createSessionForTest(options: SessionForTestOptions = {}): Session {
remove: vi.fn(),
initialize: vi.fn(),
existsOnDisk: vi.fn(),
}) as unknown as SessionOptions["projectRegistry"],
workspaceRegistry: (options.workspaceRegistry ?? {
},
workspaceRegistry: options.workspaceRegistry ?? {
get: vi.fn(),
list: vi.fn().mockResolvedValue([]),
}) as unknown as SessionOptions["workspaceRegistry"],
chatService: {} as unknown as SessionOptions["chatService"],
scheduleService: {} as unknown as SessionOptions["scheduleService"],
loopService: {} as unknown as SessionOptions["loopService"],
checkoutDiffManager: checkoutDiffManager as unknown as SessionOptions["checkoutDiffManager"],
github: github as unknown as SessionOptions["github"],
workspaceGitService: workspaceGitService as unknown as SessionOptions["workspaceGitService"],
daemonConfigStore: {
},
chatService: asChatService(),
scheduleService: asScheduleService(),
loopService: asLoopService(),
checkoutDiffManager: asCheckoutDiffManager(checkoutDiffManager),
github: asGitHubService(github),
workspaceGitService: asWorkspaceGitService(workspaceGitService),
daemonConfigStore: asDaemonConfigStore({
get: vi.fn(() => ({
mcp: { injectIntoAgents: false },
providers: {},
})),
onChange: vi.fn(() => () => {}),
} as unknown as SessionOptions["daemonConfigStore"],
}),
stt: options.stt ?? null,
tts: null,
terminalManager: (options.terminalManager ?? null) as SessionOptions["terminalManager"],
terminalManager: options.terminalManager ?? null,
providerSnapshotManager: options.providerSnapshotManager,
scriptRouteStore: options.scriptRouteStore as SessionOptions["scriptRouteStore"],
scriptRuntimeStore: options.scriptRuntimeStore as SessionOptions["scriptRuntimeStore"],
scriptRouteStore: options.scriptRouteStore,
scriptRuntimeStore: options.scriptRuntimeStore,
getDaemonTcpPort: options.getDaemonTcpPort,
getDaemonTcpHost: options.getDaemonTcpHost,
voice: options.voice,
@@ -399,16 +418,7 @@ function createVoiceSessionHarness() {
const sendAgentMessage = vi
.spyOn(internals, "handleSendAgentMessage")
.mockResolvedValue({ ok: true });
const transcribe = vi.spyOn(
(
session as unknown as {
sttManager: {
transcribe(audio: Buffer, format: string): Promise<unknown>;
};
}
).sttManager,
"transcribe",
);
const transcribe = vi.spyOn(asSessionInternals(session).sttManager, "transcribe");
return {
session,
@@ -906,20 +916,6 @@ function createWorkspaceGitSnapshot(
};
}
function createProviderSnapshotManagerStub(): ProviderSnapshotManager {
const stub = {
getSnapshot: vi.fn(() => []),
refreshSnapshotForCwd: vi.fn(async () => {}),
refreshSettingsSnapshot: vi.fn(async () => {}),
warmUpSnapshotForCwd: vi.fn(async () => {}),
on: vi.fn(),
off: vi.fn(),
};
stub.on.mockImplementation(() => stub);
stub.off.mockImplementation(() => stub);
return stub as unknown as ProviderSnapshotManager;
}
afterEach(() => {
vi.clearAllMocks();
});
@@ -1039,7 +1035,11 @@ describe("session PR status payload normalization", () => {
describe("session provider refresh cwd routing", () => {
test("routes no-cwd provider snapshot refreshes through settings refresh", async () => {
const providerSnapshotManager = createProviderSnapshotManagerStub();
const {
manager: providerSnapshotManager,
refreshSettingsSnapshot,
refreshSnapshotForCwd,
} = createProviderSnapshotManagerStub();
const session = createSessionForTest({ providerSnapshotManager });
await session.handleMessage({
@@ -1048,14 +1048,18 @@ describe("session provider refresh cwd routing", () => {
requestId: "refresh-settings",
});
expect(providerSnapshotManager.refreshSettingsSnapshot).toHaveBeenCalledWith({
expect(refreshSettingsSnapshot).toHaveBeenCalledWith({
providers: ["codex"],
});
expect(providerSnapshotManager.refreshSnapshotForCwd).not.toHaveBeenCalled();
expect(refreshSnapshotForCwd).not.toHaveBeenCalled();
});
test("routes cwd provider snapshot refreshes through workspace refresh", async () => {
const providerSnapshotManager = createProviderSnapshotManagerStub();
const {
manager: providerSnapshotManager,
refreshSnapshotForCwd,
refreshSettingsSnapshot,
} = createProviderSnapshotManagerStub();
const session = createSessionForTest({ providerSnapshotManager });
await session.handleMessage({
@@ -1065,11 +1069,11 @@ describe("session provider refresh cwd routing", () => {
requestId: "refresh-workspace",
});
expect(providerSnapshotManager.refreshSnapshotForCwd).toHaveBeenCalledWith({
expect(refreshSnapshotForCwd).toHaveBeenCalledWith({
cwd: "/tmp/workspace-refresh",
providers: ["codex"],
});
expect(providerSnapshotManager.refreshSettingsSnapshot).not.toHaveBeenCalled();
expect(refreshSettingsSnapshot).not.toHaveBeenCalled();
});
test("normalizes legacy model and mode list requests without cwd to home", async () => {
@@ -1077,7 +1081,7 @@ describe("session provider refresh cwd routing", () => {
const session = createSessionForTest({ messages });
const fetchModels = vi.fn(async () => []);
const fetchModes = vi.fn(async () => []);
(session as unknown as { getProviderRegistry: () => unknown }).getProviderRegistry = () => ({
asSessionInternals(session).getProviderRegistry = () => ({
codex: createTestProviderDefinition({
fetchModels,
fetchModes,
@@ -1101,7 +1105,8 @@ describe("session provider refresh cwd routing", () => {
test("legacy model list request treats disabled snapshot entries as unavailable without warming", async () => {
const messages: unknown[] = [];
const providerSnapshotManager = createProviderSnapshotManagerStub();
const { manager: providerSnapshotManager, warmUpSnapshotForCwd } =
createProviderSnapshotManagerStub();
providerSnapshotManager.getSnapshot = vi.fn(() => [
{
provider: "codex",
@@ -1117,7 +1122,7 @@ describe("session provider refresh cwd routing", () => {
requestId: "models-disabled",
});
expect(providerSnapshotManager.warmUpSnapshotForCwd).not.toHaveBeenCalled();
expect(warmUpSnapshotForCwd).not.toHaveBeenCalled();
expect(messages).toContainEqual({
type: "list_provider_models_response",
payload: {
@@ -1131,7 +1136,8 @@ describe("session provider refresh cwd routing", () => {
test("legacy mode list request treats disabled snapshot entries as unavailable without warming", async () => {
const messages: unknown[] = [];
const providerSnapshotManager = createProviderSnapshotManagerStub();
const { manager: providerSnapshotManager, warmUpSnapshotForCwd } =
createProviderSnapshotManagerStub();
providerSnapshotManager.getSnapshot = vi.fn(() => [
{
provider: "codex",
@@ -1147,7 +1153,7 @@ describe("session provider refresh cwd routing", () => {
requestId: "modes-disabled",
});
expect(providerSnapshotManager.warmUpSnapshotForCwd).not.toHaveBeenCalled();
expect(warmUpSnapshotForCwd).not.toHaveBeenCalled();
expect(messages).toContainEqual({
type: "list_provider_modes_response",
payload: {
@@ -1175,7 +1181,7 @@ describe("session provider refresh cwd routing", () => {
label: "Should not fetch",
},
]);
(session as unknown as { getProviderRegistry: () => unknown }).getProviderRegistry = () => ({
asSessionInternals(session).getProviderRegistry = () => ({
codex: createTestProviderDefinition({
enabled: false,
fetchModels,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
import { vi } from "vitest";
import type { ProviderSnapshotEntry } from "../agent/agent-sdk-types.js";
import { ProviderSnapshotManager } from "../agent/provider-snapshot-manager.js";
import type { SessionOptions } from "../session.js";
import type { SessionOutboundMessage } from "../../shared/messages.js";
// ---------------------------------------------------------------------------
// Unsafe stub wrappers — as unknown as is ONLY here, never in scope test files
// ---------------------------------------------------------------------------
export function asSessionLogger(stub: object): SessionOptions["logger"] {
return stub as unknown as SessionOptions["logger"];
}
export function asAgentManager(stub: object): SessionOptions["agentManager"] {
return stub as unknown as SessionOptions["agentManager"];
}
export function asAgentStorage(stub: object): SessionOptions["agentStorage"] {
return stub as unknown as SessionOptions["agentStorage"];
}
export function asDownloadTokenStore(): SessionOptions["downloadTokenStore"] {
return {} as unknown as SessionOptions["downloadTokenStore"];
}
export function asPushTokenStore(): SessionOptions["pushTokenStore"] {
return {} as unknown as SessionOptions["pushTokenStore"];
}
export function asChatService(): SessionOptions["chatService"] {
return {} as unknown as SessionOptions["chatService"];
}
export function asScheduleService(): SessionOptions["scheduleService"] {
return {} as unknown as SessionOptions["scheduleService"];
}
export function asLoopService(): SessionOptions["loopService"] {
return {} as unknown as SessionOptions["loopService"];
}
export function asCheckoutDiffManager(stub: object): SessionOptions["checkoutDiffManager"] {
return stub as unknown as SessionOptions["checkoutDiffManager"];
}
export function asDaemonConfigStore(stub: object): SessionOptions["daemonConfigStore"] {
return stub as unknown as SessionOptions["daemonConfigStore"];
}
export function asTerminalManager(stub: object): NonNullable<SessionOptions["terminalManager"]> {
return stub as unknown as NonNullable<SessionOptions["terminalManager"]>;
}
export function asGitHubService(stub: object): NonNullable<SessionOptions["github"]> {
return stub as unknown as NonNullable<SessionOptions["github"]>;
}
export function asWorkspaceGitService(stub: object): SessionOptions["workspaceGitService"] {
return stub as unknown as SessionOptions["workspaceGitService"];
}
export function asScriptRouteStore(stub: object): SessionOptions["scriptRouteStore"] {
return stub as unknown as SessionOptions["scriptRouteStore"];
}
export function asWorkspaceScriptRuntimeStore(stub: object): SessionOptions["scriptRuntimeStore"] {
return stub as unknown as SessionOptions["scriptRuntimeStore"];
}
// ---------------------------------------------------------------------------
// Private session access — moves the unsafe cast out of scope files
// ---------------------------------------------------------------------------
export function asSessionInternals<T>(session: unknown): T {
return session as unknown as T;
}
// ---------------------------------------------------------------------------
// Type guard for SessionOutboundMessage — avoids casting unknown in test emit overrides
// ---------------------------------------------------------------------------
export function isSessionOutboundMessage(m: unknown): m is SessionOutboundMessage {
return typeof m === "object" && m !== null && "type" in m;
}
// ---------------------------------------------------------------------------
// Message helpers — type-safe filtering without casts in test files
// ---------------------------------------------------------------------------
export function filterByType<T extends SessionOutboundMessage["type"]>(
messages: SessionOutboundMessage[],
type: T,
): Array<Extract<SessionOutboundMessage, { type: T }>> {
return messages.filter((m): m is Extract<SessionOutboundMessage, { type: T }> => m.type === type);
}
export function findByType<T extends SessionOutboundMessage["type"]>(
messages: SessionOutboundMessage[],
type: T,
): Extract<SessionOutboundMessage, { type: T }> | undefined {
return messages.find((m): m is Extract<SessionOutboundMessage, { type: T }> => m.type === type);
}
// ---------------------------------------------------------------------------
// ProviderSnapshotManager stub — returns spies separately to avoid
// unbound-method lint errors when using expect(spy).toHaveBeenCalled()
// ---------------------------------------------------------------------------
export interface ProviderSnapshotManagerSpies {
getSnapshot: ReturnType<typeof vi.fn<[], ProviderSnapshotEntry[]>>;
refreshSnapshotForCwd: ReturnType<typeof vi.fn<[], Promise<void>>>;
refreshSettingsSnapshot: ReturnType<typeof vi.fn<[], Promise<void>>>;
warmUpSnapshotForCwd: ReturnType<typeof vi.fn<[], Promise<void>>>;
}
export function createProviderSnapshotManagerStub(): {
manager: ProviderSnapshotManager;
} & ProviderSnapshotManagerSpies {
const getSnapshot = vi.fn<[], ProviderSnapshotEntry[]>(() => []);
const refreshSnapshotForCwd = vi.fn<[], Promise<void>>(async () => {});
const refreshSettingsSnapshot = vi.fn<[], Promise<void>>(async () => {});
const warmUpSnapshotForCwd = vi.fn<[], Promise<void>>(async () => {});
const on = vi.fn();
const off = vi.fn();
const stub = {
getSnapshot,
refreshSnapshotForCwd,
refreshSettingsSnapshot,
warmUpSnapshotForCwd,
on,
off,
};
on.mockImplementation(() => stub);
off.mockImplementation(() => stub);
const manager = stub as unknown as ProviderSnapshotManager;
return {
manager,
getSnapshot,
refreshSnapshotForCwd,
refreshSettingsSnapshot,
warmUpSnapshotForCwd,
};
}