Merge remote-tracking branch 'origin/master' into dev-jenken
# Conflicts: # .hermes/shared-memory/pitfalls.md # server-rs/crates/api-server/src/modules/jump_hop.rs # src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx # src/services/jump-hop/jumpHopClient.test.ts
This commit is contained in:
@@ -14,14 +14,15 @@ import type {
|
||||
CustomWorldWorkSummary,
|
||||
} from '../../../packages/shared/src/contracts/customWorldAgent';
|
||||
import type {
|
||||
BabyObjectMatchDraft,
|
||||
CreateBabyObjectMatchDraftRequest,
|
||||
} from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||
import type {
|
||||
JumpHopRuntimeRunSnapshotResponse,
|
||||
JumpHopWorkDetailResponse,
|
||||
JumpHopWorkProfileResponse,
|
||||
JumpHopWorkSummaryResponse,
|
||||
} from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import type {
|
||||
BabyObjectMatchDraft,
|
||||
CreateBabyObjectMatchDraftRequest,
|
||||
} from '../../../packages/shared/src/contracts/edutainmentBabyObject';
|
||||
import type { Match3DAgentSessionSnapshot } from '../../../packages/shared/src/contracts/match3dAgent';
|
||||
import type { Match3DRunSnapshot } from '../../../packages/shared/src/contracts/match3dRuntime';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
@@ -70,6 +71,7 @@ import {
|
||||
submitBigFishInput,
|
||||
} from '../../services/big-fish-runtime';
|
||||
import { listBigFishWorks } from '../../services/big-fish-works';
|
||||
import { jumpHopClient } from '../../services/jump-hop/jumpHopClient';
|
||||
import {
|
||||
type CreationEntryConfig,
|
||||
fetchCreationEntryConfig,
|
||||
@@ -89,7 +91,6 @@ import {
|
||||
regenerateBabyObjectMatchDraftAssets,
|
||||
saveBabyObjectMatchDraft,
|
||||
} from '../../services/edutainment-baby-object';
|
||||
import { jumpHopClient } from '../../services/jump-hop/jumpHopClient';
|
||||
import { match3dCreationClient } from '../../services/match3d-creation';
|
||||
import { createServerMatch3DRuntimeAdapter } from '../../services/match3d-runtime';
|
||||
import {
|
||||
@@ -610,6 +611,24 @@ vi.mock('../../services/puzzle-runtime', () => ({
|
||||
usePuzzleRuntimeProp: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/jump-hop/jumpHopClient', () => ({
|
||||
jumpHopClient: {
|
||||
createSession: vi.fn(),
|
||||
deleteWork: vi.fn(),
|
||||
executeAction: vi.fn(),
|
||||
getGalleryDetail: vi.fn(),
|
||||
getLeaderboard: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
getWorkDetail: vi.fn(),
|
||||
listGallery: vi.fn(),
|
||||
listWorks: vi.fn(),
|
||||
publishWork: vi.fn(),
|
||||
restartRun: vi.fn(),
|
||||
startRun: vi.fn(),
|
||||
submitJump: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/rpg-entry/rpgEntryLibraryClient', () => ({
|
||||
...rpgEntryLibraryServiceMocks,
|
||||
}));
|
||||
@@ -657,23 +676,6 @@ vi.mock('../../services/edutainment-baby-object', () => ({
|
||||
saveBabyObjectMatchDraft: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/jump-hop/jumpHopClient', () => ({
|
||||
jumpHopClient: {
|
||||
createSession: vi.fn(),
|
||||
deleteWork: vi.fn(),
|
||||
executeAction: vi.fn(),
|
||||
getGalleryDetail: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
getWorkDetail: vi.fn(),
|
||||
listGallery: vi.fn(),
|
||||
listWorks: vi.fn(),
|
||||
publishWork: vi.fn(),
|
||||
restartRun: vi.fn(),
|
||||
startRun: vi.fn(),
|
||||
submitJump: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/wooden-fish/woodenFishClient', () => ({
|
||||
woodenFishClient: {
|
||||
checkpointRun: vi.fn(),
|
||||
@@ -684,9 +686,14 @@ vi.mock('../../services/wooden-fish/woodenFishClient', () => ({
|
||||
getGalleryDetail: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
getWorkDetail: vi.fn(),
|
||||
listGallery: vi.fn(),
|
||||
listWorks: vi.fn(),
|
||||
listGallery: vi.fn(async () => ({
|
||||
hasMore: false,
|
||||
items: [],
|
||||
nextCursor: null,
|
||||
})),
|
||||
listWorks: vi.fn(async () => ({ items: [] })),
|
||||
publishWork: vi.fn(),
|
||||
restartRun: vi.fn(),
|
||||
startRun: vi.fn(),
|
||||
},
|
||||
}));
|
||||
@@ -804,23 +811,6 @@ vi.mock('../../services/visual-novel-works', () => ({
|
||||
updateVisualNovelWork: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/wooden-fish/woodenFishClient', () => ({
|
||||
woodenFishClient: {
|
||||
checkpointRun: vi.fn(),
|
||||
createSession: vi.fn(),
|
||||
deleteWork: vi.fn(),
|
||||
executeAction: vi.fn(),
|
||||
finishRun: vi.fn(),
|
||||
getGalleryDetail: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
getWorkDetail: vi.fn(),
|
||||
listGallery: vi.fn(),
|
||||
listWorks: vi.fn(),
|
||||
publishWork: vi.fn(),
|
||||
startRun: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/visual-novel-creation', () => ({
|
||||
compileVisualNovelWorkProfile: vi.fn(),
|
||||
createVisualNovelSession: vi.fn(),
|
||||
@@ -1636,6 +1626,7 @@ function buildMockJumpHopWork(
|
||||
templateId: 'jump-hop',
|
||||
templateName: '跳一跳',
|
||||
profileId,
|
||||
themeText: '云朵跳台',
|
||||
workTitle: '云端跳台',
|
||||
workDescription: '一路跳到星星。',
|
||||
themeTags: ['云朵', '星空'],
|
||||
@@ -1649,6 +1640,7 @@ function buildMockJumpHopWork(
|
||||
tileAssets,
|
||||
path,
|
||||
coverComposite: 'data:image/png;base64,cover',
|
||||
backButtonAsset: null,
|
||||
generationStatus: 'ready' as const,
|
||||
};
|
||||
|
||||
@@ -1659,6 +1651,7 @@ function buildMockJumpHopWork(
|
||||
profileId,
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: 'jump-hop-session-1',
|
||||
themeText: draft.themeText,
|
||||
workTitle: draft.workTitle,
|
||||
workDescription: draft.workDescription,
|
||||
themeTags: draft.themeTags,
|
||||
@@ -1678,6 +1671,7 @@ function buildMockJumpHopWork(
|
||||
characterAsset,
|
||||
tileAtlasAsset,
|
||||
tileAssets,
|
||||
backButtonAsset: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5354,7 +5348,7 @@ test('match3d result trial passes generated models into first runtime mount', as
|
||||
await waitFor(() => {
|
||||
expect(match3dServerRuntimeAdapterMock.startRun).toHaveBeenCalledWith(
|
||||
'match3d-profile-draft-1',
|
||||
{},
|
||||
ISOLATED_RUNTIME_AUTH_OPTIONS,
|
||||
);
|
||||
});
|
||||
expect(
|
||||
@@ -5447,7 +5441,7 @@ test('match3d result trial passes generated 2D image views into first runtime mo
|
||||
await waitFor(() => {
|
||||
expect(match3dServerRuntimeAdapterMock.startRun).toHaveBeenCalledWith(
|
||||
'match3d-profile-draft-2d-1',
|
||||
{},
|
||||
ISOLATED_RUNTIME_AUTH_OPTIONS,
|
||||
);
|
||||
});
|
||||
expect(
|
||||
@@ -6471,10 +6465,10 @@ test('opening a compiled draft with a missing agent session falls back to draft
|
||||
await waitFor(() => {
|
||||
expect(fallbackDraftPanel.getAttribute('aria-hidden')).toBe('false');
|
||||
expect(
|
||||
within(fallbackDraftPanel).getByText(
|
||||
screen.getAllByText(
|
||||
'这份共创草稿已失效,已为你返回草稿列表,请重新开始创作。',
|
||||
),
|
||||
).toBeTruthy();
|
||||
).length,
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
expect(window.location.search).toBe('');
|
||||
@@ -6590,6 +6584,213 @@ test('logged out public detail gates puzzle start and remix before real actions'
|
||||
expect(remixPuzzleGalleryWork).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('logged out public jump-hop detail starts runtime without requireAuth', async () => {
|
||||
const user = userEvent.setup();
|
||||
const requireAuth = vi.fn();
|
||||
const publishedJumpHopWork: JumpHopWorkProfileResponse = {
|
||||
summary: {
|
||||
runtimeKind: 'jump-hop',
|
||||
workId: 'jump-hop-work-public-1',
|
||||
profileId: 'jump-hop-profile-public-12345678',
|
||||
ownerUserId: 'user-2',
|
||||
sourceSessionId: 'jump-hop-session-public-1',
|
||||
themeText: '云上方块',
|
||||
workTitle: '云上方块跳一跳',
|
||||
workDescription: '在云层地块之间连续弹跳。',
|
||||
themeTags: ['云层', '跳跃'],
|
||||
difficulty: 'standard',
|
||||
stylePreset: 'paper-toy',
|
||||
coverImageSrc: null,
|
||||
publicationStatus: 'published',
|
||||
playCount: 3,
|
||||
updatedAt: '2026-05-29T10:00:00.000Z',
|
||||
publishedAt: '2026-05-29T10:00:00.000Z',
|
||||
publishReady: true,
|
||||
generationStatus: 'ready',
|
||||
},
|
||||
draft: {
|
||||
templateId: 'jump-hop',
|
||||
templateName: '跳一跳',
|
||||
profileId: 'jump-hop-profile-public-12345678',
|
||||
themeText: '云上方块',
|
||||
workTitle: '云上方块跳一跳',
|
||||
workDescription: '在云层地块之间连续弹跳。',
|
||||
themeTags: ['云层', '跳跃'],
|
||||
difficulty: 'standard',
|
||||
stylePreset: 'paper-toy',
|
||||
defaultCharacter: {
|
||||
characterId: 'builtin-default',
|
||||
displayName: '默认角色',
|
||||
modelKind: 'builtin-three',
|
||||
bodyColor: '#df7f40',
|
||||
accentColor: '#2563eb',
|
||||
},
|
||||
characterPrompt: '',
|
||||
tilePrompt: '云上方块',
|
||||
endMoodPrompt: null,
|
||||
characterAsset: null,
|
||||
tileAtlasAsset: null,
|
||||
tileAssets: [],
|
||||
path: null,
|
||||
coverComposite: null,
|
||||
generationStatus: 'ready',
|
||||
},
|
||||
path: {
|
||||
seed: 'jump-hop-public-seed',
|
||||
difficulty: 'standard',
|
||||
platforms: [
|
||||
{
|
||||
platformId: 'platform-0',
|
||||
tileType: 'start',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
landingRadius: 0.7,
|
||||
perfectRadius: 0.25,
|
||||
scoreValue: 1,
|
||||
},
|
||||
{
|
||||
platformId: 'platform-1',
|
||||
tileType: 'normal',
|
||||
x: 1,
|
||||
y: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
landingRadius: 0.7,
|
||||
perfectRadius: 0.25,
|
||||
scoreValue: 1,
|
||||
},
|
||||
{
|
||||
platformId: 'platform-2',
|
||||
tileType: 'normal',
|
||||
x: -1,
|
||||
y: 2,
|
||||
width: 1,
|
||||
height: 1,
|
||||
landingRadius: 0.7,
|
||||
perfectRadius: 0.25,
|
||||
scoreValue: 1,
|
||||
},
|
||||
],
|
||||
finishIndex: 2,
|
||||
cameraPreset: 'portrait-top-down',
|
||||
scoring: {
|
||||
chargeToDistanceRatio: 1,
|
||||
maxChargeMs: 1800,
|
||||
hitBonus: 0,
|
||||
perfectBonus: 0,
|
||||
},
|
||||
},
|
||||
defaultCharacter: {
|
||||
characterId: 'builtin-default',
|
||||
displayName: '默认角色',
|
||||
modelKind: 'builtin-three',
|
||||
bodyColor: '#df7f40',
|
||||
accentColor: '#2563eb',
|
||||
},
|
||||
characterAsset: {
|
||||
assetId: 'builtin-character',
|
||||
imageSrc: '',
|
||||
imageObjectKey: '',
|
||||
assetObjectId: '',
|
||||
generationProvider: 'builtin',
|
||||
prompt: '',
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
tileAtlasAsset: {
|
||||
assetId: 'tile-atlas-1',
|
||||
imageSrc: '/generated-jump-hop-assets/public/atlas.png',
|
||||
imageObjectKey: 'generated-jump-hop-assets/public/atlas.png',
|
||||
assetObjectId: 'asset-tile-atlas-1',
|
||||
generationProvider: 'gpt-image-2',
|
||||
prompt: '云上方块',
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
},
|
||||
tileAssets: [],
|
||||
};
|
||||
const publishedJumpHopRun: JumpHopRuntimeRunSnapshotResponse = {
|
||||
runId: 'jump-hop-run-public-1',
|
||||
profileId: publishedJumpHopWork.summary.profileId,
|
||||
ownerUserId: '',
|
||||
status: 'playing',
|
||||
currentPlatformIndex: 0,
|
||||
successfulJumpCount: 0,
|
||||
durationMs: 0,
|
||||
score: 0,
|
||||
combo: 0,
|
||||
path: publishedJumpHopWork.path,
|
||||
lastJump: null,
|
||||
startedAtMs: 1_779_999_000_000,
|
||||
finishedAtMs: null,
|
||||
};
|
||||
|
||||
window.history.replaceState(null, '', '/works/detail?work=JH-12345678');
|
||||
vi.mocked(jumpHopClient.listGallery).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
publicWorkCode: 'JH-12345678',
|
||||
workId: publishedJumpHopWork.summary.workId,
|
||||
profileId: publishedJumpHopWork.summary.profileId,
|
||||
ownerUserId: publishedJumpHopWork.summary.ownerUserId,
|
||||
authorDisplayName: '跳跃作者',
|
||||
themeText: publishedJumpHopWork.summary.themeText,
|
||||
workTitle: publishedJumpHopWork.summary.workTitle,
|
||||
workDescription: publishedJumpHopWork.summary.workDescription,
|
||||
coverImageSrc: null,
|
||||
themeTags: publishedJumpHopWork.summary.themeTags,
|
||||
difficulty: publishedJumpHopWork.summary.difficulty,
|
||||
stylePreset: publishedJumpHopWork.summary.stylePreset,
|
||||
publicationStatus: publishedJumpHopWork.summary.publicationStatus,
|
||||
playCount: publishedJumpHopWork.summary.playCount,
|
||||
updatedAt: publishedJumpHopWork.summary.updatedAt,
|
||||
publishedAt: publishedJumpHopWork.summary.publishedAt,
|
||||
generationStatus: publishedJumpHopWork.summary.generationStatus,
|
||||
},
|
||||
],
|
||||
hasMore: false,
|
||||
nextCursor: null,
|
||||
});
|
||||
vi.mocked(jumpHopClient.getWorkDetail).mockResolvedValue({
|
||||
item: publishedJumpHopWork,
|
||||
});
|
||||
vi.mocked(jumpHopClient.startRun).mockResolvedValue({
|
||||
run: publishedJumpHopRun,
|
||||
});
|
||||
vi.mocked(jumpHopClient.getLeaderboard).mockResolvedValue({
|
||||
profileId: publishedJumpHopWork.summary.profileId,
|
||||
items: [],
|
||||
viewerBest: null,
|
||||
});
|
||||
|
||||
render(
|
||||
<TestWrapper
|
||||
authValue={createAuthValue({
|
||||
user: null,
|
||||
canAccessProtectedData: false,
|
||||
openLoginModal: () => {},
|
||||
requireAuth,
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText('详情')).toBeTruthy();
|
||||
await user.click(screen.getByRole('button', { name: '启动' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(jumpHopClient.startRun).toHaveBeenCalledWith(
|
||||
publishedJumpHopWork.summary.profileId,
|
||||
expect.objectContaining({
|
||||
runtimeGuestToken: 'runtime-guest-token',
|
||||
runtimeMode: 'published',
|
||||
}),
|
||||
);
|
||||
});
|
||||
expect(requireAuth).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('owned public puzzle detail edits original draft instead of remixing', async () => {
|
||||
const user = userEvent.setup();
|
||||
const ownedPuzzleWork = {
|
||||
@@ -8050,6 +8251,7 @@ test('direct jump hop result route restores work detail by profile id', async ()
|
||||
profileId: 'jump-hop-profile-restore-1',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: null,
|
||||
themeText: '恢复后的云端跳台',
|
||||
workTitle: '恢复后的云端跳台',
|
||||
workDescription: '从 profileId 回读完整跳一跳结果。',
|
||||
themeTags: ['云朵'],
|
||||
@@ -9222,7 +9424,7 @@ test('public code search opens a published Match3D work by M3 code and starts ru
|
||||
await waitFor(() => {
|
||||
expect(match3dServerRuntimeAdapterMock.startRun).toHaveBeenCalledWith(
|
||||
'match3d-profile-public-1',
|
||||
{},
|
||||
LOGGED_IN_RECOMMEND_RUNTIME_AUTH_OPTIONS,
|
||||
);
|
||||
});
|
||||
expect(
|
||||
@@ -9294,7 +9496,7 @@ test('published Match3D runtime receives persisted generated models', async () =
|
||||
await waitFor(() => {
|
||||
expect(match3dServerRuntimeAdapterMock.startRun).toHaveBeenCalledWith(
|
||||
'match3d-profile-model-1',
|
||||
{},
|
||||
LOGGED_IN_RECOMMEND_RUNTIME_AUTH_OPTIONS,
|
||||
);
|
||||
});
|
||||
expect(
|
||||
|
||||
Reference in New Issue
Block a user