Files
Genarrative/apps/ai-game-creator-shell/tests/gameTestSliceModel.test.ts
menghao 752505547d
Project CI / Frontend tests (pull_request) Failing after 23s
Project CI / Repository checks (pull_request) Failing after 1m4s
Project CI / Backend tests (pull_request) Successful in 4m59s
Project CI / Native shell tests (pull_request) Successful in 13m17s
实现运行工作台正式测试切片与悬停信息
新增正式测试切片合同、状态机与上一项/播放暂停/下一项控制
新增可信悬停资源解析与资源管理画布定位
扩展 Tauri 版本登记、运行 Bridge 和 Agent 生成合同
补齐 TypeScript、Rust 与 AppSurface 回归测试
同步工作台 PRD、技术方案与共享决策记录
保持 P2 资源替换、参数写入和下一版本能力未接线
2026-08-04 17:06:24 +08:00

227 lines
6.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type {
GameRuntimeMessage,
RunnableGameVersion,
} from '../../../../packages/shared/src/contracts/gameCreationApp';
import {
beginGameTestSlice,
formalGameTestSlices,
projectGameTestSliceMessage,
projectGameTestSliceSessionStatus,
trustedInspectionFromRuntimeMessage,
} from '../src/features/project-workspace/gameTestSliceModel';
const version: RunnableGameVersion = {
schemaVersion: 'game-creator-runnable-version.v1',
versionId: 'runnable-r7',
projectId: 'project-1',
parentVersionId: null,
projectRevision: 7,
artifactPath: '.agent/runnable-versions/runnable-r7/artifact',
artifactSha256: 'a'.repeat(64),
entryPath: 'game/index.html',
resourceBindings: [{ slotId: 'hero', resourceId: 'hero-art' }],
testSlices: [
{
sliceId: 'boss',
versionId: 'runnable-r7',
title: '首领战',
order: 2,
startCondition: '进入首领关',
endCondition: '首领战结束',
status: 'idle',
},
{
sliceId: 'core',
versionId: 'runnable-r7',
title: '核心玩法',
order: 1,
startCondition: '游戏 ready',
endCondition: '核心循环结束',
status: 'idle',
},
],
resourceDescriptors: [
{
resourceId: 'hero-art',
name: 'hero.png',
category: 'art',
subtype: 'character',
width: 128,
height: 256,
format: 'image/png',
},
],
createdReason: 'initial',
validation: {
staticSmokePassed: true,
previewValidatePassed: true,
playtestPassed: true,
agentId: 'program-agent',
runId: 'run-7',
reportPath:
'.agent/runtime/browser-validations/program-agent/run-7/1/validation.json',
},
createdAt: 700,
};
function runtimeMessage(
type: GameRuntimeMessage['type'],
payload: GameRuntimeMessage['payload'],
): GameRuntimeMessage {
return {
schemaVersion: 'game-creator-runtime-message.v1',
protocolVersion: 'game-creator-run-bridge.v1',
requestId: `request-${type}`,
sessionId: 'session-1',
previewId: 'preview-1',
projectId: version.projectId,
sequence: 1,
type,
versionId: version.versionId,
projectRevision: version.projectRevision,
payload,
} as GameRuntimeMessage;
}
describe('P4/P5 game runtime models', () => {
it('uses only valid finite version-owned slices and enforces state transitions', () => {
const slices = formalGameTestSlices(version);
expect(slices.map((slice) => slice.sliceId)).toEqual(['core', 'boss']);
const starting = beginGameTestSlice(slices[0]);
const playing = projectGameTestSliceMessage(
starting,
runtimeMessage('runtime.slice', {
sliceId: 'core',
title: '不可信标题',
status: 'playing',
}),
);
expect(playing?.status).toBe('playing');
const completed = projectGameTestSliceMessage(
playing,
runtimeMessage('runtime.slice', {
sliceId: 'core',
title: '任意标题',
status: 'completed',
}),
);
expect(completed?.status).toBe('completed');
expect(
projectGameTestSliceMessage(
completed,
runtimeMessage('runtime.slice', {
sliceId: 'core',
title: '任意标题',
status: 'playing',
}),
)?.status,
).toBe('completed');
});
it('rejects invalid version slice identities instead of inventing a fallback', () => {
expect(
formalGameTestSlices({
...version,
testSlices: [{ ...version.testSlices![0], versionId: 'other' }],
}),
).toEqual([]);
expect(formalGameTestSlices({ ...version, testSlices: undefined })).toEqual(
[],
);
expect(
formalGameTestSlices({
...version,
testSlices: version.testSlices!.map((slice) => ({
...slice,
order: 1,
})),
}),
).toEqual([]);
expect(
formalGameTestSlices({
...version,
testSlices: [{ ...version.testSlices![0], status: 'playing' }],
}),
).toEqual([]);
});
it('projects only legal pause and resume transitions from the run session', () => {
const playing = projectGameTestSliceMessage(
beginGameTestSlice(formalGameTestSlices(version)[0]),
runtimeMessage('runtime.slice', {
sliceId: 'core',
title: '',
status: 'playing',
}),
);
const paused = projectGameTestSliceSessionStatus(
playing,
runtimeMessage('runtime.state', { status: 'paused' }),
);
expect(paused?.status).toBe('paused');
expect(
projectGameTestSliceSessionStatus(
paused,
runtimeMessage('runtime.state', { status: 'playing' }),
)?.status,
).toBe('playing');
});
it('resolves hover IDs only through the current version binding and descriptor', () => {
const resolved = trustedInspectionFromRuntimeMessage(
version,
runtimeMessage('runtime.inspection', {
inspectionId: 'hover-resource',
label: '伪造名称',
metrics: { name: '伪造资源' },
targetKind: 'resource',
targetId: 'hero-art',
}),
);
expect(resolved).toMatchObject({
name: 'hero.png',
resourceId: 'hero-art',
slotId: 'hero',
width: 128,
height: 256,
});
expect(
trustedInspectionFromRuntimeMessage(
version,
runtimeMessage('runtime.inspection', {
inspectionId: 'hover-entity',
label: '',
metrics: {},
targetKind: 'entity',
targetId: 'hero',
}),
),
).toMatchObject({ resourceId: 'hero-art', slotId: 'hero' });
expect(
trustedInspectionFromRuntimeMessage(
version,
runtimeMessage('runtime.inspection', {
inspectionId: 'hover-unbound',
label: '伪造名称',
metrics: {},
targetKind: 'resource',
targetId: 'unbound',
}),
),
).toBeNull();
expect(
trustedInspectionFromRuntimeMessage(
version,
runtimeMessage('runtime.inspection', {
inspectionId: 'hover-clear',
label: '',
metrics: {},
targetKind: 'none',
}),
),
).toBeNull();
});
});