142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import { normalizeSkillProcessLine } from '../src/modules/netaclaw/service/skill_executor';
|
|
|
|
describe('skill process events', () => {
|
|
it('only parses explicit process_event JSON lines', () => {
|
|
const event = normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
'{"type":"process_event","stage":"inspect","message":"Inspecting","taskId":"task-1","status":"completed","level":"warning","current":2,"total":4,"percent":55,"timestamp":"2026-05-06T00:00:00.000Z","note":"kept"}',
|
|
7,
|
|
);
|
|
|
|
expect(event).toEqual({
|
|
source: 'skill',
|
|
targetType: 'skill',
|
|
taskId: 'task-1',
|
|
stage: 'inspect',
|
|
status: 'completed',
|
|
level: 'warning',
|
|
message: 'Inspecting',
|
|
timestamp: '2026-05-06T00:00:00.000Z',
|
|
sequence: 7,
|
|
current: 2,
|
|
total: 4,
|
|
percent: 55,
|
|
payload: { note: 'kept' },
|
|
});
|
|
});
|
|
|
|
it('supports bracketed stderr prefixes before JSON', () => {
|
|
const event = normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
'[vehicle-damage-inspection] {"type":"process_event","stage":"upload","time":"2026-05-06T00:00:00.000Z"}',
|
|
1,
|
|
);
|
|
|
|
expect(event).toEqual(expect.objectContaining({
|
|
source: 'skill',
|
|
targetType: 'skill',
|
|
stage: 'upload',
|
|
status: 'running',
|
|
level: 'info',
|
|
message: 'upload',
|
|
timestamp: '2026-05-06T00:00:00.000Z',
|
|
sequence: 1,
|
|
}));
|
|
});
|
|
|
|
it('derives percent from batch and totalBatches', () => {
|
|
const event = normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
'{"type":"process_event","stage":"batching","batch":1,"totalBatches":3}',
|
|
2,
|
|
);
|
|
|
|
expect(event).toEqual(expect.objectContaining({
|
|
current: 1,
|
|
total: 3,
|
|
percent: 33,
|
|
}));
|
|
});
|
|
|
|
it('sanitizes payload before emitting process events', () => {
|
|
const event = normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
JSON.stringify({
|
|
type: 'process_event',
|
|
message: 'request completed',
|
|
apiKey: 'secret',
|
|
rawText: 'x'.repeat(600),
|
|
nested: { value: 1 },
|
|
}),
|
|
3,
|
|
);
|
|
|
|
expect(event?.payload).toEqual({
|
|
apiKey: '[filtered]',
|
|
rawText: `${'x'.repeat(500)}...`,
|
|
nested: '[object]',
|
|
});
|
|
});
|
|
|
|
it('returns null for ordinary stderr', () => {
|
|
expect(normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
'Traceback: failed to import module',
|
|
1,
|
|
)).toBeNull();
|
|
});
|
|
|
|
it('returns null for ordinary JSON without process_event type', () => {
|
|
expect(normalizeSkillProcessLine(
|
|
'vehicle-damage-inspection',
|
|
'{"stage":"inspect","message":"ordinary json"}',
|
|
1,
|
|
)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('vehicle damage inspection process event output', () => {
|
|
it('main skill progress logs are explicit process events', async () => {
|
|
const scriptPath = require.resolve('../skills/vehicle-damage-inspection/scripts/index.cjs');
|
|
const originalWrite = process.stderr.write;
|
|
const writes: string[] = [];
|
|
jest.resetModules();
|
|
process.stderr.write = jest.fn((chunk: any) => {
|
|
writes.push(String(chunk));
|
|
return true;
|
|
}) as any;
|
|
|
|
try {
|
|
const skill = require(scriptPath);
|
|
await skill.run(
|
|
{ videoUrl: 'demo.mp4', taskId: 'task-1', mode: 'frames-only' },
|
|
{
|
|
RZYX_AI_WORKSPACE_ROOT: 'C:/data/workspace/vehicle-damage-inspection',
|
|
RZYX_AI_UPLOAD_ROOT: 'C:/data/uploads',
|
|
},
|
|
{
|
|
createWorkspace: () => ({
|
|
taskId: 'task-1',
|
|
workspacePath: 'C:/data/workspace/vehicle-damage-inspection/task-1',
|
|
}),
|
|
resolveVideoPath: () => 'C:/data/uploads/demo.mp4',
|
|
extractFrames: async () => ({
|
|
videoInfo: { duration: 1, resolution: '960x544', extractedFrames: 1 },
|
|
frames: [{ index: 0, timestamp: 0, path: 'frame.jpg' }],
|
|
}),
|
|
writeJson: () => undefined,
|
|
},
|
|
);
|
|
} finally {
|
|
process.stderr.write = originalWrite;
|
|
}
|
|
|
|
const parsed = writes
|
|
.map(line => line.match(/\[vehicle-damage-inspection\]\s*(\{.*\})/)?.[1])
|
|
.filter(Boolean)
|
|
.map(json => JSON.parse(json as string));
|
|
expect(parsed.length).toBeGreaterThan(0);
|
|
expect(parsed.every(item => item.type === 'process_event')).toBe(true);
|
|
});
|
|
});
|