"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const pet = require("../pet-runtime.js"); const bridge = require("../bridge-client.js"); const wireContract = require("../../Contracts/erp-agent-wire-contract-v1.json"); test("Codex v1 atlas geometry and all nine rows are exact", () => { assert.deepEqual(pet.ATLAS, { columns: 8, rows: 9, frameWidth: 192, frameHeight: 208, width: 1536, height: 1872 }); assert.equal(pet.ANIMATIONS.idle.row, 0); assert.equal(pet.ANIMATIONS["running-right"].frames, 8); assert.equal(pet.ANIMATIONS["running-left"].row, 2); assert.deepEqual(pet.ANIMATIONS.waiting.durations, [150, 150, 150, 150, 150, 260]); assert.equal(pet.ANIMATIONS.review.row, 8); }); test("semantic ERP states map to visible pet states", () => { assert.equal(pet.animationForSemanticState("thinking"), "review"); assert.equal(pet.animationForSemanticState("awaiting_confirmation"), "waiting"); assert.equal(pet.animationForSemanticState("executing"), "running"); assert.equal(pet.animationForSemanticState("success"), "jumping"); assert.equal(pet.animationForSemanticState("error"), "failed"); assert.throws(() => pet.animationForSemanticState("write_without_confirmation"), /Unknown pet state/); }); test("frame CSS selects the correct atlas cell", () => { const first = pet.frameStyle("idle", 0, "asset.webp"); const last = pet.frameStyle("review", 5, "asset.webp"); assert.equal(first.backgroundSize, "800% 900%"); assert.equal(first.backgroundPosition, "0% 0%"); assert.equal(last.backgroundPosition, (5 / 7) * 100 + "% 100%"); assert.throws(() => pet.frameStyle("waving", 4, "asset.webp"), RangeError); }); test("animator follows per-frame duration and cancels stale schedules", () => { const scheduled = []; const element = { style: {}, dataset: {} }; const animator = new pet.AtlasAnimator(element, { setTimer(callback, delay) { scheduled.push({ callback, delay }); return scheduled.length; }, clearTimer() {} }); animator.play("waving"); assert.equal(scheduled[0].delay, 140); scheduled[0].callback(); assert.equal(element.dataset.frame, "1"); animator.play("waiting"); assert.equal(element.dataset.animation, "waiting"); scheduled[1].callback(); assert.equal(element.dataset.animation, "waiting"); }); test("bridge client keeps one idempotency key for retries of the same plan", async () => { const requests = []; const transport = { async send(request) { requests.push(request); return validResponse(request, executionData()); } }; const client = new bridge.BridgeClient(transport, { clientSessionId: "session-test" }); const planId = "0123456789abcdef0123456789abcdef"; const correlationId = "plan-correlation-01234567"; await client.execute(planId, correlationId); await client.execute(planId, correlationId); assert.equal(requests.length, 2); assert.equal(requests[0].payload.idempotencyKey, requests[1].payload.idempotencyKey); assert.equal(requests[0].clientSessionId, "session-test"); assert.equal(requests[0].correlationId, correlationId); assert.equal(requests[1].correlationId, correlationId); }); test("bridge client gives a followup plan an independent idempotency key", async () => { const requests = []; const transport = { async send(request) { requests.push(request); return validResponse(request, executionData()); } }; const client = new bridge.BridgeClient(transport, { clientSessionId: "session-followup" }); await client.execute( "0123456789abcdef0123456789abcdef", "plan-correlation-01234567"); await client.execute( "fedcba9876543210fedcba9876543210", "plan-correlation-01234567"); assert.notEqual(requests[0].payload.idempotencyKey, requests[1].payload.idempotencyKey); }); test("bridge client binds followup plans to the trusted ERP correlation", async () => { const correlationId = "plan-correlation-76543210"; const followupPlan = { planId: "fedcba9876543210fedcba9876543210", commandName: "hr.leave.submit" }; const client = new bridge.BridgeClient({ async send(request) { return validResponse(request, executionData({ followupPlan: { ...followupPlan, bridgeCorrelationId: "forged-correlation" } })); } }, { clientSessionId: "session-correlated-followup" }); const data = await client.execute( "0123456789abcdef0123456789abcdef", correlationId); assert.equal(data.bridgeCorrelationId, correlationId); assert.equal(data.followupPlan.bridgeCorrelationId, correlationId); assert.equal(data.followupPlan.planId, followupPlan.planId); }); test("bridge client binds response protocol request and correlation", async () => { const mismatches = [ request => ({ ...validResponse(request, {}), protocolVersion: "2.0" }), request => ({ ...validResponse(request, {}), requestId: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }), request => ({ ...validResponse(request, {}), correlationId: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }), request => ({ ...validResponse(request, {}), unexpected: true }), request => ({ ...validResponse(request, {}), success: 1 }) ]; for (const makeResponse of mismatches) { const client = new bridge.BridgeClient({ async send(request) { return makeResponse(request); } }, { clientSessionId: "session-protocol" }); await assert.rejects( client.health(), error => error && error.code === "bridge_protocol_error" ); } }); test("bridge client preserves a valid business error only after binding", async () => { const recovery = { action: "inspect_existing_record", retryable: false, planInvalidated: true, message: "先查询现有发票,再重新生成预览。" }; const client = new bridge.BridgeClient({ async send(request) { return { ...validResponse(request, { recovery }), success: false, code: "duplicate_invoice", message: "该供应商的发票号码已经存在。" }; } }, { clientSessionId: "session-business-error" }); let boundError; await assert.rejects( client.health(), error => { boundError = error; return error && error.code === "duplicate_invoice" && error.message === "该供应商的发票号码已经存在。" && error.response.data.recovery.action === "inspect_existing_record"; } ); const summary = pet.bridgeFailureSummary(boundError); assert.equal(summary.planInvalidated, true); assert.equal(summary.retryable, false); assert.match(summary.text, /下一步:先查询现有发票.*错误码:duplicate_invoice/); assert.match(summary.text, /关联 ID:/); const timeout = pet.bridgeFailureSummary({ code: "bridge_timeout", message: "untrusted SQL SELECT password" }); assert.equal(timeout.retryable, true); assert.equal(timeout.planInvalidated, false); assert.doesNotMatch(timeout.text, /password|SELECT/); const malformedRecoveries = [ {}, { recovery: { ...recovery, action: "run_sql" } }, { recovery: { ...recovery, retryable: "false" } }, { recovery: { ...recovery, sql: "select secret" } }, { recovery: { ...recovery, message: "下一步\n执行" } } ]; for (const data of malformedRecoveries) { const malformedClient = new bridge.BridgeClient({ async send(request) { return { ...validResponse(request, data), success: false, code: "duplicate_invoice", message: "该供应商的发票号码已经存在。" }; } }, { clientSessionId: "session-malformed-recovery" }); await assert.rejects( malformedClient.health(), error => error && error.code === "bridge_protocol_error" ); } }); test("diagnostic execution receipt is strict and gives a safe next-turn summary", async () => { const correlationId = "diagnostic-correlation-01234567"; const client = new bridge.BridgeClient({ async send(request) { return validResponse( request, diagnosticExecutionData(request.correlationId)); } }, { clientSessionId: "session-diagnostic-result" }); const data = await client.execute( "0123456789abcdef0123456789abcdef", correlationId); const summary = pet.diagnosticExecutionSummary(data.result); assert.match(summary, /数据库字段不存在.*下一轮对话.*具体哪里配置错了/); assert.doesNotMatch(summary, /password|SELECT|physical_table/i); const malformedClient = new bridge.BridgeClient({ async send(request) { const responseData = diagnosticExecutionData(request.correlationId); responseData.result.data.sql = "SELECT password FROM physical_table"; return validResponse(request, responseData); } }, { clientSessionId: "session-diagnostic-injection" }); await assert.rejects( malformedClient.execute( "fedcba9876543210fedcba9876543210", correlationId), error => error && error.code === "bridge_protocol_error" ); }); test("bridge client rejects invalid request shapes before transport", async () => { let calls = 0; const client = new bridge.BridgeClient({ async send(request) { calls += 1; return validResponse(request, {}); } }, { clientSessionId: "session-request-shape" }); await assert.rejects(client.plan("module.diagnose", []), /计划请求格式无效/); await assert.rejects( client.execute("not-a-server-plan", "plan-correlation-01234567"), /执行请求格式无效/); await assert.rejects( client.execute("0123456789abcdef0123456789abcdef"), /执行请求格式无效/); assert.equal(calls, 0); }); test("atlas validation rejects dimensions that could crop guga", () => { assert.equal(pet.validateAtlas(1536, 1872), true); assert.equal(pet.validateAtlas(1536, 1792), false); }); test("anchor click focuses a hover-opened panel before allowing toggle close", () => { assert.equal(pet.panelOpenAfterAnchorClick(false, false), true); assert.equal(pet.panelOpenAfterAnchorClick(true, true), true); assert.equal(pet.panelOpenAfterAnchorClick(true, false), false); assert.throws( () => pet.panelOpenAfterAnchorClick("open", false), TypeError); }); test("pet quick actions are fixed context-aware prompts", () => { assert.match( pet.quickActionPrompt("purchase_invoice_entry"), /先读取当前 ERP 上下文和能力.*不要生成写入计划/ ); assert.match( pet.quickActionPrompt("leave_request"), /原始日期表达与时段.*不得替我补全或猜测/ ); assert.equal( pet.quickActionPrompt("current_module_help"), "当前 ERP 界面有哪些功能?请先读取实时上下文,并只根据当前模块的用户级功能说明回答。" ); assert.match( pet.quickActionPrompt("current_module_diagnosis"), /只读配置诊断.*不要复现初始化.*不要生成修复 SQL/ ); assert.match( pet.quickActionPrompt("workflow_readiness"), /ERP 内置管理员.*adapters\.status.*不要泄露配置证据、SQL 或物理字段/ ); assert.throws( () => pet.quickActionPrompt("execute_current_module"), /Unknown pet quick action/ ); }); test("admin workflow readiness quick action requires trusted administrator scope", () => { const context = { userId: "1", userName: "管理员", accountBook: "测试账套", subSystemId: "SCM", subSystemName: "供应链", databaseScopeFingerprint: "b".repeat(64), isAdministrator: true }; assert.equal(pet.quickActionVisible("workflow_readiness", context), true); assert.equal(pet.quickActionVisible("purchase_invoice_entry", null), true); const ordinaryUser = structuredClone(context); ordinaryUser.userId = "2"; ordinaryUser.userName = "普通用户"; ordinaryUser.isAdministrator = false; assert.equal(pet.quickActionVisible("workflow_readiness", ordinaryUser), false); const malformedAdministrator = structuredClone(context); malformedAdministrator.databaseScopeFingerprint = "B".repeat(64); assert.equal( pet.quickActionVisible("workflow_readiness", malformedAdministrator), false ); assert.throws( () => pet.quickActionVisible("unknown_action", context), /Unknown pet quick action/ ); }); test("ERP session scope summary is complete bounded and database-evidence only", () => { const context = { userId: "USER-001", userName: "测试用户", accountBook: "生产账套甲", subSystemId: "SCM-01", subSystemName: "采购管理", databaseScopeFingerprint: "a".repeat(64), isAdministrator: false }; const summary = pet.erpSessionScopeSummary(context); assert.equal(summary.complete, true); assert.equal(summary.userId, "USER-001"); assert.equal(summary.accountBook, "生产账套甲"); assert.equal(summary.subSystemId, "SCM-01"); assert.equal(summary.databaseEvidence, "a".repeat(12)); assert.equal("databaseScopeFingerprint" in summary, false); const missingAccount = structuredClone(context); delete missingAccount.accountBook; assert.equal(pet.erpSessionScopeSummary(missingAccount).complete, false); const uppercaseFingerprint = structuredClone(context); uppercaseFingerprint.databaseScopeFingerprint = "A".repeat(64); assert.equal( pet.erpSessionScopeSummary(uppercaseFingerprint).complete, false); const controlText = structuredClone(context); controlText.userName = "测试\u0085用户"; assert.equal(pet.erpSessionScopeSummary(controlText).complete, false); const forgedAdministrator = structuredClone(context); forgedAdministrator.isAdministrator = "false"; assert.equal( pet.erpSessionScopeSummary(forgedAdministrator).complete, false); assert.equal(pet.erpSessionScopeSummary(null).complete, false); }); test("purchase execution requires complete trusted header and line previews", () => { const plan = structuredClone(wireContract.scenarios.find( scenario => scenario.name === "purchase_resolve_to_create").plan); const preview = pet.purchaseLinePreview(plan); assert.equal(preview.required, true); assert.equal(preview.complete, true); assert.equal(preview.header.invoiceNumber, "INV-CONTRACT-1"); assert.equal(preview.header.sourceDocumentCount, 1); assert.equal(preview.lines[0].sourceOrderNumber, "PO-1"); assert.equal(pet.isExecutablePlan(plan), true); const missingSupplier = structuredClone(plan); delete missingSupplier.preview["供应商"]; delete missingSupplier.data.preview["供应商"]; assert.equal(pet.purchaseLinePreview(missingSupplier).complete, false); assert.equal(pet.isExecutablePlan(missingSupplier), false); const forgedHeaderTotal = structuredClone(plan); forgedHeaderTotal.preview["价税合计"] = 999; forgedHeaderTotal.data.preview["价税合计"] = 999; assert.equal(pet.purchaseLinePreview(forgedHeaderTotal).complete, false); assert.equal(pet.isExecutablePlan(forgedHeaderTotal), false); const forgedLineCount = structuredClone(plan); forgedLineCount.preview["发票行数"] = 2; forgedLineCount.data.preview["发票行数"] = 2; assert.equal(pet.purchaseLinePreview(forgedLineCount).complete, false); assert.equal(pet.isExecutablePlan(forgedLineCount), false); const missingQuantity = structuredClone(plan); delete missingQuantity.data.lineMatches[0].invoiceQuantity; assert.equal(pet.purchaseLinePreview(missingQuantity).complete, false); assert.equal(pet.isExecutablePlan(missingQuantity), false); const ambiguous = structuredClone(plan); ambiguous.data.lineMatches[0].candidateCount = 2; assert.equal(pet.purchaseLinePreview(ambiguous).complete, false); assert.equal(pet.isExecutablePlan(ambiguous), false); assert.equal(pet.previewReviewComplete(0, 340, 800), false); assert.equal(pet.previewReviewComplete(458, 340, 800), true); assert.equal(pet.previewReviewComplete(460, 340, 800), true); assert.equal(pet.previewReviewComplete(Number.NaN, 340, 800), false); }); test("leave create and submit require complete trusted confirmation previews", () => { const createPlan = wireContract.scenarios.find( scenario => scenario.name === "leave_resolve_to_create").plan; const createPreview = pet.leaveConfirmationPreview(createPlan); assert.equal(createPreview.required, true); assert.equal(createPreview.complete, true); assert.equal(createPreview.employeeId, "EMP-1"); assert.equal(createPreview.calculatedHours, 4); assert.equal(pet.isExecutablePlan(createPlan), true); const unicodeErpCode = structuredClone(createPlan); unicodeErpCode.preview["员工"] = "员工一号"; unicodeErpCode.data.preview["员工"] = "员工一号"; assert.equal(pet.leaveConfirmationPreview(unicodeErpCode).complete, true); const missingReason = structuredClone(createPlan); delete missingReason.preview["原因"]; delete missingReason.data.preview["原因"]; assert.equal(pet.leaveConfirmationPreview(missingReason).complete, false); assert.equal(pet.isExecutablePlan(missingReason), false); const forgedSubmitIntent = structuredClone(createPlan); forgedSubmitIntent.preview["创建后提交"] = true; forgedSubmitIntent.data.preview["创建后提交"] = true; assert.equal(pet.leaveConfirmationPreview(forgedSubmitIntent).complete, false); assert.equal(pet.isExecutablePlan(forgedSubmitIntent), false); const zonedTime = structuredClone(createPlan); zonedTime.preview["开始时间"] = "2026-08-12T13:00:00+08:00"; zonedTime.data.preview["开始时间"] = "2026-08-12T13:00:00+08:00"; assert.equal(pet.leaveConfirmationPreview(zonedTime).complete, false); const submitPlan = wireContract.scenarios.find( scenario => scenario.name === "leave_submit_followup").plan; assert.equal(pet.leaveConfirmationPreview(submitPlan).complete, true); assert.equal(pet.isExecutablePlan(submitPlan), true); const forgedAction = structuredClone(submitPlan); forgedAction.preview["动作"] = "删除申请"; forgedAction.data.preview["动作"] = "删除申请"; assert.equal(pet.leaveConfirmationPreview(forgedAction).complete, false); assert.equal(pet.isExecutablePlan(forgedAction), false); }); test("initialization trace requires complete scope limits and risk disclosure", () => { const plan = wireContract.scenarios.find( scenario => scenario.name === "module_trace_initialization").plan; const preview = pet.initializationTracePreview(plan); assert.equal(preview.required, true); assert.equal(preview.complete, true); assert.equal(preview.moduleName, "采购订单"); assert.equal(preview.forceTerminationSupported, false); assert.equal(pet.isExecutablePlan(plan), true); const missingModuleName = structuredClone(plan); delete missingModuleName.preview.moduleName; delete missingModuleName.data.preview.moduleName; assert.equal(pet.initializationTracePreview(missingModuleName).complete, false); assert.equal(pet.isExecutablePlan(missingModuleName), false); const forcedTermination = structuredClone(plan); forcedTermination.preview.forceTerminationSupported = true; forcedTermination.data.preview.forceTerminationSupported = true; assert.equal(pet.initializationTracePreview(forcedTermination).complete, false); assert.equal(pet.isExecutablePlan(forcedTermination), false); const changedLimit = structuredClone(plan); changedLimit.preview.maxEvents = 201; changedLimit.data.preview.maxEvents = 201; changedLimit.data.maxEvents = 201; assert.equal(pet.initializationTracePreview(changedLimit).complete, false); const missingWarning = structuredClone(plan); missingWarning.warnings = []; assert.equal(pet.initializationTracePreview(missingWarning).complete, false); assert.equal(pet.isExecutablePlan(missingWarning), false); }); test("dynamic module create requires bounded opaque master and detail previews", () => { const plan = structuredClone(wireContract.scenarios.find( scenario => scenario.name === "dynamic_module_resolve_to_create").plan); const preview = pet.dynamicModuleConfirmationPreview(plan); assert.equal(preview.required, true); assert.equal(preview.complete, true); assert.equal(preview.mode, "create"); assert.equal(preview.totalValues, 2); assert.equal(preview.masterValues[0].parameterId, "m0123456789abcdef"); assert.equal(preview.detailRows[0].values[0].value, "第一行"); assert.equal(pet.isExecutablePlan(plan), true); const emptyValue = structuredClone(plan); emptyValue.preview.masterValues[0].value = ""; bindDynamicCreateProjections(emptyValue); assert.equal(pet.dynamicModuleConfirmationPreview(emptyValue).complete, true); const projectionMismatch = structuredClone(plan); projectionMismatch.data.parameterPreview.masterValues[0].value = "被篡改"; assert.equal( pet.dynamicModuleConfirmationPreview(projectionMismatch).complete, false); assert.equal(pet.isExecutablePlan(projectionMismatch), false); const physicalFieldLeak = structuredClone(plan); physicalFieldLeak.preview.masterValues[0].physicalField = "CUSTOMER_NAME"; bindDynamicCreateProjections(physicalFieldLeak); assert.equal( pet.dynamicModuleConfirmationPreview(physicalFieldLeak).complete, false); const duplicateParameter = structuredClone(plan); duplicateParameter.preview.masterValues.push( structuredClone(duplicateParameter.preview.masterValues[0])); bindDynamicCreateProjections(duplicateParameter); assert.equal( pet.dynamicModuleConfirmationPreview(duplicateParameter).complete, false); const wrongRowNumber = structuredClone(plan); wrongRowNumber.preview.detailRows[0].rowNumber = 2; bindDynamicCreateProjections(wrongRowNumber); assert.equal( pet.dynamicModuleConfirmationPreview(wrongRowNumber).complete, false); const controlCharacter = structuredClone(plan); controlCharacter.preview.detailRows[0].values[0].value = "第一行\u0085"; bindDynamicCreateProjections(controlCharacter); assert.equal( pet.dynamicModuleConfirmationPreview(controlCharacter).complete, false); const aggregateTooLarge = structuredClone(plan); aggregateTooLarge.preview.masterValues = Array.from({ length: 5 }, (_, index) => ({ parameterId: "m" + index.toString(16).padStart(16, "0"), label: "大字段" + index, valueType: "string", value: "值".repeat(30000) })); bindDynamicCreateProjections(aggregateTooLarge); assert.equal( pet.dynamicModuleConfirmationPreview(aggregateTooLarge).complete, false); const exposedBlocker = structuredClone(plan); exposedBlocker.data.writeExecutionBlocker = "adapter_missing"; assert.equal( pet.dynamicModuleConfirmationPreview(exposedBlocker).complete, false); const changedVersion = structuredClone(plan); changedVersion.commandVersion = "2.0"; assert.equal(pet.dynamicModuleConfirmationPreview(changedVersion).complete, false); assert.equal(pet.isExecutablePlan(changedVersion), false); }); test("dynamic module update requires a bounded concurrent before-after preview", () => { const plan = structuredClone(wireContract.scenarios.find( scenario => scenario.name === "dynamic_module_resolve_to_update").plan); const preview = pet.dynamicModuleConfirmationPreview(plan); assert.equal(preview.required, true); assert.equal(preview.complete, true); assert.equal(preview.mode, "update"); assert.equal(preview.recordDisplay, "客户 C-001 / 朗速客户"); assert.equal(preview.changes[0].previousValue, "100.00"); assert.equal(preview.changes[0].newValue, "120.50"); assert.equal(pet.isExecutablePlan(plan), true); const missingPrevious = structuredClone(plan); delete missingPrevious.preview.changes[0].previousValue; bindDynamicUpdateProjection(missingPrevious); assert.equal( pet.dynamicModuleConfirmationPreview(missingPrevious).complete, false); const noActualChange = structuredClone(plan); noActualChange.preview.changes[0].newValue = "100.00"; bindDynamicUpdateProjection(noActualChange); assert.equal( pet.dynamicModuleConfirmationPreview(noActualChange).complete, false); assert.equal(pet.isExecutablePlan(noActualChange), false); const physicalFieldLeak = structuredClone(plan); physicalFieldLeak.preview.changes[0].columnName = "CREDIT_LIMIT"; bindDynamicUpdateProjection(physicalFieldLeak); assert.equal( pet.dynamicModuleConfirmationPreview(physicalFieldLeak).complete, false); const duplicateParameter = structuredClone(plan); const duplicate = structuredClone(duplicateParameter.preview.changes[0]); duplicate.newValue = "130.00"; duplicateParameter.preview.changes.push(duplicate); bindDynamicUpdateProjection(duplicateParameter); assert.equal( pet.dynamicModuleConfirmationPreview(duplicateParameter).complete, false); const unsafeValue = structuredClone(plan); unsafeValue.preview.changes[0].newValue = "120.50\u0000"; bindDynamicUpdateProjection(unsafeValue); assert.equal( pet.dynamicModuleConfirmationPreview(unsafeValue).complete, false); const projectionMismatch = structuredClone(plan); projectionMismatch.data.preview.changes[0].newValue = "999.99"; assert.equal( pet.dynamicModuleConfirmationPreview(projectionMismatch).complete, false); const forgedAdapter = structuredClone(plan); forgedAdapter.data.adapter.evidenceSha256 = "not-a-hash"; assert.equal( pet.dynamicModuleConfirmationPreview(forgedAdapter).complete, false); assert.equal(pet.isExecutablePlan(forgedAdapter), false); }); test("atlas asset must decode at the exact production dimensions", async () => { const loaded = await pet.loadAtlasAsset("https://asset.test/guga.webp", { createImage: () => fakeImage(1536, 1872) }); assert.deepEqual(loaded, { url: "https://asset.test/guga.webp", width: 1536, height: 1872 }); }); test("atlas asset fails closed on crop corruption and timeout", async () => { await assert.rejects( pet.loadAtlasAsset("https://asset.test/cropped.webp", { createImage: () => fakeImage(1536, 1792) }), error => error && error.code === "pet_sprite_dimensions_invalid" ); await assert.rejects( pet.loadAtlasAsset("https://asset.test/corrupt.webp", { createImage: () => fakeImage(1536, 1872, true) }), error => error && error.code === "pet_sprite_decode_failed" ); await assert.rejects( pet.loadAtlasAsset("https://asset.test/hung.webp", { createImage: () => ({ naturalWidth: 0, naturalHeight: 0 }), setTimer(callback) { callback(); return 1; }, clearTimer() {} }), error => error && error.code === "pet_sprite_load_timeout" ); }); test("invalid server plans can never enable execution", () => { assert.equal(pet.isExecutablePlan({ planId: "p1", valid: true, executionAllowed: true, risk: "write" }), true); assert.equal(pet.isExecutablePlan({ planId: "p2", valid: true, executionAllowed: true, risk: "navigate" }), true); assert.equal(pet.isExecutablePlan({ planId: "p3", valid: true, executionAllowed: true, risk: "critical" }), true); assert.equal(pet.isExecutablePlan({ planId: "p4", valid: true, executionAllowed: false, risk: "draft" }), false); assert.equal(pet.isExecutablePlan({ planId: "p5", valid: true, executionAllowed: false, risk: "read" }), false); assert.equal(pet.isExecutablePlan({ planId: "p6", valid: false, executionAllowed: true, risk: "write" }), false); assert.equal(pet.isExecutablePlan({ valid: true, executionAllowed: true, risk: "write" }), false); assert.equal(pet.isExecutablePlan(null), false); }); function validResponse(request, data) { return { protocolVersion: "1.0", requestId: request.requestId, correlationId: request.correlationId, success: true, code: "ok", message: null, data: data || {} }; } function executionData(overrides) { return { result: { success: true, code: "purchase_document_created", message: "采购业务单据已创建。", recordId: null, replayed: false, data: {} }, followupPlan: null, followupCode: null, ...(overrides || {}) }; } function diagnosticExecutionData(correlationId) { return executionData({ result: { success: true, code: "initialization_failure_captured", message: "已捕获模块初始化失败的诊断证据。", recordId: "hr_4011", replayed: false, data: { diagnosticContextSchemaVersion: "1.0", diagnosticId: "diag-0123456789abcdef0123456789abcdef", correlationId, evidencePersisted: true, evidenceContentHash: "b".repeat(64), outcome: "failed", primaryFindingCode: "missing_column", moduleOpenSucceeded: false, eventCount: 1, failedEventCount: 1, slowEventCount: 0, traceTruncated: false, summaryTruncated: false, findings: [{ severity: "error", code: "missing_column", category: "low_code_configuration", stage: "initialization_sql", confidence: "observed", message: "初始化引用的数据库字段不存在。", recommendation: "检查低代码字段映射、客户扩展字段和账套升级版本。", occurrenceCount: 1, sqlFingerprint: "c".repeat(64), caller: "caller_0001" }], staticDiagnosis: { moduleCode: "hr_4011", moduleKind: "base", healthy: false, issueCount: 1, issues: [{ severity: "error", code: "base.fields_missing", source: "base_field_config" }] }, contextAvailable: true } } }); } function fakeImage(width, height, decodeFails) { const image = { naturalWidth: width, naturalHeight: height, async decode() { if (decodeFails) throw new Error("decode failed"); } }; Object.defineProperty(image, "src", { set() { queueMicrotask(() => image.onload()); } }); return image; } function bindDynamicCreateProjections(plan) { plan.data.preview = structuredClone(plan.preview); plan.data.parameterPreview = structuredClone(plan.preview); return plan; } function bindDynamicUpdateProjection(plan) { plan.data.preview = structuredClone(plan.preview); return plan; }