import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; import { privacyDeletionProgress } from '../src/pages/privacy-detail.ts'; const webRoot = fileURLToPath(new URL('..', import.meta.url)); const requireDependency = createRequire(path.join(webRoot, 'package.json')); const ts = requireDependency('typescript'); const React = requireDependency('react'); const { renderToStaticMarkup } = requireDependency('react-dom/server'); const source = fs.readFileSync(process.env.PRIVACY_CARD_SOURCE || path.join(webRoot, 'src/pages/SaaSWorkspace.tsx'), 'utf8'); // Exercise the production card without importing the entire workspace or its // unrelated browser-only integrations. Its display helpers are small stubs. const cardSource = source.slice(source.indexOf('function PrivacyRequestCard('), source.indexOf('function Info(')); assert.ok(cardSource, 'the privacy card must exist'); const compiled = ts.transpileModule(cardSource + '\nexports.PrivacyRequestCard = PrivacyRequestCard;', { compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX, target: ts.ScriptTarget.ES2022 } }); const module = { exports: {} }; new Function('require', 'exports', 'useState', 'useRef', 'privacyDeletionProgress', 'formatDate', 'privacyRequestTypeLabel', 'privacyRequestStatus', 'privacyStatusLabel', 'privacyCategoryLabel', 'privacyRequestSummary', 'privacySubsystemLabel', 'privacySubtaskStatusLabel', 'Info', compiled.outputText)( requireDependency, module.exports, React.useState, React.useRef, privacyDeletionProgress, value => value, () => '删除数据', item => item.status, value => value, value => value, () => '', value => value, value => value, ({ label, value }) => React.createElement('div', {}, label, value), ); const tasks = ['COMPLETED', 'SKIPPED', 'COMPLETED', 'COMPLETED', 'FAILED', 'PENDING'].map((status, index) => ({ id: `private-task-${index}`, subsystem: ['redis', 'export_files', 'object_storage', 'pgvector', 'provider_index', 'postgresql'][index], status, attempts: status === 'FAILED' ? 8 : 0, last_error: status === 'FAILED' ? 'HTTP 429 synthetic-stack' : undefined, next_attempt_at: status === 'FAILED' ? '2026-10-04T14:48:45Z' : undefined })); const request = { id: 'private-request-id', subject_id: 'private-subject-id', request_type: 'DELETE', status: 'EXECUTING', version: 2, created_at: '2026-10-04T10:00:00Z', updated_at: '2026-10-04T14:48:15Z', subtasks: tasks, result_summary: {} }; const render = overrides => renderToStaticMarkup(React.createElement(module.exports.PrivacyRequestCard, { request, subjectName: '测试长者', selected: false, loading: false, onRefresh() {}, ...overrides })); const visibleSummary = markup => markup.slice(0, markup.indexOf(']*>/g, ''); test('normal view keeps evidence count and controls while technical data stays collapsed', () => { const markup = render({ readAt: '2026-10-06T00:00:00Z' }); const summary = visibleSummary(markup); assert.match(summary, /尚未全部清除/); assert.match(summary, /已核对 4\/6/); assert.match(summary, /最近核验:2026-10-06T00:00:00Z/); assert.match(summary, /刷新/); assert.match(summary, /复制请求编号/); assert.doesNotMatch(summary, /private-|HTTP 429|provider_index|v2|已尝试/); assert.match(markup, /
诊断详情/); assert.doesNotMatch(markup, /]* open/); assert.match(markup, /HTTP 429 synthetic-stack/); assert.match(markup, /2026-10-04T14:48:45Z/); assert.match(markup, /不代表仍会自动重试/); }); test('list-only state does not misrepresent an empty array as verified progress', () => { const summary = visibleSummary(render({ request: { ...request, subtasks: [] } })); assert.match(summary, /详情尚未读取或已更新/); assert.match(summary, /最近核验:尚未读取详情/); assert.doesNotMatch(summary, /0\/0|2026-10-04T14:48:15Z/); }); test('failed refresh keeps its original verification time and hides the technical exception', () => { const markup = render({ readAt: '2026-10-05T00:00:00Z', detailError: 'synthetic-detail-502' }); assert.match(visibleSummary(markup), /最近核验:2026-10-05T00:00:00Z(最新刷新失败,保留上次结果)/); assert.doesNotMatch(visibleSummary(markup), /synthetic-detail-502/); assert.match(markup, /最近读取异常:synthetic-detail-502/); }); test('loading refresh disables the read action without offering deletion retry', () => { const markup = render({ loading: true }); assert.match(markup, /disabled="">正在核验/); assert.doesNotMatch(visibleSummary(markup), /重新删除|重试删除|重新入队/); }); test('a completed parent with failed children stays incomplete in both badge and summary', () => { const summary = visibleSummary(render({ request: { ...request, status: 'COMPLETED' } })); assert.match(summary, /尚未全部清除/); assert.match(summary, /核验信息不一致/); assert.doesNotMatch(summary, /已核对完成/); });