233 lines
7.6 KiB
TypeScript
233 lines
7.6 KiB
TypeScript
import { expect, test } from 'vitest';
|
|
|
|
import type {
|
|
BarkBattleDraftConfig,
|
|
BarkBattlePublishedConfig,
|
|
BarkBattleWorkSummary,
|
|
} from '../../../packages/shared/src/contracts/barkBattle';
|
|
import {
|
|
buildBarkBattlePublishedConfigFromDraft,
|
|
buildBarkBattlePublishedConfigFromWork,
|
|
buildBarkBattlePublishSnapshot,
|
|
mergeBarkBattlePublishedConfigAssets,
|
|
mergeBarkBattleWorksByWorkId,
|
|
mergeBarkBattleWorkSummary,
|
|
resolveBarkBattleDraftGenerationStatus,
|
|
shouldPreserveLocalBarkBattleWorkOnRefresh,
|
|
} from './barkBattleWorkCache';
|
|
|
|
function buildBarkBattleWork(
|
|
overrides: Partial<BarkBattleWorkSummary> = {},
|
|
): BarkBattleWorkSummary {
|
|
return {
|
|
workId: 'BB-cache-race-12345678',
|
|
draftId: 'bark-battle-draft-1',
|
|
ownerUserId: 'user-1',
|
|
authorDisplayName: '测试玩家',
|
|
title: '汪汪测试杯',
|
|
summary: '测试声浪赛',
|
|
themeDescription: '阳光草坪声浪竞技场',
|
|
playerImageDescription: '戴红色围巾的柯基选手',
|
|
opponentImageDescription: '蓝色护目镜哈士奇对手',
|
|
playerCharacterImageSrc: '/generated-bark-battle/player.png',
|
|
opponentCharacterImageSrc: '/generated-bark-battle/opponent.png',
|
|
uiBackgroundImageSrc: '/generated-bark-battle/background.png',
|
|
difficultyPreset: 'normal',
|
|
status: 'draft',
|
|
generationStatus: 'ready',
|
|
publishReady: true,
|
|
playCount: 0,
|
|
updatedAt: '2026-05-21T10:00:00.000Z',
|
|
publishedAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function buildBarkBattleDraft(
|
|
overrides: Partial<BarkBattleDraftConfig> = {},
|
|
): BarkBattleDraftConfig {
|
|
return {
|
|
draftId: 'bark-battle-draft-1',
|
|
workId: 'BB-cache-race-12345678',
|
|
configVersion: 2,
|
|
rulesetVersion: 'bark-battle-ruleset-v2',
|
|
title: '汪汪测试杯',
|
|
description: '测试声浪赛',
|
|
themeDescription: '阳光草坪声浪竞技场',
|
|
playerImageDescription: '戴红色围巾的柯基选手',
|
|
opponentImageDescription: '蓝色护目镜哈士奇对手',
|
|
onomatopoeia: ['汪', '破阵'],
|
|
playerCharacterImageSrc: '/generated-bark-battle/player.png',
|
|
opponentCharacterImageSrc: '/generated-bark-battle/opponent.png',
|
|
uiBackgroundImageSrc: '/generated-bark-battle/background.png',
|
|
difficultyPreset: 'normal',
|
|
updatedAt: '2026-05-21T10:00:00.000Z',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('preserves local published bark battle when refresh only returns same work draft', () => {
|
|
const published = buildBarkBattleWork({
|
|
status: 'published',
|
|
playCount: 3,
|
|
updatedAt: '2026-05-21T10:02:00.000Z',
|
|
publishedAt: '2026-05-21T10:02:00.000Z',
|
|
});
|
|
const refreshedDraft = buildBarkBattleWork({
|
|
status: 'draft',
|
|
playCount: 0,
|
|
updatedAt: '2026-05-21T10:01:00.000Z',
|
|
publishedAt: null,
|
|
});
|
|
|
|
expect(shouldPreserveLocalBarkBattleWorkOnRefresh(published, [refreshedDraft])).toBe(
|
|
true,
|
|
);
|
|
|
|
const [merged] = mergeBarkBattleWorksByWorkId([refreshedDraft, published]);
|
|
|
|
expect(merged?.status).toBe('published');
|
|
expect(merged?.publishedAt).toBe('2026-05-21T10:02:00.000Z');
|
|
expect(merged?.playCount).toBe(3);
|
|
});
|
|
|
|
test('does not let later draft cache updates downgrade an existing published bark battle', () => {
|
|
const published = buildBarkBattleWork({
|
|
status: 'published',
|
|
playCount: 4,
|
|
updatedAt: '2026-05-21T10:03:00.000Z',
|
|
publishedAt: '2026-05-21T10:03:00.000Z',
|
|
});
|
|
const staleDraft = buildBarkBattleWork({
|
|
title: '旧草稿标题',
|
|
status: 'draft',
|
|
playCount: 0,
|
|
updatedAt: '2026-05-21T10:01:00.000Z',
|
|
publishedAt: null,
|
|
});
|
|
|
|
const merged = mergeBarkBattleWorkSummary(published, staleDraft);
|
|
|
|
expect(merged.status).toBe('published');
|
|
expect(merged.title).toBe('汪汪测试杯');
|
|
expect(merged.playCount).toBe(4);
|
|
expect(merged.publishedAt).toBe('2026-05-21T10:03:00.000Z');
|
|
});
|
|
|
|
test('preserves local ready bark battle draft when refresh has not returned it yet', () => {
|
|
const readyDraft = buildBarkBattleWork({
|
|
status: 'draft',
|
|
generationStatus: 'ready',
|
|
publishReady: true,
|
|
playerCharacterImageSrc: '/generated-bark-battle/player-ready.png',
|
|
opponentCharacterImageSrc: '/generated-bark-battle/opponent-ready.png',
|
|
uiBackgroundImageSrc: '/generated-bark-battle/background-ready.png',
|
|
});
|
|
|
|
expect(shouldPreserveLocalBarkBattleWorkOnRefresh(readyDraft, [])).toBe(true);
|
|
|
|
const merged = mergeBarkBattleWorksByWorkId([
|
|
...[],
|
|
...(shouldPreserveLocalBarkBattleWorkOnRefresh(readyDraft, [])
|
|
? [readyDraft]
|
|
: []),
|
|
]);
|
|
|
|
expect(merged).toHaveLength(1);
|
|
expect(merged[0]?.workId).toBe('BB-cache-race-12345678');
|
|
expect(merged[0]?.generationStatus).toBe('ready');
|
|
});
|
|
|
|
test('resolves bark battle draft generation status from required images', () => {
|
|
expect(
|
|
resolveBarkBattleDraftGenerationStatus(
|
|
buildBarkBattleDraft({ uiBackgroundImageSrc: undefined }),
|
|
false,
|
|
),
|
|
).toBe('pending_assets');
|
|
expect(
|
|
resolveBarkBattleDraftGenerationStatus(
|
|
buildBarkBattleDraft({ opponentCharacterImageSrc: '' }),
|
|
true,
|
|
),
|
|
).toBe('partial_failed');
|
|
expect(resolveBarkBattleDraftGenerationStatus(buildBarkBattleDraft(), true)).toBe(
|
|
'ready',
|
|
);
|
|
});
|
|
|
|
test('builds draft runtime config with stable defaults', () => {
|
|
const config = buildBarkBattlePublishedConfigFromDraft(
|
|
buildBarkBattleDraft({
|
|
workId: undefined,
|
|
configVersion: undefined,
|
|
rulesetVersion: undefined,
|
|
}),
|
|
);
|
|
|
|
expect(config.workId).toBe('bark-battle-draft-1');
|
|
expect(config.draftId).toBe('bark-battle-draft-1');
|
|
expect(config.configVersion).toBe(1);
|
|
expect(config.rulesetVersion).toBe('bark-battle-ruleset-v1');
|
|
expect(config.playTypeId).toBe('bark-battle');
|
|
expect(config.publishedAt).toBe('2026-05-21T10:00:00.000Z');
|
|
});
|
|
|
|
test('builds work runtime config with publishedAt fallback', () => {
|
|
const config = buildBarkBattlePublishedConfigFromWork(
|
|
buildBarkBattleWork({ publishedAt: null }),
|
|
);
|
|
|
|
expect(config.workId).toBe('BB-cache-race-12345678');
|
|
expect(config.description).toBe('测试声浪赛');
|
|
expect(config.publishedAt).toBe('2026-05-21T10:00:00.000Z');
|
|
expect(config.playerCharacterImageSrc).toBe('/generated-bark-battle/player.png');
|
|
});
|
|
|
|
test('builds publish snapshot without empty asset fields', () => {
|
|
const snapshot = buildBarkBattlePublishSnapshot(
|
|
buildBarkBattleDraft({
|
|
playerCharacterImageSrc: '',
|
|
opponentCharacterImageSrc: undefined,
|
|
}),
|
|
);
|
|
|
|
expect(snapshot).not.toHaveProperty('playerCharacterImageSrc');
|
|
expect(snapshot).not.toHaveProperty('opponentCharacterImageSrc');
|
|
expect(snapshot.uiBackgroundImageSrc).toBe(
|
|
'/generated-bark-battle/background.png',
|
|
);
|
|
});
|
|
|
|
test('merges draft assets into published config when publish response omits them', () => {
|
|
const draft = buildBarkBattleDraft();
|
|
const published: BarkBattlePublishedConfig = {
|
|
workId: 'BB-cache-race-12345678',
|
|
draftId: 'bark-battle-draft-1',
|
|
configVersion: 2,
|
|
rulesetVersion: 'bark-battle-ruleset-v2',
|
|
playTypeId: 'bark-battle',
|
|
title: '汪汪测试杯',
|
|
description: '测试声浪赛',
|
|
themeDescription: '阳光草坪声浪竞技场',
|
|
playerImageDescription: '戴红色围巾的柯基选手',
|
|
opponentImageDescription: '蓝色护目镜哈士奇对手',
|
|
onomatopoeia: ['汪', '破阵'],
|
|
difficultyPreset: 'normal',
|
|
updatedAt: '2026-05-21T10:01:00.000Z',
|
|
publishedAt: '2026-05-21T10:01:00.000Z',
|
|
};
|
|
|
|
const merged = mergeBarkBattlePublishedConfigAssets(published, draft);
|
|
|
|
expect(merged.playerCharacterImageSrc).toBe(
|
|
'/generated-bark-battle/player.png',
|
|
);
|
|
expect(merged.opponentCharacterImageSrc).toBe(
|
|
'/generated-bark-battle/opponent.png',
|
|
);
|
|
expect(merged.uiBackgroundImageSrc).toBe(
|
|
'/generated-bark-battle/background.png',
|
|
);
|
|
});
|