清理退役策划测试夹具
删除策划 V2 与批准 GDD 的旧测试数据和调用模拟 保留现役设计 Agent 与 DirectCodex 的界面测试 移除测试中对退役规划命令的断言
This commit is contained in:
@@ -11388,7 +11388,6 @@ export function App({
|
||||
!directCodexProductRuntime && orchestrationMode === 'professional-dag'
|
||||
}
|
||||
workspaceStatus={workspaceStatus}
|
||||
planningLane={planningLaneActive}
|
||||
designView={useDesignAgentSurface ? designAgentView : null}
|
||||
onDesignApprove={
|
||||
useDesignAgentSurface
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import React from 'react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { PlanGddStateViewV1 } from '../../src/app/types';
|
||||
import * as clientApi from '../../src/services/clientApi';
|
||||
import { resetLlmModelCatalogCacheForTest } from '../../src/services/llmModelCatalog';
|
||||
import { useLauncherHomeDraftStore } from '../../src/view/home/useHomeDraftStore';
|
||||
@@ -483,150 +482,6 @@ function agentRuntimeUserInputRequest({
|
||||
};
|
||||
}
|
||||
|
||||
function createPlanGddStateView(
|
||||
overrides: Partial<PlanGddStateViewV1> = {},
|
||||
): PlanGddStateViewV1 {
|
||||
const gddRef = {
|
||||
gddId: 'gdd-plan-0001',
|
||||
version: 1,
|
||||
fingerprint: 'sha256-serde-json-v2:1111111111111111',
|
||||
};
|
||||
return {
|
||||
schemaVersion: 'plan-gdd-state-view.v1',
|
||||
projectId: 'local-project-draft',
|
||||
gddId: gddRef.gddId,
|
||||
state: 'ready_for_approval',
|
||||
session: {
|
||||
sessionId: 'plan-session-0001',
|
||||
sessionRevision: 3,
|
||||
sessionFingerprint: 'sha256-serde-json-v2:2222222222222222',
|
||||
phase: 'awaiting_gdd_approval',
|
||||
clarificationRound: 2,
|
||||
repairDepth: 0,
|
||||
accumulatedAgentMillis: 42_000,
|
||||
activeRunId: null,
|
||||
awaitingAnswerFor: null,
|
||||
decisionStateCounts: {
|
||||
confirmed: 2,
|
||||
defaultPending: 1,
|
||||
prototypePending: 0,
|
||||
},
|
||||
},
|
||||
versions: [
|
||||
{
|
||||
gddRef,
|
||||
status: 'ready_for_approval',
|
||||
approvalRequestId: 'gdd-approval-0001',
|
||||
createdAtUtc: '2026-08-18T00:00:00Z',
|
||||
decision: null,
|
||||
},
|
||||
],
|
||||
displayGdd: {
|
||||
schemaVersion: 'plan-gdd.v1',
|
||||
projectId: 'local-project-draft',
|
||||
gddId: gddRef.gddId,
|
||||
version: gddRef.version,
|
||||
submissionId: 'action-0123456789abcdef01234567',
|
||||
approvalRequestId: 'gdd-approval-0001',
|
||||
actionFingerprint: 'a'.repeat(64),
|
||||
agentId: 'project-planning',
|
||||
source: 'agent-delegate',
|
||||
runProfile: 'standard',
|
||||
runProfileBindingFingerprint: 'b'.repeat(64),
|
||||
rootAgentId: 'project-supervisor',
|
||||
rootRunId: 'run-plan-root-0001',
|
||||
delegationId: 'delegation-0001',
|
||||
sessionId: 'plan-session-0001',
|
||||
sourceSessionRevision: 2,
|
||||
sourceSessionFingerprint: 'sha256-serde-json-v2:3333333333333333',
|
||||
createdByRunId: 'run-plan-child-0001',
|
||||
createdAtUtc: '2026-08-18T00:00:00Z',
|
||||
fingerprint: gddRef.fingerprint,
|
||||
game: {
|
||||
title: '灯塔守夜人',
|
||||
oneLiner: '在潮汐涨落之间调度光束,护送迷航的船只回港。',
|
||||
genre: { primary: '策略', fusion: null },
|
||||
artStyle: {
|
||||
visualType: '像素',
|
||||
keywords: ['夜色', '海雾'],
|
||||
moodAndColor: '冷蓝为主,暖黄光束作为唯一高光。',
|
||||
mvpArtBoundary: '只做灯塔与三类船只的静帧。',
|
||||
},
|
||||
pillars: [
|
||||
{
|
||||
name: '光束调度',
|
||||
playerFeel: '在有限视野里做取舍。',
|
||||
mechanism: '每回合只能照亮一个扇区。',
|
||||
decisionState: 'confirmed',
|
||||
basis: null,
|
||||
},
|
||||
],
|
||||
coreLoop: ['观察潮汐', '分配光束', '结算返港'],
|
||||
targetUsers: {
|
||||
coreUsers: '喜欢短局策略的玩家',
|
||||
preferences: '偏好可预测的规则',
|
||||
sessionLength: '单局 5 分钟',
|
||||
referenceGames: ['灯塔物语'],
|
||||
},
|
||||
platformFacts: {
|
||||
runtime: 'web',
|
||||
viewports: ['desktop', 'mobile'],
|
||||
inputs: ['pointer'],
|
||||
preview: '本地预览',
|
||||
},
|
||||
mvpSystems: [
|
||||
{
|
||||
system: '潮汐时钟',
|
||||
minimalFunction: '固定三段潮汐循环。',
|
||||
whyRequired: '没有它就没有节奏压力。',
|
||||
verifyMethod: '观察一局内三段是否各触发一次。',
|
||||
decisionState: 'confirmed',
|
||||
basis: null,
|
||||
},
|
||||
],
|
||||
outOfScope: ['多人对战'],
|
||||
creatorTips: {
|
||||
doFirst: '先做潮汐时钟。',
|
||||
deferForNow: '暂缓天气系统。',
|
||||
howToVerify: '单局跑满三段潮汐。',
|
||||
expandWhen: '核心循环稳定后再加船种。',
|
||||
},
|
||||
},
|
||||
decisions: [
|
||||
{
|
||||
id: 'decision-0001',
|
||||
topic: '光束是否可分裂',
|
||||
state: 'confirmed',
|
||||
answerSource: 'user_option',
|
||||
round: 1,
|
||||
answerSummary: '不可分裂,保持取舍压力。',
|
||||
basis: null,
|
||||
},
|
||||
],
|
||||
prototypeValidationItems: [
|
||||
{
|
||||
id: 'proto-0001',
|
||||
question: '单扇区照明是否足够做出取舍?',
|
||||
microPrototype: '纸面推演三回合。',
|
||||
observation: '玩家是否出现犹豫。',
|
||||
passCriterion: '三回合内至少一次改变计划。',
|
||||
},
|
||||
],
|
||||
},
|
||||
pendingApproval: {
|
||||
gddRef,
|
||||
pendingActionId: 'action-0123456789abcdef01234567',
|
||||
actionFingerprint: 'a'.repeat(64),
|
||||
approvalRequestId: 'gdd-approval-0001',
|
||||
sessionId: 'plan-session-0001',
|
||||
runId: 'run-plan-root-0001',
|
||||
},
|
||||
approvedGddRef: null,
|
||||
recoveryPending: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createProjectSupervisorRuntimeHarness({
|
||||
projectPath = '/tmp/authorized-game',
|
||||
sessionId = 'supervisor-session-active',
|
||||
@@ -638,8 +493,6 @@ function createProjectSupervisorRuntimeHarness({
|
||||
initialProjectRevision = 0,
|
||||
runtimeMapLoader,
|
||||
expectedRunProfile = 'autonomous-game-build',
|
||||
planningV2Result = null,
|
||||
planningV2StartResult = null,
|
||||
designAgentView = null,
|
||||
designAgentContinueView = null,
|
||||
designWorkspaceFiles = [],
|
||||
@@ -654,8 +507,6 @@ function createProjectSupervisorRuntimeHarness({
|
||||
initialProjectRevision?: number;
|
||||
runtimeMapLoader?: () => Promise<Array<Record<string, unknown>>>;
|
||||
expectedRunProfile?: 'standard' | 'autonomous-game-build';
|
||||
planningV2Result?: Record<string, unknown> | null;
|
||||
planningV2StartResult?: Record<string, unknown> | null;
|
||||
designAgentView?: Record<string, unknown> | null;
|
||||
designAgentContinueView?: Record<string, unknown> | null;
|
||||
designWorkspaceFiles?: Array<Record<string, unknown>>;
|
||||
@@ -703,14 +554,9 @@ function createProjectSupervisorRuntimeHarness({
|
||||
let rejectRuntime: Record<string, unknown> | null = null;
|
||||
let answerRuntime: Record<string, unknown> | null = null;
|
||||
let answerFailuresRemaining = 0;
|
||||
// 默认不配置策划状态:hydrate 与接入本 harness 之前一样抛出,既有用例行为不变。
|
||||
let currentPlanningV2Result = planningV2Result;
|
||||
let currentPlanningV2StartResult = planningV2StartResult;
|
||||
let currentDesignAgentView = designAgentView;
|
||||
const currentDesignContinueView = designAgentContinueView;
|
||||
const currentDesignWorkspaceFiles = [...designWorkspaceFiles];
|
||||
let planningV2DecisionError: string | null = null;
|
||||
const planningV2DecisionCalls: Array<Record<string, unknown>> = [];
|
||||
let runtimeUpdateHandler:
|
||||
| ((event: {
|
||||
payload: {
|
||||
@@ -1060,35 +906,6 @@ function createProjectSupervisorRuntimeHarness({
|
||||
if (command === 'read_design_workspace_file') {
|
||||
return '';
|
||||
}
|
||||
if (command === 'hydrate_planning_session_v2') {
|
||||
return currentPlanningV2Result;
|
||||
}
|
||||
if (
|
||||
command === 'start_planning_session_v2' ||
|
||||
command === 'continue_planning_session_v2'
|
||||
) {
|
||||
const nextResult =
|
||||
command === 'start_planning_session_v2'
|
||||
? (currentPlanningV2StartResult ?? currentPlanningV2Result)
|
||||
: currentPlanningV2Result;
|
||||
if (!nextResult) {
|
||||
throw new Error('PLANNING_V2_RESULT_NOT_CONFIGURED');
|
||||
}
|
||||
currentPlanningV2Result = nextResult;
|
||||
return nextResult;
|
||||
}
|
||||
if (command === 'decide_planning_artifact_v2') {
|
||||
planningV2DecisionCalls.push({ ...(args ?? {}) });
|
||||
if (planningV2DecisionError) {
|
||||
const failure = planningV2DecisionError;
|
||||
planningV2DecisionError = null;
|
||||
throw new Error(failure);
|
||||
}
|
||||
if (!currentPlanningV2Result) {
|
||||
throw new Error('PLANNING_V2_RESULT_NOT_CONFIGURED');
|
||||
}
|
||||
return currentPlanningV2Result;
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
},
|
||||
);
|
||||
@@ -1156,19 +973,9 @@ function createProjectSupervisorRuntimeHarness({
|
||||
failNextAnswers(count = 1) {
|
||||
answerFailuresRemaining = count;
|
||||
},
|
||||
setPlanningV2Result(state: Record<string, unknown> | null) {
|
||||
currentPlanningV2Result = state;
|
||||
},
|
||||
emitDesignAgentEvent(payload: Record<string, unknown>) {
|
||||
designAgentUpdateHandler?.({ payload });
|
||||
},
|
||||
setPlanningV2StartResult(state: Record<string, unknown> | null) {
|
||||
currentPlanningV2StartResult = state;
|
||||
},
|
||||
failNextPlanningV2Decision(message: string) {
|
||||
planningV2DecisionError = message;
|
||||
},
|
||||
planningV2DecisionCalls,
|
||||
appendSupervisorMessage(message: Record<string, unknown>) {
|
||||
currentSupervisorMessages.push(message);
|
||||
},
|
||||
@@ -1377,7 +1184,6 @@ export {
|
||||
composerValue,
|
||||
createGameCreationAppManifest,
|
||||
createGameCreationAppSeedTasks,
|
||||
createPlanGddStateView,
|
||||
createProjectSupervisorRuntimeHarness,
|
||||
deriveAgentStatusCards,
|
||||
describe,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { ProjectSupervisorComponentProps } from '../../src/features/app-shell/model';
|
||||
import { useHomeProjectCreation } from '../../src/features/app-shell/useHomeProjectCreation';
|
||||
import { WorkspaceLauncherShell } from '../../src/features/app-shell/WorkspaceLauncher';
|
||||
import type { LauncherView } from '../../src/view/layout';
|
||||
import {
|
||||
act,
|
||||
App,
|
||||
@@ -63,38 +61,6 @@ function homeDesignContinueView({
|
||||
};
|
||||
}
|
||||
|
||||
function ApprovedGddStartHarness() {
|
||||
const [, setLauncherView] = React.useState<LauncherView>(
|
||||
'project-development',
|
||||
);
|
||||
const [, setStatus] = React.useState('');
|
||||
const [, setAgentChatProjectPath] = React.useState('');
|
||||
const controller = useHomeProjectCreation({
|
||||
setStatus,
|
||||
setLauncherView,
|
||||
setAgentChatProjectPath,
|
||||
rememberRecentWorkspace: () => undefined,
|
||||
});
|
||||
|
||||
if (controller.currentProjectContext) {
|
||||
return React.createElement(
|
||||
'p',
|
||||
{ 'aria-label': '已进入自动游戏项目' },
|
||||
controller.currentProjectContext.initialPrompt,
|
||||
);
|
||||
}
|
||||
|
||||
return React.createElement(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
onClick: () =>
|
||||
void controller.startGameFromApprovedGdd('/tmp/planning-project'),
|
||||
},
|
||||
'直接用已批准 GDD 开始建造',
|
||||
);
|
||||
}
|
||||
|
||||
async function openResourceBookCategory(label: string) {
|
||||
const categoryByLabel: Record<string, string> = {
|
||||
'UI 交互': 'ui-interaction',
|
||||
@@ -1704,73 +1670,6 @@ export function registerHomeProjectCreationTests() {
|
||||
expect(followUpPayload).not.toHaveProperty('attachments');
|
||||
});
|
||||
|
||||
it('starts an automatic game project from the approved GDD', async () => {
|
||||
const automaticProjectPath = '/tmp/approved-gdd-game';
|
||||
const manifest = createGameCreationAppManifest(
|
||||
'approved-gdd-game',
|
||||
'已批准 GDD 游戏',
|
||||
);
|
||||
const gddContent = '# Fast GDD\n\n批准后的方案内容';
|
||||
const invoke = vi.fn(async (command: string) => {
|
||||
if (command === 'read_local_project_file') {
|
||||
return {
|
||||
path: 'game/fast_gdd.md',
|
||||
absolutePath: '/tmp/planning-project/game/fast_gdd.md',
|
||||
content: gddContent,
|
||||
};
|
||||
}
|
||||
if (command === 'create_automatic_local_game_project') {
|
||||
return {
|
||||
projectPath: automaticProjectPath,
|
||||
manifestPath: `${automaticProjectPath}/.agent/manifest.json`,
|
||||
manifest,
|
||||
};
|
||||
}
|
||||
if (command === 'upload_local_asset') {
|
||||
return {
|
||||
id: 'approved-gdd-asset',
|
||||
localPath: 'assets/uploads/fast_gdd.md',
|
||||
absolutePath: `${automaticProjectPath}/assets/uploads/fast_gdd.md`,
|
||||
manifestPath: `${automaticProjectPath}/.agent/manifest.json`,
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
});
|
||||
window.__TAURI__ = { core: { invoke } };
|
||||
render(React.createElement(ApprovedGddStartHarness));
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '直接用已批准 GDD 开始建造' }),
|
||||
);
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '直接用已批准 GDD 开始建造' }),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
invoke.mock.calls.filter(
|
||||
([command]) => command === 'create_automatic_local_game_project',
|
||||
),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
expect(screen.getByLabelText('已进入自动游戏项目').textContent).toContain(
|
||||
'请按照附件中的已批准 GDD 开始建造这款游戏。',
|
||||
);
|
||||
expect(invoke).toHaveBeenCalledWith('read_local_project_file', {
|
||||
projectPath: '/tmp/planning-project',
|
||||
relativePath: 'game/fast_gdd.md',
|
||||
commandId: 'file.read',
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(invoke).toHaveBeenCalledWith('upload_local_asset', {
|
||||
projectPath: automaticProjectPath,
|
||||
fileName: 'fast_gdd.md',
|
||||
mediaType: 'text/markdown',
|
||||
bytes: Array.from(new TextEncoder().encode(gddContent)),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the home composer out of chat mode while automatic project creation is pending', async () => {
|
||||
let rejectAutomaticProject: ((error: Error) => void) | null = null;
|
||||
const automaticProject = new Promise<never>((_resolve, reject) => {
|
||||
@@ -2054,11 +1953,6 @@ export function registerHomeProjectCreationTests() {
|
||||
expect(JSON.stringify(startCall?.[1] ?? {})).not.toContain(
|
||||
'本轮用户附件',
|
||||
);
|
||||
expect(
|
||||
invoke.mock.calls.some(
|
||||
([command]) => command === 'start_planning_session_v2',
|
||||
),
|
||||
).toBe(false);
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
'chat_with_game_creator_direct_codex',
|
||||
expect.anything(),
|
||||
@@ -2163,11 +2057,6 @@ export function registerHomeProjectCreationTests() {
|
||||
expect(JSON.stringify(startCall?.[1] ?? {})).not.toContain(
|
||||
'assets/uploads/reference.png',
|
||||
);
|
||||
expect(
|
||||
invoke.mock.calls.some(
|
||||
([command]) => command === 'start_planning_session_v2',
|
||||
),
|
||||
).toBe(false);
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
'chat_with_game_creator_direct_codex',
|
||||
expect.anything(),
|
||||
@@ -2245,12 +2134,6 @@ export function registerHomeProjectCreationTests() {
|
||||
}),
|
||||
);
|
||||
});
|
||||
expect(
|
||||
invoke.mock.calls.some(
|
||||
([command]) => command === 'start_planning_session_v2',
|
||||
),
|
||||
).toBe(false);
|
||||
|
||||
await screen.findByText('首版角色规范图采用哪种美术方向?');
|
||||
expect(screen.getByRole('button', { name: '像素' })).not.toBeNull();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user