import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('@tarojs/taro', () => ({ default: { getStorageSync: vi.fn(() => []), setStorage: vi.fn(), removeStorageSync: vi.fn(), }, })); vi.mock('@/services/request', () => ({ api: { post: vi.fn().mockResolvedValue({ success: true }) }, })); describe('Analytics PII 清理', () => { beforeEach(() => { vi.clearAllMocks(); }); it('flushEvents 发送的 batch 不含 PII', async () => { const { trackEvent, flushEvents } = await import('@/services/analytics'); const { api } = await import('@/services/request'); trackEvent('page_view', { page: 'health', userId: 'should-be-removed', patientId: 'should-be-removed', user_name: 'should-be-removed', phone: 'should-be-removed', id_card: 'should-be-removed', }); await flushEvents(); const postCall = vi.mocked(api.post).mock.calls[0]; const body = postCall[1] as { events: Array> }; const evt = body.events[0]; // 事件级别不应有 userId/patientId expect(evt).not.toHaveProperty('userId'); expect(evt).not.toHaveProperty('patientId'); // properties 中不应有 PII 字段 const props = evt.properties as Record; expect(props).not.toHaveProperty('userId'); expect(props).not.toHaveProperty('patientId'); expect(props).not.toHaveProperty('user_name'); expect(props).not.toHaveProperty('phone'); expect(props).not.toHaveProperty('id_card'); // 正常字段保留 expect(props.page).toBe('health'); }); it('trackEvent 不在事件级别包含 userId/patientId', async () => { const { trackEvent, flushEvents } = await import('@/services/analytics'); const { api } = await import('@/services/request'); trackEvent('test_event'); await flushEvents(); const postCall = vi.mocked(api.post).mock.calls[0]; const body = postCall[1] as { events: Array> }; const evt = body.events[0]; expect(evt).not.toHaveProperty('userId'); expect(evt).not.toHaveProperty('patientId'); }); it('sanitizeProperties 过滤所有 PII 标识字段', async () => { const { trackEvent, flushEvents } = await import('@/services/analytics'); const { api } = await import('@/services/request'); trackEvent('test', { openid: 'oXXX', access_token: 'tok', refresh_token: 'ref', email: 'test@test.com', address: '某地', mobile: '13800001111', page: 'settings', // 非 PII 字段 }); await flushEvents(); const postCall = vi.mocked(api.post).mock.calls[0]; const body = postCall[1] as { events: Array> }; const props = body.events[0].properties as Record; // 全部 PII 被过滤,只剩 page expect(props).not.toHaveProperty('openid'); expect(props).not.toHaveProperty('access_token'); expect(props).not.toHaveProperty('refresh_token'); expect(props).not.toHaveProperty('email'); expect(props).not.toHaveProperty('address'); expect(props).not.toHaveProperty('mobile'); expect(props.page).toBe('settings'); }); });