feat: integrate automatic game resource delivery
This commit is contained in:
@@ -12,6 +12,100 @@ vi.mock('@/lib/coding-attachments', () => ({
|
||||
describe('CodingConversationTimeline', () => {
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
it('shows one Game Resource progress card from generation through automatic project save', async () => {
|
||||
const { codingConversationStore } = await import('@/stores/coding-conversations');
|
||||
const { CodingConversationTimeline } = await import(
|
||||
'@/pages/Chat/CodingConversationTimeline'
|
||||
);
|
||||
const base = createProductSnapshot('conversation-game-resource-progress', 1);
|
||||
const details = (phase: 'saving' | 'saved') => ({
|
||||
schema: 'makelore-capability.v1' as const,
|
||||
plugin_id: 'makelore.game-resource',
|
||||
plugin_version: '1.0.0',
|
||||
capability_id: 'game-resource.generate',
|
||||
operation: 'generate',
|
||||
request_id: 'pi:game-run:game-tool',
|
||||
success: true,
|
||||
status: phase === 'saved' ? 200 : 202,
|
||||
code: null,
|
||||
error: null,
|
||||
retryable: false,
|
||||
billing: phase === 'saved' ? {
|
||||
mode: 'platform_metered' as const,
|
||||
status: 'settled' as const,
|
||||
reserved_points: '2.00',
|
||||
actual_points: '2.00',
|
||||
usage_amount: 1,
|
||||
unit: 'generation',
|
||||
} : {
|
||||
mode: 'platform_metered' as const,
|
||||
status: 'dispatched' as const,
|
||||
reserved_points: '2.00',
|
||||
usage_amount: 1,
|
||||
unit: 'generation',
|
||||
},
|
||||
payload_schema: 'game-resource.v1',
|
||||
data: {
|
||||
phase,
|
||||
executionId: '11111111-1111-4111-8111-111111111111',
|
||||
providerStatus: 'succeeded',
|
||||
deliveryStatus: phase,
|
||||
outputCount: 2,
|
||||
files: phase === 'saved' ? [
|
||||
{ path: 'assets/generated/game-resource/execution/output-1.png', bytes: 128 },
|
||||
{ path: 'assets/generated/game-resource/execution/output-2.png', bytes: 256 },
|
||||
] : [],
|
||||
},
|
||||
});
|
||||
const snapshot = (seq: number, phase: 'saving' | 'saved') => {
|
||||
const toolDetails = details(phase);
|
||||
return {
|
||||
...base,
|
||||
cursor: { ...base.cursor, seq },
|
||||
nodes: [{
|
||||
kind: 'tool' as const,
|
||||
id: 'tool-game-resource-progress',
|
||||
toolCallId: 'game-tool',
|
||||
toolName: 'game_resource_generate',
|
||||
title: '生成游戏资源',
|
||||
inputText: '',
|
||||
status: phase === 'saved' ? 'complete' as const : 'running' as const,
|
||||
output: [{
|
||||
kind: 'text' as const,
|
||||
id: `tool-output-game-resource-${phase}`,
|
||||
text: JSON.stringify(toolDetails),
|
||||
status: phase === 'saved' ? 'complete' as const : 'streaming' as const,
|
||||
}],
|
||||
details: toolDetails,
|
||||
}],
|
||||
};
|
||||
};
|
||||
codingConversationStore.getState().applySnapshotEvent({
|
||||
type: 'snapshot',
|
||||
conversationId: 'conversation-game-resource-progress',
|
||||
workerGeneration: 1,
|
||||
seq: base.cursor.seq,
|
||||
snapshot: snapshot(base.cursor.seq, 'saving'),
|
||||
});
|
||||
|
||||
render(<CodingConversationTimeline conversationId="conversation-game-resource-progress" />);
|
||||
|
||||
expect(screen.getByTestId('tool-progress-preview')).toHaveTextContent(
|
||||
'游戏资源 · 正在保存到项目',
|
||||
);
|
||||
|
||||
const nextSeq = base.cursor.seq + 1;
|
||||
act(() => codingConversationStore.getState().applySnapshotEvent({
|
||||
type: 'snapshot',
|
||||
conversationId: 'conversation-game-resource-progress',
|
||||
workerGeneration: 1,
|
||||
seq: nextSeq,
|
||||
snapshot: snapshot(nextSeq, 'saved'),
|
||||
}));
|
||||
await waitFor(() => expect(screen.getByTestId('tool-progress-preview'))
|
||||
.toHaveTextContent('游戏资源 · 已保存 2 个文件'));
|
||||
});
|
||||
|
||||
it('resolves attachment refs into temporary object URLs without base64 state', async () => {
|
||||
const createObjectURL = vi.fn(() => 'blob:authenticated-preview');
|
||||
const revokeObjectURL = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user