import { describe, it, expect, vi, beforeEach } from 'vitest'; import { join } from 'node:path'; const { handlers, mockOpenPath } = vi.hoisted(() => ({ handlers: {} as Record unknown>, mockOpenPath: vi.fn(async () => '') })); vi.mock('electron', () => ({ default: { ipcMain: { handle: (ch: string, fn: (...args: unknown[]) => unknown) => { handlers[ch] = fn; } }, dialog: {}, shell: { openPath: mockOpenPath } } })); import { registerInboxApi } from '../../src/main/ipc/inboxApi'; function makeDeps(profileDir: string): Parameters[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(); }); });