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(); }); });