Files
Genarrative/apps/ai-game-creator-shell/tests/resourceCanvasSectionMapping.test.ts
T
suzmii 6f09bf3a00 AGC 资源画布分区轴改为 6 类资产分类加独立项目版本栏目(阶段一:契约与读时映射)
- 契约层新增 PROJECT_RESOURCE_CANVAS_SECTIONS:6 类资产分类加末尾独立的项目版本栏目,并补齐分区类型
- 契约层新增旧四 / 五栏目取值白名单、Persisted 联合类型与两个类型守卫
- Rust 契约 ProjectResourceCanvasSection 增补 7 个现行 variant 并保留 Code / Art 旧值,新增同名分区常量
- Rust 契约不升 game-creator-resource-layout.v1 版本、不给未知 section 加兜底,损坏 payload 继续失败关闭
- 新增 resourceCanvasSectionMapping.ts:旧栏目到现行分区的映射表、回落栏目与读时归一化
- 读时归一化只改写 section,x / y / manuallyPlaced 原样保留,无法归并时沿用既有丢弃行为
- resourceProjectionModel 把扩展名分类器更名为 projectedResourceKind 并收敛为准入与显示类型
- resourceProjectionModel 新增 projectResourceCanvasCategory:项目版本独立成栏、Agent 回执归文档、其余按资产分类
- Rust resource_layout 测试夹具改用现行分区值,并新增既有旧 section sidecar 仍可读取的用例
- 新增 resourceCanvasSectionMapping 测试:旧值乘目标栏目矩阵覆盖坐标保留、section 改写、changed 判定与回落列不变量
- projectResourceProjectionModel 测试新增 7 栏分区投影、项目版本独立成栏与只登记游戏代码项目落在待归类
- 同步 PRD、GameAgent 资源自由画板技术方案、AGC 实施计划与共享记忆决策记录的分区口径
2026-09-10 19:57:34 +08:00

167 lines
5.1 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
LEGACY_PROJECT_RESOURCE_CANVAS_SECTIONS,
PROJECT_RESOURCE_CANVAS_SECTIONS,
type ProjectResourceCanvasCategory,
} from '../../../packages/shared/src/contracts/gameCreationApp';
import {
LEGACY_RESOURCE_CANVAS_SECTION_FALLBACK,
LEGACY_RESOURCE_CANVAS_SECTION_TARGETS,
normalizeResourceCanvasPosition,
resolveResourceCanvasSection,
} from '../src/view/project-development/resourceCanvasSectionMapping';
function categoryByResourceId(category: ProjectResourceCanvasCategory) {
return new Map<string, ProjectResourceCanvasCategory>([
['resource-1', category],
]);
}
describe('资源画布分区轴与新旧栏目映射', () => {
it('现行分区为 6 类资产分类加末尾的项目版本栏目', () => {
expect(PROJECT_RESOURCE_CANVAS_SECTIONS).toEqual([
'ui-interaction',
'character',
'scene',
'audio',
'document',
'unclassified',
'version',
]);
});
it('每个旧栏目值都有归并目标,且回落栏目本身属于目标集合', () => {
for (const legacy of LEGACY_PROJECT_RESOURCE_CANVAS_SECTIONS) {
const targets = LEGACY_RESOURCE_CANVAS_SECTION_TARGETS[legacy];
expect(targets.length).toBeGreaterThan(0);
for (const target of targets) {
expect(PROJECT_RESOURCE_CANVAS_SECTIONS).toContain(target);
}
expect(targets).toContain(
LEGACY_RESOURCE_CANVAS_SECTION_FALLBACK[legacy],
);
}
});
it('旧栏目坐标按资源当前分区归并,x / y / manuallyPlaced 原样保留', () => {
const position = {
resourceId: 'resource-1',
section: 'art' as const,
x: -24,
y: 96,
manuallyPlaced: true,
};
for (const legacy of LEGACY_PROJECT_RESOURCE_CANVAS_SECTIONS) {
for (const target of LEGACY_RESOURCE_CANVAS_SECTION_TARGETS[legacy]) {
const normalized = normalizeResourceCanvasPosition(
{ ...position, section: legacy },
categoryByResourceId(target),
);
expect(normalized).toEqual({
position: {
resourceId: 'resource-1',
section: target,
x: -24,
y: 96,
manuallyPlaced: true,
},
changed: legacy !== target,
});
}
}
});
it('旧 art 是扩展名口径,按资源当前分区落入四类资产栏目', () => {
expect(LEGACY_RESOURCE_CANVAS_SECTION_TARGETS.art).toEqual([
'ui-interaction',
'character',
'scene',
'unclassified',
]);
for (const target of LEGACY_RESOURCE_CANVAS_SECTION_TARGETS.art) {
expect(resolveResourceCanvasSection('art', target)).toBe(target);
}
expect(LEGACY_RESOURCE_CANVAS_SECTION_FALLBACK.art).toBe('unclassified');
});
it('旧 code 统一归入待归类', () => {
expect(LEGACY_RESOURCE_CANVAS_SECTION_TARGETS.code).toEqual([
'unclassified',
]);
expect(resolveResourceCanvasSection('code', 'unclassified')).toBe(
'unclassified',
);
expect(resolveResourceCanvasSection('code', 'document')).toBeNull();
});
it('新旧同名的旧值不产生额外写入,独有旧值改写后必然判定为变化', () => {
for (const legacy of ['document', 'audio', 'version'] as const) {
const normalized = normalizeResourceCanvasPosition(
{
resourceId: 'resource-1',
section: legacy,
x: 12,
y: 34,
manuallyPlaced: false,
},
categoryByResourceId(legacy),
);
expect(normalized?.position.section).toBe(legacy);
expect(normalized?.changed).toBe(false);
}
for (const legacy of ['art', 'code'] as const) {
const target = LEGACY_RESOURCE_CANVAS_SECTION_FALLBACK[legacy];
const normalized = normalizeResourceCanvasPosition(
{
resourceId: 'resource-1',
section: legacy,
x: 12,
y: 34,
manuallyPlaced: false,
},
categoryByResourceId(target),
);
expect(normalized?.position.section).toBe(target);
expect(normalized?.changed).toBe(true);
}
});
it('现役分区与资源分区不一致时沿用既有行为丢弃该坐标', () => {
expect(resolveResourceCanvasSection('scene', 'character')).toBeNull();
expect(resolveResourceCanvasSection('version', 'document')).toBeNull();
expect(
normalizeResourceCanvasPosition(
{
resourceId: 'resource-1',
section: 'scene',
x: 0,
y: 0,
manuallyPlaced: true,
},
categoryByResourceId('character'),
),
).toBeNull();
});
it('不猜测无法识别的分区取值和未知资源', () => {
expect(resolveResourceCanvasSection('not-a-section', 'scene')).toBeNull();
expect(resolveResourceCanvasSection(undefined, 'scene')).toBeNull();
expect(resolveResourceCanvasSection(null, 'scene')).toBeNull();
expect(
normalizeResourceCanvasPosition(
{
resourceId: 'missing-resource',
section: 'scene',
x: 0,
y: 0,
manuallyPlaced: true,
},
new Map<string, ProjectResourceCanvasCategory>(),
),
).toBeNull();
});
});