Files
Genarrative/packages/shared/src/contracts/barkBattle.test.ts
T
2026-05-22 05:00:07 +08:00

231 lines
7.7 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import {
BARK_BATTLE_ASSET_SLOTS,
BARK_BATTLE_DIFFICULTY_PRESETS,
type BarkBattleDraftConfig,
type BarkBattleDraftConfigUpdateRequest,
type BarkBattleFinishResponse,
type BarkBattleGeneratedImageAsset,
type BarkBattleImageAssetGenerateRequest,
type BarkBattlePersonalBestSummary,
type BarkBattleWorkStats,
} from './barkBattle';
describe('Bark Battle shared contracts', () => {
test('default draft config fixture uses normal difficulty and v1 description fields', () => {
const draft: BarkBattleDraftConfig = {
draftId: 'draft-bark-1',
workId: 'work-bark-1',
configVersion: 2,
rulesetVersion: 'bark-battle-ruleset-v1',
title: '汪汪声浪挑战',
description: '轻配置草稿',
themeDescription: '傍晚城市公园里的声浪擂台',
playerImageDescription: '戴红围巾的柯基主角',
opponentImageDescription: '蓝色运动头带的哈士奇对手',
onomatopoeia: ['轰汪!', '嗷呜!', '咚咚!'],
playerCharacterImageSrc: '/generated-bark-battle/player/image.png',
opponentCharacterImageSrc: 'https://example.test/opponent.png',
uiBackgroundImageSrc: '/generated-bark-battle/ui/background.png',
difficultyPreset: 'normal',
updatedAt: '2026-05-13T03:00:00.000Z',
};
expect(BARK_BATTLE_DIFFICULTY_PRESETS).toEqual(['easy', 'normal', 'hard']);
expect(draft.difficultyPreset).toBe('normal');
expect(Object.keys(draft)).toEqual([
'draftId',
'workId',
'configVersion',
'rulesetVersion',
'title',
'description',
'themeDescription',
'playerImageDescription',
'opponentImageDescription',
'onomatopoeia',
'playerCharacterImageSrc',
'opponentCharacterImageSrc',
'uiBackgroundImageSrc',
'difficultyPreset',
'updatedAt',
]);
expect(draft.playerCharacterImageSrc).toContain('/generated-bark-battle/');
expect('barkSoundSrc' in draft).toBe(false);
expect('leaderboardEnabled' in draft).toBe(false);
});
test('draft config update contract persists generated image slots only', () => {
const update: BarkBattleDraftConfigUpdateRequest = {
draftId: 'draft-bark-1',
workId: 'BB-12345678',
configVersion: 2,
rulesetVersion: 'bark-battle-ruleset-v1',
title: '汪汪声浪挑战',
description: '轻配置草稿',
themeDescription: '傍晚城市公园里的声浪擂台',
playerImageDescription: '戴红围巾的柯基主角',
opponentImageDescription: '蓝色运动头带的哈士奇对手',
onomatopoeia: ['轰!', '燃起来!', '破阵!'],
playerCharacterImageSrc: '/generated-bark-battle/player/image.png',
opponentCharacterImageSrc: '/generated-bark-battle/opponent/image.png',
uiBackgroundImageSrc: '/generated-bark-battle/ui/background.png',
difficultyPreset: 'normal',
};
expect(Object.keys(update)).toEqual([
'draftId',
'workId',
'configVersion',
'rulesetVersion',
'title',
'description',
'themeDescription',
'playerImageDescription',
'opponentImageDescription',
'onomatopoeia',
'playerCharacterImageSrc',
'opponentCharacterImageSrc',
'uiBackgroundImageSrc',
'difficultyPreset',
]);
expect('barkSoundSrc' in update).toBe(false);
expect('leaderboardEnabled' in update).toBe(false);
});
test('image generation contract uses dedicated Bark Battle slots and backend prompt result', () => {
expect(BARK_BATTLE_ASSET_SLOTS).toEqual([
'player-character',
'opponent-character',
'ui-background',
]);
const request: BarkBattleImageAssetGenerateRequest = {
slot: 'opponent-character',
draftId: 'bark-battle-draft-1',
config: {
title: '汪汪冠军杯',
description: '',
themeDescription: '霓虹公园擂台',
playerImageDescription: '红围巾柴犬',
opponentImageDescription: '蓝头带哈士奇',
onomatopoeia: ['轰汪!', '炸场!', '冲啊!'],
difficultyPreset: 'normal',
},
};
const response: BarkBattleGeneratedImageAsset = {
imageSrc: '/generated-bark-battle-assets/draft/opponent/image.webp',
assetId: 'asset-1',
sourceType: 'generated',
model: 'gpt-image-2',
size: '1024*1024',
taskId: 'task-1',
prompt: '后端拼装后的对手形象 prompt',
};
expect(JSON.parse(JSON.stringify(request))).toMatchObject({
slot: 'opponent-character',
config: {
opponentImageDescription: '蓝头带哈士奇',
onomatopoeia: ['轰汪!', '炸场!', '冲啊!'],
},
});
expect(JSON.parse(JSON.stringify(response))).toMatchObject({
imageSrc: '/generated-bark-battle-assets/draft/opponent/image.webp',
prompt: '后端拼装后的对手形象 prompt',
});
});
test('finish accepted player_win fixture exposes backend adjudication result', () => {
const response: BarkBattleFinishResponse = {
status: 'accepted',
runId: 'run-bark-1',
workId: 'work-bark-1',
configVersion: 3,
rulesetVersion: 'bark-battle-ruleset-v1',
difficultyPreset: 'hard',
serverResult: 'player_win',
scoreSummary: {
finalEnergy: 87,
triggerCount: 42,
maxVolume: 0.96,
averageVolume: 0.61,
comboMax: 9,
durationMs: 30000,
},
leaderboardScore: 870429630,
antiCheatFlags: [],
updatedAt: '2026-05-13T03:00:30.000Z',
};
expect(response.status).toBe('accepted');
expect(response.serverResult).toBe('player_win');
expect(response.scoreSummary.finalEnergy).toBe(87);
expect(response.antiCheatFlags).toEqual([]);
});
test('work stats fixture tracks starts, finishes, result counts, flags and energy summary', () => {
const stats: BarkBattleWorkStats = {
workId: 'work-bark-1',
configVersion: 3,
rulesetVersion: 'bark-battle-ruleset-v1',
difficultyPreset: 'normal',
playStartCount: 18,
finishCount: 15,
winCount: 8,
drawCount: 2,
lossCount: 5,
flaggedCount: 1,
leaderboardEntryCount: 7,
bestLeaderboardScore: 930389410,
bestFinalEnergy: 93,
averageFinalEnergy: 41.25,
updatedAt: '2026-05-13T04:00:00.000Z',
};
expect(stats.playStartCount).toBe(18);
expect(stats.finishCount).toBe(15);
expect(stats.winCount + stats.drawCount + stats.lossCount).toBe(15);
expect(stats.flaggedCount).toBe(1);
expect(stats.bestFinalEnergy).toBeGreaterThan(stats.averageFinalEnergy);
});
test('optional score fields may be omitted instead of serialized as null', () => {
const finishWithoutLeaderboard: BarkBattleFinishResponse = {
status: 'accepted',
runId: 'run-bark-no-rank',
workId: 'work-bark-1',
configVersion: 3,
rulesetVersion: 'bark-battle-ruleset-v1',
difficultyPreset: 'normal',
serverResult: 'draw',
scoreSummary: {
finalEnergy: 50,
triggerCount: 12,
maxVolume: 0.7,
averageVolume: 0.5,
comboMax: 3,
durationMs: 30000,
},
antiCheatFlags: [],
updatedAt: '2026-05-13T03:00:30.000Z',
};
const personalBestWithoutWin: BarkBattlePersonalBestSummary = {
workId: 'work-bark-1',
rulesetVersion: 'bark-battle-ruleset-v1',
difficultyPreset: 'normal',
winCount: 0,
drawCount: 1,
lossCount: 2,
finishCount: 3,
updatedAt: '2026-05-13T04:00:00.000Z',
};
expect('leaderboardScore' in finishWithoutLeaderboard).toBe(false);
expect('bestLeaderboardScore' in personalBestWithoutWin).toBe(false);
expect('bestFinalEnergy' in personalBestWithoutWin).toBe(false);
});
});