Files
inkling/tests/unit/inboxApi-openMedia.test.ts
altair823 4db7a0bce0 refactor(v032): recall IPC handle→on + fix sibling test mocks (#36)
- inbox:emitRecallShown / emitRecallSnoozed: ipcMain.handle → on
  (fire-and-forget honest pattern, return value 의존자 0)
- preload: ipcRenderer.invoke → send (matching on the main side)
- shared/types: Promise<void> → void on both recall emit methods
- store.ts: drop await on emitRecallSnoozed (now void)
- inboxApi-*.test.ts: add ipcMain.on to electron mock (broken by above)
- tests/unit/recall-ipc.test.ts: new TDD test for handle→on migration

Note: #20 CaptureService telemetry .catch debug log skipped —
CaptureService has no logger field; adding one would require non-trivial
constructor signature change. Reported as CONCERN below.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 14:23:19 +09:00

64 lines
2.0 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { join } from 'node:path';
const { handlers, mockOpenPath } = vi.hoisted(() => ({
handlers: {} as Record<string, (...args: unknown[]) => unknown>,
mockOpenPath: vi.fn(async () => '')
}));
vi.mock('electron', () => ({
default: {
ipcMain: {
handle: (ch: string, fn: (...args: unknown[]) => unknown) => {
handlers[ch] = fn;
},
on: (_ch: string, _fn: unknown) => {}
},
dialog: {},
shell: { openPath: mockOpenPath }
}
}));
import { registerInboxApi } from '../../src/main/ipc/inboxApi';
function makeDeps(profileDir: string): Parameters<typeof registerInboxApi>[0] {
// Minimal stub — `inbox:open-media` 핸들러는 deps.paths.profileDir 만 참조.
return {
repo: {} as never,
continuity: {} as never,
capture: {} as never,
health: {} as never,
intent: {} as never,
getInboxWindow: () => null,
settings: {} as never,
providerHolder: {} as never,
paths: { profileDir }
};
}
describe('inbox:open-media IPC', () => {
beforeEach(() => {
Object.keys(handlers).forEach((k) => delete handlers[k]);
mockOpenPath.mockClear();
});
it('opens valid relPath with shell.openPath', async () => {
registerInboxApi(makeDeps('/profile'));
const handler = handlers['inbox:open-media'];
if (handler === undefined) throw new Error('handler not registered');
const r = await handler(null, 'media/note1/img.png');
expect(r).toEqual({ ok: true });
expect(mockOpenPath).toHaveBeenCalledWith(join('/profile', 'media', 'note1', 'img.png'));
});
it('rejects path traversal with reason "invalid path"', async () => {
registerInboxApi(makeDeps('/profile'));
const handler = handlers['inbox:open-media'];
if (handler === undefined) throw new Error('handler not registered');
const r = await handler(null, '../etc/passwd') as { ok: boolean; reason?: string };
expect(r.ok).toBe(false);
expect(r.reason).toBe('invalid path');
expect(mockOpenPath).not.toHaveBeenCalled();
});
});