f3a8a38acd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
105 lines
3.6 KiB
TypeScript
105 lines
3.6 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { resolve } from 'node:path';
|
|
import { writeFile } from 'node:fs/promises';
|
|
import type {} from '../physics/agent';
|
|
|
|
for (const fault of [
|
|
'empty-grasp',
|
|
'empty-once',
|
|
'alignment',
|
|
'slip',
|
|
'jam',
|
|
'navigation',
|
|
'unreachable',
|
|
'overspeed',
|
|
] as const) {
|
|
test(`真实 WASM 故障:${fault} 不可冒充成功/无限恢复`, async ({ page }, info) => {
|
|
await page.goto('/physics/agent.html');
|
|
await page.waitForFunction(() => Boolean(window.lekiwiAgent));
|
|
await page.evaluate(
|
|
(url) => window.lekiwiAgent.boot(url),
|
|
`/@fs${resolve('build/lekiwi/lekiwi-v1.zip')}`,
|
|
);
|
|
try {
|
|
const r = await page.evaluate(
|
|
(fault) => window.lekiwiAgent.task(0, undefined, { fault }),
|
|
fault,
|
|
);
|
|
await writeFile(info.outputPath('fault-evidence.json'), JSON.stringify(r, null, 2));
|
|
console.log('WASM fault', {
|
|
fault,
|
|
state: r.state,
|
|
error: r.error,
|
|
failures: r.failures,
|
|
recoveries: r.recoveries,
|
|
simTime: r.simTime,
|
|
});
|
|
expect(r.mode).toBe('mock');
|
|
expect(r.state).toBe(fault === 'empty-once' ? 'succeeded' : 'failed');
|
|
expect(r.succeeded).toBe(fault === 'empty-once');
|
|
expect(r.simTime).toBeLessThanOrEqual(70.01);
|
|
expect(r.llmCalls).toBe(1);
|
|
if (fault === 'empty-grasp' || fault === 'empty-once' || fault === 'alignment') {
|
|
const skill = fault === 'alignment' ? 'pregrasp' : 'close';
|
|
const code = fault === 'alignment' ? 'alignment_timeout' : 'empty_grasp';
|
|
const recovered = fault === 'empty-once';
|
|
expect(r.recoveries[skill]).toBe(recovered ? 1 : 2);
|
|
expect(r.failures.map((f) => f.code)).toEqual(Array(recovered ? 1 : 3).fill(code));
|
|
if (!recovered) expect(r.trace.every((s) => !s.secure)).toBe(true);
|
|
else {
|
|
expect(r.transported).toBeGreaterThanOrEqual(0.5);
|
|
expect(r.sample.onGoalSupport).toBe(true);
|
|
}
|
|
} else {
|
|
expect(r.recoveries).toEqual({});
|
|
expect(r.failures).toHaveLength(1);
|
|
expect(r.failures[0].code).toBe(
|
|
{
|
|
slip: 'lost_grasp',
|
|
jam: 'joint_stall',
|
|
navigation: 'navigation_timeout',
|
|
unreachable: 'unreachable',
|
|
overspeed: 'joint_velocity',
|
|
}[fault],
|
|
);
|
|
}
|
|
if (fault === 'slip') {
|
|
expect(Math.max(...r.trace.map((s) => s.transported))).toBeGreaterThan(0.12);
|
|
expect(r.transported).toBe(0);
|
|
}
|
|
} finally {
|
|
await page.evaluate(() => window.lekiwiAgent.dispose());
|
|
}
|
|
});
|
|
}
|
|
|
|
for (const goal of [
|
|
[0.237, 0.565, 0.128],
|
|
[0.277, 0.665, 0.128],
|
|
]) {
|
|
test(`真实 WASM 受限目标角点 ${goal.slice(0, 2).join(',')}(非任意目标泛化)`, async ({
|
|
page,
|
|
}, info) => {
|
|
await page.goto('/physics/agent.html');
|
|
await page.waitForFunction(() => Boolean(window.lekiwiAgent));
|
|
await page.evaluate(
|
|
(url) => window.lekiwiAgent.boot(url),
|
|
`/@fs${resolve('build/lekiwi/lekiwi-v1.zip')}`,
|
|
);
|
|
try {
|
|
const r = await page.evaluate(
|
|
(goal) => window.lekiwiAgent.task(0, undefined, { goal }),
|
|
goal,
|
|
);
|
|
await writeFile(info.outputPath('goal-evidence.json'), JSON.stringify(r, null, 2));
|
|
expect(r.state, r.error).toBe('succeeded');
|
|
expect(r.goal).toEqual(goal);
|
|
expect(r.succeeded).toBe(true);
|
|
expect(r.transported).toBeGreaterThanOrEqual(0.5);
|
|
expect(Math.hypot(...r.sample.object.map((v, i) => v - goal[i]))).toBeLessThan(0.04);
|
|
} finally {
|
|
await page.evaluate(() => window.lekiwiAgent.dispose());
|
|
}
|
|
});
|
|
}
|