// @vitest-environment jsdom import { cleanup, render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { afterEach, describe, expect, it, vi } from 'vitest'; import type { InstanceContext } from '../app-shell.js'; import { CallsModule, type CallsDataSource, type CallsSnapshot } from './calls-module.js'; afterEach(cleanup); const owner: InstanceContext = { id: 'alpha', name: 'Alpha', origin: 'https://alpha.example', status: 'online', authentication: 'authenticated', freshness: 'fresh', }; const snapshot: CallsSnapshot = { observedAt: '2026-07-17T12:30:00Z', calls: { state: 'active', active: 2, ringing: 1, held: 0 }, devices: { state: 'available', total: 4, online: 3, busy: 2 }, }; function deferredSource() { let resolvePending!: (value: CallsSnapshot) => void; const load = vi.fn( (_instanceId, _signal) => new Promise((done) => { void _instanceId; void _signal; resolvePending = done; }), ); return { source: { load }, load, resolve: (value: CallsSnapshot) => resolvePending(value) }; } describe('isolated Calls read module', () => { it('loads only through the injected source and renders aggregate call and device status', async () => { const pending = deferredSource(); render(); expect(screen.getByRole('status', { name: '通话加载状态' })).toBeTruthy(); expect(pending.load).toHaveBeenCalledWith('alpha', expect.any(AbortSignal)); pending.resolve(snapshot); const calls = await screen.findByRole('region', { name: '通话状态' }); const devices = screen.getByRole('region', { name: '设备状态' }); expect(within(calls).getByText('活跃')).toBeTruthy(); expect(within(calls).getByText('2')).toBeTruthy(); expect(within(devices).getByText('可用')).toBeTruthy(); expect(screen.getByText(/观测时间:2026-07-17T12:30:00Z/)).toBeTruthy(); }); it('strictly allowlists aggregate fields and exposes no phone numbers, audio, logs, or controls', async () => { const unsafe = { ...snapshot, phoneNumber: '+1-555-0100', audio: 'private recording body', logBody: 'private call log body', calls: { ...snapshot.calls, caller: '+1-555-0101', transcript: 'private transcript' }, devices: { ...snapshot.devices, phoneNumber: '+1-555-0102', audioUrl: 'secret-audio-url' }, } as CallsSnapshot; render( unsafe }} />); expect(await screen.findByText('可用')).toBeTruthy(); expect(document.body.textContent).not.toMatch(/555|private|secret-audio/i); expect(screen.queryByRole('button')).toBeNull(); }); it('fails closed without authentication or an injected source', async () => { const load = vi.fn().mockResolvedValue(snapshot); const { rerender } = render( , ); expect(screen.getByRole('alert').textContent).toMatch(/需要先完成认证/i); expect(load).not.toHaveBeenCalled(); rerender(); expect(screen.getByRole('status', { name: '通话不可用' }).textContent).toMatch( /没有可用的安全通话只读数据源.*不会臆造.*生产端点/i, ); }); it('uses fixed safe errors, retries, and retains the same owner snapshot on refresh failure', async () => { const user = userEvent.setup(); const load = vi .fn() .mockResolvedValueOnce(snapshot) .mockRejectedValueOnce(new Error('secret endpoint and telephone')) .mockResolvedValueOnce({ ...snapshot, calls: { ...snapshot.calls, active: 3 } }); const source = { load }; const { rerender } = render( , ); expect(await screen.findByText('可用')).toBeTruthy(); rerender(); const alert = await screen.findByRole('alert'); expect(alert.textContent).toMatch(/正在显示上次已知的通话数据/i); expect(alert.textContent).not.toMatch(/secret endpoint|telephone/i); expect(screen.getByText('可用')).toBeTruthy(); await user.click(screen.getByRole('button', { name: '重试加载通话' })); expect( await within(screen.getByRole('region', { name: '通话状态' })).findByText('3'), ).toBeTruthy(); }); it('aborts superseded reads and fences late data from another owner', async () => { const alpha = deferredSource(); const bravo = deferredSource(); const load = vi.fn((id, signal) => id === 'alpha' ? alpha.source.load(id, signal) : bravo.source.load(id, signal), ); const source = { load }; const { rerender } = render(); const alphaSignal = load.mock.calls[0]?.[1]; rerender( , ); expect(alphaSignal?.aborted).toBe(true); alpha.resolve({ ...snapshot, calls: { state: 'late-alpha' } }); bravo.resolve({ ...snapshot, calls: { state: 'current-bravo' } }); expect(await screen.findByText('current-bravo')).toBeTruthy(); expect(screen.queryByText('late-alpha')).toBeNull(); }); });