1
This commit is contained in:
@@ -15,12 +15,14 @@ vi.mock('../apiClient', async () => {
|
||||
|
||||
import { AnimationState } from '../../types';
|
||||
import {
|
||||
beginRpgRuntimeStorySession,
|
||||
buildStoryMomentFromRuntimeOptions,
|
||||
getRpgRuntimeClientVersion,
|
||||
getRpgRuntimeSessionId,
|
||||
getRpgRuntimeStoryState,
|
||||
isRpgRuntimeServerFunctionId,
|
||||
isRpgRuntimeTaskFunctionId,
|
||||
loadRpgRuntimeInventoryView,
|
||||
resolveRpgRuntimeStoryAction,
|
||||
resolveRpgRuntimeStoryMoment,
|
||||
shouldUseRpgRuntimeServerOptions,
|
||||
@@ -31,6 +33,52 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
requestJsonMock.mockReset();
|
||||
});
|
||||
|
||||
it('starts runtime sessions through the backend bootstrap endpoint', async () => {
|
||||
requestJsonMock.mockResolvedValue({
|
||||
sessionId: 'runtime-server-1',
|
||||
serverVersion: 1,
|
||||
snapshot: {
|
||||
version: 2,
|
||||
savedAt: '2026-04-28T00:00:00.000Z',
|
||||
bottomTab: 'adventure',
|
||||
gameState: {
|
||||
runtimeSessionId: 'runtime-server-1',
|
||||
currentScene: 'Story',
|
||||
playerCharacter: { id: 'role-1', name: '沈砺' },
|
||||
playerEquipment: { weapon: null, armor: null, relic: null },
|
||||
},
|
||||
currentStory: null,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await beginRpgRuntimeStorySession({
|
||||
worldType: 'CUSTOM',
|
||||
customWorldProfile: { id: 'profile-1' } as never,
|
||||
character: { id: 'role-1', name: '沈砺' } as never,
|
||||
runtimeMode: 'play',
|
||||
disablePersistence: false,
|
||||
});
|
||||
|
||||
expect(result.snapshot.gameState.runtimeSessionId).toBe(
|
||||
'runtime-server-1',
|
||||
);
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/runtime/story/sessions',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
worldType: 'CUSTOM',
|
||||
customWorldProfile: { id: 'profile-1' },
|
||||
character: { id: 'role-1', name: '沈砺' },
|
||||
runtimeMode: 'play',
|
||||
disablePersistence: false,
|
||||
}),
|
||||
}),
|
||||
'初始化运行时开局失败',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('builds runtime action requests against the dedicated story endpoint', async () => {
|
||||
requestJsonMock.mockResolvedValue({
|
||||
sessionId: 'runtime-main',
|
||||
@@ -76,7 +124,6 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
optionText: '继续交谈',
|
||||
},
|
||||
},
|
||||
snapshot: undefined,
|
||||
}),
|
||||
}),
|
||||
'执行运行时动作失败',
|
||||
@@ -131,7 +178,6 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
itemId: 'focus-tonic',
|
||||
},
|
||||
},
|
||||
snapshot: undefined,
|
||||
}),
|
||||
}),
|
||||
'执行运行时动作失败',
|
||||
@@ -139,7 +185,7 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('submits runtime state resolution with snapshot context to the server', async () => {
|
||||
it('reads runtime story state by server session id', async () => {
|
||||
requestJsonMock.mockResolvedValue({
|
||||
sessionId: 'runtime-main',
|
||||
serverVersion: 4,
|
||||
@@ -179,34 +225,103 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
await getRpgRuntimeStoryState({
|
||||
sessionId: 'runtime-main',
|
||||
clientVersion: 7,
|
||||
snapshot: {
|
||||
gameState: { currentScene: 'Story' } as never,
|
||||
bottomTab: 'adventure',
|
||||
currentStory: {
|
||||
text: '本地故事',
|
||||
options: [],
|
||||
} as never,
|
||||
},
|
||||
});
|
||||
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/runtime/story/state/resolve',
|
||||
'/api/runtime/story/state/runtime-main',
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
sessionId: 'runtime-main',
|
||||
clientVersion: 7,
|
||||
snapshot: {
|
||||
gameState: {
|
||||
currentScene: 'Story',
|
||||
method: 'GET',
|
||||
}),
|
||||
'读取运行时故事状态失败',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('loads backend inventory view from runtime story state', async () => {
|
||||
requestJsonMock.mockResolvedValue({
|
||||
sessionId: 'runtime-inventory',
|
||||
serverVersion: 5,
|
||||
viewModel: {
|
||||
player: {
|
||||
hp: 100,
|
||||
maxHp: 100,
|
||||
mana: 20,
|
||||
maxMana: 20,
|
||||
},
|
||||
encounter: null,
|
||||
companions: [],
|
||||
inventory: {
|
||||
playerCurrency: 90,
|
||||
currencyText: '90 铜钱',
|
||||
inBattle: false,
|
||||
backpackItems: [],
|
||||
equipmentSlots: [],
|
||||
forgeRecipes: [
|
||||
{
|
||||
id: 'synthesis-refined-ingot',
|
||||
name: '压炼锭材',
|
||||
kind: 'synthesis',
|
||||
description: '把零散残片和基础材料压成稳定可用的金属锭材。',
|
||||
resultLabel: '精炼锭材',
|
||||
currencyCost: 18,
|
||||
currencyText: '18 铜钱',
|
||||
requirements: [
|
||||
{
|
||||
id: 'material:any',
|
||||
label: '任意材料',
|
||||
quantity: 3,
|
||||
owned: 3,
|
||||
},
|
||||
],
|
||||
canCraft: true,
|
||||
action: {
|
||||
functionId: 'forge_craft',
|
||||
actionText: '制作精炼锭材',
|
||||
payload: { recipeId: 'synthesis-refined-ingot' },
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
bottomTab: 'adventure',
|
||||
currentStory: {
|
||||
text: '本地故事',
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
availableOptions: [],
|
||||
status: {
|
||||
inBattle: false,
|
||||
npcInteractionActive: false,
|
||||
currentNpcBattleMode: null,
|
||||
currentNpcBattleOutcome: null,
|
||||
},
|
||||
},
|
||||
presentation: {
|
||||
actionText: '',
|
||||
resultText: '',
|
||||
storyText: '',
|
||||
options: [],
|
||||
},
|
||||
patches: [],
|
||||
snapshot: {
|
||||
version: 2,
|
||||
savedAt: '2026-04-08T00:00:00.000Z',
|
||||
bottomTab: 'adventure',
|
||||
gameState: {
|
||||
runtimeSessionId: 'runtime-inventory',
|
||||
runtimeActionVersion: 5,
|
||||
},
|
||||
currentStory: null,
|
||||
},
|
||||
});
|
||||
|
||||
const view = await loadRpgRuntimeInventoryView({
|
||||
gameState: {
|
||||
runtimeSessionId: 'runtime-inventory',
|
||||
runtimeActionVersion: 5,
|
||||
} as never,
|
||||
});
|
||||
|
||||
expect(view.forgeRecipes[0]?.action.functionId).toBe('forge_craft');
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/runtime/story/state/runtime-inventory',
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
}),
|
||||
'读取运行时故事状态失败',
|
||||
expect.any(Object),
|
||||
@@ -336,6 +451,14 @@ describe('rpgRuntimeStoryClient', () => {
|
||||
player: { hp: 10, maxHp: 10, mana: 5, maxMana: 5 },
|
||||
encounter: null,
|
||||
companions: [],
|
||||
inventory: {
|
||||
playerCurrency: 0,
|
||||
currencyText: '0 铜钱',
|
||||
inBattle: false,
|
||||
backpackItems: [],
|
||||
equipmentSlots: [],
|
||||
forgeRecipes: [],
|
||||
},
|
||||
availableOptions: [],
|
||||
status: {
|
||||
inBattle: false,
|
||||
|
||||
Reference in New Issue
Block a user