const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const http = require('node:http') const { execFileSync } = require('node:child_process') const dependencies = '/Users/macclaw/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules' const { chromium } = require(path.join(dependencies, 'playwright')) const { Document, Packer, Paragraph } = require(path.join(dependencies, 'docx')) const { PDFDocument, StandardFonts } = require(path.join(dependencies, 'pdf-lib')) const directory = path.resolve(__dirname, '../../acceptance') const base = 'http://127.0.0.1:18080/api/v1' const database = 'yawan_native_acceptance_20260915' const sql = statement => execFileSync('docker', ['--context', 'colima-yawan', 'exec', 'WeKnora-postgres', 'psql', '-U', 'postgres', '-d', database, '-v', 'ON_ERROR_STOP=1', '-Atc', statement], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim() const literal = value => "'" + String(value).replaceAll("'", "''") + "'" const checks = [], errors = [], modelCalls = [] let auth, model, human, channel, browser, page, stage = 'login' const check = (name, passed) => { checks.push({ name, passed: !!passed }); assert.ok(passed, name) } const server = http.createServer(async (request, response) => { try { let text = ''; for await (const part of request) text += part const body = JSON.parse(text), system = body.messages[0].content, input = JSON.parse(body.messages.at(-1).content) const schema = JSON.parse(system.slice(system.indexOf('{'), system.indexOf('}') + 1)) modelCalls.push(input) const evidence = input.sources.map(source => source.text || source.Text).join('; ') const content = JSON.stringify(Object.fromEntries(Object.keys(schema).map(key => [key, `Evidence: ${evidence}`]))) response.writeHead(200, { 'Content-Type': 'application/json' }) response.end(JSON.stringify({ id: 'digital-parity', model: 'digital-parity', choices: [{ index: 0, message: { role: 'assistant', content }, finish_reason: 'stop' }], usage: { prompt_tokens: 30, completion_tokens: 30, total_tokens: 60 } })) } catch { response.writeHead(500); response.end('{}') } }) async function request(route, method = 'GET', body, expected = 200) { const response = await fetch(base + route, { method, headers: { 'Content-Type': 'application/json', ...(auth ? { Authorization: `Bearer ${auth.token}`, 'X-Tenant-ID': '10059' } : {}) }, body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(45000) }) assert.equal(response.status, expected, `${stage}: ${method} ${route} HTTP ${response.status}`) return response.json() } async function main() { const credentials = JSON.parse(fs.readFileSync(path.join(directory, 'super-admin-login.json'), 'utf8')) auth = await request('/auth/login', 'POST', { email: credentials.email, password: credentials.password }) await new Promise(resolve => server.listen(18083, '0.0.0.0', resolve)) try { stage = 'create_fixtures' model = (await request('/models', 'POST', { name: `digital-parity-${Date.now()}`, type: 'KnowledgeQA', source: 'remote', parameters: { base_url: 'http://host.docker.internal:18083/v1', api_key: 'local-test-only', provider: 'generic' } }, 201)).data human = (await request('/digital-humans', 'POST', { name: `数字人迁移验收-${Date.now()}`, description: '临时测试,完成后删除', model_id: model.id }, 201)).data const primary = (await request(`/agents/${human.primary_agent_id}`)).data check('automatic_same_name_agent_and_binding', primary.name === human.name && human.bindings.length === 1 && human.bindings[0].agent_id === primary.id) stage = 'document_upload' const docx = await Packer.toBuffer(new Document({ sections: [{ children: [new Paragraph('DOCX_PARITY_EVIDENCE: Explain a concept, demonstrate an example, and summarize the lesson.')] }] })) const pdf = await PDFDocument.create(), font = await pdf.embedFont(StandardFonts.Helvetica), pdfPage = pdf.addPage([600, 300]) pdfPage.drawText('PDF_PARITY_EVIDENCE: Use a clear example and conclude with a recap.', { x: 25, y: 220, size: 12, font }) const form = new FormData() form.set('revision', String(human.revision)); form.set('model_id', model.id) form.append('files', new Blob([docx], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }), 'parity-evidence.docx') form.append('files', new Blob([await pdf.save()], { type: 'application/pdf' }), 'parity-evidence.pdf') const upload = await fetch(`${base}/digital-humans/${human.id}/distill`, { method: 'POST', headers: { Authorization: `Bearer ${auth.token}`, 'X-Tenant-ID': '10059' }, body: form, signal: AbortSignal.timeout(30000) }) check('docx_pdf_upload_accepted', upload.status === 202) const job = (await upload.json()).data, start = Date.now(), progress = [] let current do { await new Promise(resolve => setTimeout(resolve, 500)) current = (await request(`/digital-humans/${human.id}/jobs/${job.id}`)).data if (progress.at(-1)?.stage !== current.stage) progress.push({ status: current.status, stage: current.stage, progress: current.progress }) } while (['running', 'queued'].includes(current.status) && Date.now() - start < 150000) fs.writeFileSync(path.join(directory, 'digital-parity-document-progress.json'), JSON.stringify({ duration_ms: Date.now() - start, progress, error: current.error }, null, 2)) check('docx_pdf_real_reader_completed', current.status === 'completed') human = (await request(`/digital-humans/${human.id}`)).data const evidence = modelCalls.flatMap(call => call.sources || []).map(source => source.text || source.Text).join('\n') fs.writeFileSync(path.join(directory, 'digital-parity-document-evidence.json'), JSON.stringify(modelCalls.flatMap(call => call.sources || []), null, 2)) check('both_document_markers_reach_model', evidence.includes('Explain a concept, demonstrate an example, and summarize the lesson.') && evidence.includes('Use a clear example and conclude with a recap')) stage = 'wechat_browser' channel = { id: require('node:crypto').randomUUID(), name: '验收专用已停用连接' } sql(`INSERT INTO im_channels (id, tenant_id, agent_id, platform, name, enabled, mode, output_mode, session_mode, credentials, created_at, updated_at) VALUES (${literal(channel.id)},10059,${literal(human.primary_agent_id)},'wechat',${literal(channel.name)},false,'longpoll','full','user','{}',now(),now())`) browser = await chromium.launch({ headless: true, channel: 'chrome' }) const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } }) await context.addInitScript(a => { const values = { weknora_user: JSON.stringify({ ...a.user, tenant_id: String(a.user.tenant_id) }), weknora_tenant: JSON.stringify({ ...a.active_tenant, id: '10059' }), weknora_token: a.token, weknora_refresh_token: a.refresh_token, weknora_memberships: JSON.stringify(a.memberships) } for (const [key, value] of Object.entries(values)) localStorage.setItem(key, value) localStorage.setItem('weknora:new-user-guide-done:v1', '1') }, auth) page = await context.newPage(); page.setDefaultTimeout(15000) page.on('pageerror', error => errors.push(error.message)) await page.goto('http://127.0.0.1:15173/platform/digital-humans') const row = page.locator('.human-row').filter({ hasText: human.name }) await row.getByRole('button', { name: '微信连接', exact: true }).click() const panel = page.locator('.wechat-panel') await panel.getByText(channel.name, { exact: true }).waitFor() await panel.getByRole('button', { name: '删除微信连接', exact: true }).waitFor() check('wechat_delete_control_visible', await panel.getByRole('button', { name: '删除微信连接', exact: true }).isVisible()) await page.screenshot({ path: path.join(directory, 'digital-wechat-desktop.png'), animations: 'disabled' }) await page.setViewportSize({ width: 390, height: 844 }) await page.screenshot({ path: path.join(directory, 'digital-wechat-mobile.png'), animations: 'disabled' }) check('wechat_mobile_no_horizontal_overflow', await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 2)) await page.setViewportSize({ width: 1440, height: 1000 }) const qrResponse = page.waitForResponse(response => response.url().endsWith(`/digital-humans/${human.id}/wechat/qrcode`) && response.request().method() === 'POST', { timeout: 50000 }) await panel.getByRole('button', { name: '连接微信', exact: true }).click() const response = await qrResponse check('official_wechat_qrcode_response', response.status() === 200) const dialog = page.locator('.t-dialog:visible') await dialog.locator('canvas, .t-qrcode svg').first().waitFor() check('wechat_qrcode_renders', await dialog.locator('canvas, .t-qrcode svg').first().evaluate(element => { if (element.tagName.toLowerCase() === 'svg') return element.getBoundingClientRect().width > 100 && element.querySelectorAll('path,rect').length > 0 const data = element.getContext('2d').getImageData(0, 0, element.width, element.height).data let dark = 0, light = 0 for (let i = 0; i < data.length; i += 4) { if (data[i] < 80 && data[i + 3] > 0) dark++; if (data[i] > 200 && data[i + 3] > 0) light++ } return dark > 100 && light > 100 })) await page.screenshot({ path: path.join(directory, 'digital-wechat-qr-desktop.png'), animations: 'disabled' }) await page.setViewportSize({ width: 390, height: 844 }) await page.screenshot({ path: path.join(directory, 'digital-wechat-qr-mobile.png'), animations: 'disabled' }) check('wechat_qrcode_mobile_fits', await dialog.evaluate(element => { const box = element.getBoundingClientRect(); return box.left >= 0 && box.right <= innerWidth + 2 && box.top >= 0 && box.bottom <= innerHeight + 2 })) await dialog.locator('.t-dialog__close').click() await panel.getByRole('button', { name: '删除微信连接', exact: true }).click() await page.getByRole('button', { name: '删除连接', exact: true }).click() await panel.getByText(channel.name, { exact: true }).waitFor({ state: 'detached' }) check('wechat_delete_persisted', (await request(`/digital-humans/${human.id}/wechat/channels`)).data.length === 0) channel = undefined check('no_browser_errors', errors.length === 0) } finally { if (page && checks.some(item => !item.passed)) await page.screenshot({ path: path.join(directory, 'digital-parity-failure.png'), animations: 'disabled' }).catch(() => {}) if (browser) await browser.close() if (human) { await request(`/digital-humans/${human.id}`, 'DELETE').catch(() => {}) const humanID = literal(human.id), agentID = literal(human.primary_agent_id) sql(`BEGIN; DELETE FROM im_channels WHERE agent_id=${agentID}; DELETE FROM digital_human_jobs WHERE digital_human_id=${humanID}; DELETE FROM digital_human_bindings WHERE digital_human_id=${humanID}; DELETE FROM digital_human_versions WHERE digital_human_id=${humanID}; DELETE FROM digital_humans WHERE id=${humanID}; DELETE FROM custom_agents WHERE id=${agentID}; COMMIT;`) check('temporary_human_agent_removed', sql(`SELECT count(*) FROM digital_humans WHERE id=${humanID}`) === '0' && sql(`SELECT count(*) FROM custom_agents WHERE id=${agentID}`) === '0') } if (model) sql(`DELETE FROM models WHERE id=${literal(model.id)}`) await new Promise(resolve => server.close(resolve)) const result = { time: new Date().toISOString(), stage, checks, browser_errors: errors, model_calls: modelCalls.length, model: 'local deterministic stub', limits: ['No real WeChat scan or message', 'Text PDF tested; scanned PDF OCR not tested'] } fs.writeFileSync(path.join(directory, 'digital-parity-live-20260916.json'), JSON.stringify(result, null, 2)) console.log(JSON.stringify(result, null, 2)) } } main().catch(error => { console.error(`${stage}: ${error.message}`); process.exitCode = 1 })