192 lines
5.6 KiB
TypeScript
192 lines
5.6 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const apiClientMocks = vi.hoisted(() => ({
|
|
requestJson: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('./apiClient', async () => {
|
|
const actual =
|
|
await vi.importActual<typeof import('./apiClient')>('./apiClient');
|
|
return {
|
|
...actual,
|
|
requestJson: apiClientMocks.requestJson,
|
|
};
|
|
});
|
|
|
|
import { startBigFishRun } from './big-fish-runtime/bigFishRuntimeClient';
|
|
import { startBarkBattleRun } from './bark-battle-runtime/barkBattleRuntimeClient';
|
|
import { startJumpHopRuntimeRun } from './jump-hop/jumpHopClient';
|
|
import { startMatch3DRun } from './match3d-runtime/match3dRuntimeClient';
|
|
import {
|
|
advancePuzzleNextLevel,
|
|
startPuzzleRun,
|
|
submitPuzzleLeaderboard,
|
|
} from './puzzle-runtime/puzzleRuntimeClient';
|
|
import { startSquareHoleRun } from './square-hole-runtime/squareHoleRuntimeClient';
|
|
import { startVisualNovelRun } from './visual-novel-runtime/visualNovelRuntimeClient';
|
|
|
|
describe('recommended runtime guest launch clients', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
apiClientMocks.requestJson.mockResolvedValue({ run: {} });
|
|
});
|
|
|
|
it.each([
|
|
{
|
|
name: 'jump-hop',
|
|
start: () =>
|
|
startJumpHopRuntimeRun('jump-hop-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/jump-hop/runs',
|
|
},
|
|
{
|
|
name: 'visual-novel',
|
|
start: () =>
|
|
startVisualNovelRun(
|
|
'visual-novel-profile-1',
|
|
{ profileId: 'visual-novel-profile-1', mode: 'play' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/visual-novel/works/visual-novel-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'match3d',
|
|
start: () =>
|
|
startMatch3DRun('match3d-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/match3d/works/match3d-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'square-hole',
|
|
start: () =>
|
|
startSquareHoleRun('square-hole-profile-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/square-hole/works/square-hole-profile-1/runs',
|
|
},
|
|
{
|
|
name: 'big-fish',
|
|
start: () =>
|
|
startBigFishRun('big-fish-session-1', {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/big-fish/sessions/big-fish-session-1/runs',
|
|
},
|
|
{
|
|
name: 'bark-battle',
|
|
start: () =>
|
|
startBarkBattleRun('bark-battle-work-1', {}, {
|
|
runtimeGuestToken: 'runtime-guest-token',
|
|
}),
|
|
expectedUrl: '/api/runtime/bark-battle/works/bark-battle-work-1/runs',
|
|
},
|
|
{
|
|
name: 'puzzle',
|
|
start: () =>
|
|
startPuzzleRun(
|
|
{ profileId: 'puzzle-profile-1', levelId: 'level-1' },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs',
|
|
},
|
|
{
|
|
name: 'puzzle leaderboard',
|
|
start: () =>
|
|
submitPuzzleLeaderboard(
|
|
'run-puzzle-1',
|
|
{
|
|
profileId: 'puzzle-profile-1',
|
|
gridSize: 3,
|
|
elapsedMs: 18_000,
|
|
nickname: '玩家',
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
),
|
|
expectedUrl: '/api/runtime/puzzle/runs/run-puzzle-1/leaderboard',
|
|
},
|
|
])(
|
|
'$name start request uses the runtime guest bearer token without touching login auth',
|
|
async ({ start, expectedUrl }) => {
|
|
await start();
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe(expectedUrl);
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
|
|
it('puzzle next level can carry preferSimilarWork through the runtime guest request', async () => {
|
|
await advancePuzzleNextLevel(
|
|
'run-puzzle-1',
|
|
{ preferSimilarWork: true },
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
);
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe('/api/runtime/puzzle/runs/run-puzzle-1/next-level');
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
'Content-Type': 'application/json',
|
|
}),
|
|
body: JSON.stringify({ preferSimilarWork: true }),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
skipAuth: true,
|
|
skipRefresh: true,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('puzzle leaderboard submission does not retry unsafe writes', async () => {
|
|
await submitPuzzleLeaderboard(
|
|
'run-puzzle-1',
|
|
{
|
|
profileId: 'puzzle-profile-1',
|
|
gridSize: 3,
|
|
elapsedMs: 18_000,
|
|
nickname: '玩家',
|
|
},
|
|
{ runtimeGuestToken: 'runtime-guest-token' },
|
|
);
|
|
|
|
const [url, init, , options] = apiClientMocks.requestJson.mock.calls[0];
|
|
expect(url).toBe('/api/runtime/puzzle/runs/run-puzzle-1/leaderboard');
|
|
expect(init).toEqual(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer runtime-guest-token',
|
|
'Content-Type': 'application/json',
|
|
}),
|
|
}),
|
|
);
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
retry: expect.objectContaining({
|
|
maxRetries: 0,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
});
|