Files
Genarrative/apps/ai-game-creator-shell/tests/gamePackageUploadProgress.test.ts
T
suzmii a2b70d92b8
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m21s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m18s
Project CI / Frontend tests (pull_request) Successful in 2m8s
Project CI / Repository checks (pull_request) Failing after 16s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m56s
Project CI / Native shell tests (pull_request) Successful in 5m47s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
作品管理与Phaser4客户端发布闭环
补齐 Phaser4 客户端发布前校验、阶段进度与原生幂等账本

增加 Web 作者私有作品管理、资料编辑、版本历史与软删除

增加后台作品筛选、游标分页与删除治理

同步 SpacetimeDB schema、bindings、DTO、文档与定向测试
2026-10-01 16:30:40 +08:00

137 lines
4.1 KiB
TypeScript

// @vitest-environment jsdom
/**
* 发布进度事件的前端收敛层:Rust 的 `game-package-upload-progress` 载荷在这里被
* 规范化(未知阶段丢弃)、换算百分比,并通过统一的事件订阅入口投递给 UI。
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import {
computeGamePackageUploadPercent,
GAME_PACKAGE_UPLOAD_PROGRESS_EVENT,
readGamePackageUploadProgressEvent,
subscribeGamePackageUploadProgress,
} from '../src/services/gamePackageUploadProgress';
import { createTauriEventFake, type TauriEventFake } from './tauriEventFake';
let fake: TauriEventFake | null = null;
beforeEach(() => {
fake = createTauriEventFake();
fake.install();
});
afterEach(() => {
fake?.restore();
fake = null;
});
describe('发布进度事件收敛', () => {
it('规范化原生载荷:阶段、文案与字节计数都对上', () => {
expect(
readGamePackageUploadProgressEvent({
versionId: ' gamever_1 ',
publishId: ' agc-publish-1 ',
phase: 'upload',
message: ' 正在上传发行包… ',
receivedBytes: 5_242_880.9,
totalBytes: 10_000_000,
}),
).toEqual({
versionId: 'gamever_1',
publishId: 'agc-publish-1',
phase: 'upload',
message: '正在上传发行包…',
receivedBytes: 5_242_880,
totalBytes: 10_000_000,
});
});
it('不认识的阶段与非法载荷一律丢弃,不让 UI 退回错误阶段', () => {
expect(readGamePackageUploadProgressEvent(null)).toBeNull();
expect(readGamePackageUploadProgressEvent('upload')).toBeNull();
expect(
readGamePackageUploadProgressEvent({
phase: 'staging',
receivedBytes: 1,
totalBytes: 2,
}),
).toBeNull();
expect(
readGamePackageUploadProgressEvent({
phase: 'verify',
receivedBytes: -3,
totalBytes: Number.NaN,
}),
).toEqual({
versionId: '',
publishId: '',
phase: 'verify',
message: '',
receivedBytes: 0,
totalBytes: 0,
});
});
it('只有拿到总字节才换算百分比,其余阶段返回 null', () => {
expect(computeGamePackageUploadPercent(0, 0)).toBeNull();
expect(computeGamePackageUploadPercent(5, 0)).toBeNull();
expect(computeGamePackageUploadPercent(0, 10)).toBe(0);
expect(computeGamePackageUploadPercent(5, 10)).toBe(50);
expect(computeGamePackageUploadPercent(1, 3)).toBe(33);
expect(computeGamePackageUploadPercent(12, 10)).toBe(100);
});
it('订阅只投递规范化后的事件,注销后不再投递', async () => {
const handler = vi.fn();
const unsubscribe = await subscribeGamePackageUploadProgress(handler);
fake?.flushRegistrationEvals();
fake?.emit(GAME_PACKAGE_UPLOAD_PROGRESS_EVENT, {
versionId: 'gamever_1',
publishId: 'agc-publish-1',
phase: 'upload',
message: '正在上传发行包…',
receivedBytes: 10,
totalBytes: 40,
});
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith({
versionId: 'gamever_1',
publishId: 'agc-publish-1',
phase: 'upload',
message: '正在上传发行包…',
receivedBytes: 10,
totalBytes: 40,
});
// 未知阶段不投递:UI 不会因为一条不认识的事件退回错误步骤。
fake?.emit(GAME_PACKAGE_UPLOAD_PROGRESS_EVENT, {
phase: 'unknown',
message: '不该出现',
receivedBytes: 0,
totalBytes: 0,
});
expect(handler).toHaveBeenCalledTimes(1);
unsubscribe();
fake?.emit(GAME_PACKAGE_UPLOAD_PROGRESS_EVENT, {
phase: 'submit',
message: '正在提交审核…',
receivedBytes: 0,
totalBytes: 0,
});
expect(handler).toHaveBeenCalledTimes(1);
expect(fake?.jsCallbackCount()).toBe(0);
});
it('没有原生宿主时订阅是空操作而不是报错', async () => {
fake?.restore();
fake = null;
const handler = vi.fn();
const unsubscribe = await subscribeGamePackageUploadProgress(handler);
expect(typeof unsubscribe).toBe('function');
unsubscribe();
expect(handler).not.toHaveBeenCalled();
});
});