1
This commit is contained in:
@@ -23,6 +23,8 @@ vi.mock('../services/rpg-entry', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../services/rpg-runtime', () => ({
|
||||
getRpgRuntimeSessionId: (gameState: Pick<GameState, 'runtimeSessionId'>) =>
|
||||
gameState.runtimeSessionId?.trim() || 'runtime-main',
|
||||
rpgSnapshotClient: {
|
||||
getSnapshot: storageMocks.getSaveSnapshot,
|
||||
putSnapshot: storageMocks.putSaveSnapshot,
|
||||
@@ -30,7 +32,11 @@ vi.mock('../services/rpg-runtime', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function SettingsHarness({ authenticatedUserId }: { authenticatedUserId: string | null }) {
|
||||
function SettingsHarness({
|
||||
authenticatedUserId,
|
||||
}: {
|
||||
authenticatedUserId: string | null;
|
||||
}) {
|
||||
const settings = useGameSettings(authenticatedUserId);
|
||||
|
||||
return (
|
||||
@@ -50,14 +56,18 @@ function SettingsHarness({ authenticatedUserId }: { authenticatedUserId: string
|
||||
|
||||
function PersistenceHarness({
|
||||
authenticatedUserId,
|
||||
gameState = {} as GameState,
|
||||
currentStory = null as StoryMoment | null,
|
||||
}: {
|
||||
authenticatedUserId: string | null;
|
||||
gameState?: GameState;
|
||||
currentStory?: StoryMoment | null;
|
||||
}) {
|
||||
const persistence = useRpgSessionPersistence({
|
||||
authenticatedUserId,
|
||||
gameState: {} as GameState,
|
||||
gameState,
|
||||
bottomTab: 'adventure' as BottomTab,
|
||||
currentStory: null as StoryMoment | null,
|
||||
currentStory,
|
||||
isLoading: false,
|
||||
setGameState: () => {},
|
||||
setBottomTab: () => {},
|
||||
@@ -67,7 +77,9 @@ function PersistenceHarness({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="saved-game">{persistence.hasSavedGame ? 'yes' : 'no'}</div>
|
||||
<div data-testid="saved-game">
|
||||
{persistence.hasSavedGame ? 'yes' : 'no'}
|
||||
</div>
|
||||
<div data-testid="hydrating">
|
||||
{persistence.isHydratingSnapshot ? 'yes' : 'no'}
|
||||
</div>
|
||||
@@ -161,3 +173,64 @@ test('unauthenticated runtime skips remote snapshot hydration', async () => {
|
||||
expect(screen.getByTestId('saved-game').textContent).toBe('no');
|
||||
expect(storageMocks.getSaveSnapshot).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('runtime autosave requests backend checkpoint without uploading local state', async () => {
|
||||
vi.useFakeTimers();
|
||||
storageMocks.getSaveSnapshot.mockResolvedValue(null);
|
||||
storageMocks.putSaveSnapshot.mockResolvedValue({
|
||||
version: 2,
|
||||
savedAt: '2026-04-28T10:00:00.000Z',
|
||||
bottomTab: 'adventure',
|
||||
currentStory: null,
|
||||
gameState: {
|
||||
runtimeSessionId: 'runtime-main',
|
||||
currentScene: 'Story',
|
||||
},
|
||||
});
|
||||
const gameState = {
|
||||
runtimeSessionId: 'runtime-main',
|
||||
runtimePersistenceDisabled: false,
|
||||
runtimeMode: 'play',
|
||||
currentScene: 'Story',
|
||||
worldType: 'CUSTOM',
|
||||
playerCharacter: { id: 'hero_001' },
|
||||
runtimeStats: {
|
||||
playTimeMs: 0,
|
||||
lastPlayTickAt: null,
|
||||
hostileNpcsDefeated: 0,
|
||||
questsAccepted: 0,
|
||||
itemsUsed: 0,
|
||||
scenesTraveled: 0,
|
||||
},
|
||||
} as unknown as GameState;
|
||||
const story = { text: '开场', options: [], streaming: false } as StoryMoment;
|
||||
|
||||
render(
|
||||
<PersistenceHarness
|
||||
authenticatedUserId="user-1"
|
||||
gameState={gameState}
|
||||
currentStory={story}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
expect(storageMocks.getSaveSnapshot).toHaveBeenCalledTimes(1);
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(400);
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(storageMocks.putSaveSnapshot).toHaveBeenCalledWith(
|
||||
{
|
||||
sessionId: 'runtime-main',
|
||||
bottomTab: 'adventure',
|
||||
},
|
||||
expect.objectContaining({
|
||||
signal: expect.any(AbortSignal),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user