// Shared fixtures for the per-theme bridge-action tests.
//
// Each action test file mocks `../src/bridge/bridge-oidb` and (where
// needed) `../src/bridge/highway/*` so we can assert what the action
// asked the OIDB / Highway layer to do without booting a real Bridge.
//
// `mockBridge()` returns a minimal stand-in: enough state for the
// actions to thread but no real packet/event machinery.
//
// As the #6 Api-on-ctx refactor moves business methods OFF Bridge and
// onto `bridge.apis..method()`, the mock grows an `apis` block
// matching the ApiHub shape. Each Api gets its own stub helper
// (e.g. `mockMessageApi()`) and tests can override individual entries
// via `mockBridge({ apis: { message: ... } })`.
import { vi } from 'vitest';
import type { SendPacketResult } from '@snowluma/common/packet-sender';
/** Default receipt returned by sendGroup / sendPrivate / sendC2cFile mocks. */
const STUB_RECEIPT = { messageId: 1, sequence: 1, clientSequence: 0, random: 1, timestamp: 0 };
export interface MockMessageApi {
sendGroup: ReturnType;
sendPrivate: ReturnType;
sendC2cFile: ReturnType;
recallGroup: ReturnType;
recallPrivate: ReturnType;
markGroupRead: ReturnType;
markPrivateRead: ReturnType;
markAllRead: ReturnType;
}
export function mockMessageApi(): MockMessageApi {
return {
sendGroup: vi.fn(async () => STUB_RECEIPT),
sendPrivate: vi.fn(async () => STUB_RECEIPT),
sendC2cFile: vi.fn(async () => STUB_RECEIPT),
recallGroup: vi.fn(async () => undefined),
recallPrivate: vi.fn(async () => undefined),
markGroupRead: vi.fn(async () => undefined),
markPrivateRead: vi.fn(async () => undefined),
markAllRead: vi.fn(async () => undefined),
};
}
export interface MockContactsApi {
fetchFriendList: ReturnType;
fetchGroupList: ReturnType;
fetchGroupMemberList: ReturnType;
fetchUserProfile: ReturnType;
fetchGroupRequests: ReturnType;
fetchDownloadRKeys: ReturnType;
}
export function mockContactsApi(): MockContactsApi {
return {
fetchFriendList: vi.fn(async () => []),
fetchGroupList: vi.fn(async () => []),
fetchGroupMemberList: vi.fn(async () => []),
fetchUserProfile: vi.fn(async () => ({ uid: 'profile-uid' })),
fetchGroupRequests: vi.fn(async () => []),
fetchDownloadRKeys: vi.fn(async () => []),
};
}
export interface MockGroupFileApi {
upload: ReturnType;
uploadPrivate: ReturnType;
publish: ReturnType;
trans: ReturnType;
getCount: ReturnType;
list: ReturnType;
getUrl: ReturnType;
getPrivateUrl: ReturnType;
getPttUrl: ReturnType;
getPrivatePttUrl: ReturnType;
getVideoUrl: ReturnType;
getPrivateVideoUrl: ReturnType;
delete: ReturnType;
move: ReturnType;
createFolder: ReturnType;
deleteFolder: ReturnType;
renameFolder: ReturnType;
}
export function mockGroupFileApi(): MockGroupFileApi {
return {
upload: vi.fn(async () => ({ fileId: 'stub-fid' })),
uploadPrivate: vi.fn(async () => ({ fileId: 'stub-pfid', fileHash: 'stub-hash' })),
publish: vi.fn(async () => undefined),
trans: vi.fn(async () => ({ saveBusId: 102, saveFilePath: '/stub-path' })),
getCount: vi.fn(async () => ({ fileCount: 0, maxCount: 10000 })),
list: vi.fn(async () => ({ files: [], folders: [] })),
getUrl: vi.fn(async () => 'stub://url'),
getPrivateUrl: vi.fn(async () => 'stub://private-url'),
getPttUrl: vi.fn(async () => 'stub://ptt-url'),
getPrivatePttUrl: vi.fn(async () => 'stub://private-ptt-url'),
getVideoUrl: vi.fn(async () => 'stub://video-url'),
getPrivateVideoUrl: vi.fn(async () => 'stub://private-video-url'),
delete: vi.fn(async () => undefined),
move: vi.fn(async () => undefined),
createFolder: vi.fn(async () => undefined),
deleteFolder: vi.fn(async () => undefined),
renameFolder: vi.fn(async () => undefined),
};
}
export interface MockApiHub {
message: MockMessageApi;
contacts: MockContactsApi;
groupFile: MockGroupFileApi;
// additional Apis added commit-by-commit as #6 progresses
}
export function mockApiHub(overrides: Partial = {}): MockApiHub {
return {
message: overrides.message ?? mockMessageApi(),
contacts: overrides.contacts ?? mockContactsApi(),
groupFile: overrides.groupFile ?? mockGroupFileApi(),
};
}
export interface MockBridge {
identity: {
uin: string;
selfUid: string;
nickname: string;
findUidByUin: ReturnType;
findUinByUid: ReturnType;
resolveUin: ReturnType;
findGroupMember: ReturnType;
updateFriendRemark: ReturnType;
updateGroupRemark: ReturnType;
forgetGroup: ReturnType;
};
events: { emit: ReturnType };
apis: MockApiHub;
sendRawPacket: ReturnType;
fetchFriendList: ReturnType;
fetchGroupMemberList: ReturnType;
fetchUserProfile: ReturnType;
resolveUserUid: ReturnType;
// Uploaded-file metadata cache helpers — GroupFileApi.upload /
// uploadPrivate call these to remember the upload, so tests
// covering those code paths get a default-no-op shim.
rememberUploadedFile: ReturnType;
recallUploadedFile: ReturnType;
}
export function mockBridge(overrides: Partial = {}): MockBridge {
const defaultResp: SendPacketResult = {
success: true,
gotResponse: true,
errorCode: 0,
errorMessage: '',
responseData: Buffer.alloc(0),
};
return {
identity: {
uin: '10001',
selfUid: 'self-uid',
nickname: 'self-nick',
findUidByUin: vi.fn(() => 'cached-uid'),
findUinByUid: vi.fn(() => 0),
resolveUin: vi.fn(async () => null),
findGroupMember: vi.fn(() => null),
updateFriendRemark: vi.fn(() => true),
updateGroupRemark: vi.fn(() => true),
forgetGroup: vi.fn(),
...(overrides.identity ?? {}),
} as MockBridge['identity'],
events: overrides.events ?? { emit: vi.fn(async () => undefined) },
apis: overrides.apis ?? mockApiHub(),
sendRawPacket: vi.fn(async () => defaultResp),
fetchFriendList: vi.fn(async () => []),
fetchGroupMemberList: vi.fn(async () => []),
fetchUserProfile: vi.fn(async () => ({ uid: 'profile-uid' })),
resolveUserUid: vi.fn(async () => 'resolved-uid'),
rememberUploadedFile: vi.fn(),
recallUploadedFile: vi.fn(() => undefined),
...overrides,
};
}