Files
Genarrative/scripts/check-spacetime-schema-guard.test.ts
kdletters 46baebbc10
Project CI / Repository checks (push) Successful in 3m8s
Project CI / Frontend tests (push) Successful in 3m26s
Project CI / Backend tests (push) Successful in 3m58s
Project CI / Native shell tests (push) Successful in 15m16s
清理过期测试与门禁
删除退役内容校验、视觉小说墓碑脚本和旧玩法门禁文档

移除死别名与退役分支触发并同步当前文档入口

为Gitea和Jenkins补齐可信SpacetimeDB schema比较基线

补充workflow、schema和生产运维防回归测试
2026-08-21 12:20:43 +08:00

128 lines
4.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
collectTablesFromSources,
isFormalGateEnvironment,
listReachableRustFiles,
resolveBaseRef,
} from './check-spacetime-schema-guard.mjs';
const manifestPath = 'server-rs/crates/spacetime-module/Cargo.toml';
const sourceRoot = 'server-rs/crates/spacetime-module/src';
type SourceFiles = Record<string, string>;
function createSourceReader(files: SourceFiles) {
const sources = new Map(Object.entries(files));
return (path: string) => sources.get(path) ?? null;
}
function collectReachableTables(files: SourceFiles) {
const readSource = createSourceReader(files);
const paths = listReachableRustFiles(readSource);
return {
paths,
result: collectTablesFromSources(
paths.map((path: string) => ({ path, text: readSource(path) ?? '' })),
),
};
}
function tableSource(structName: string, accessor: string) {
return `#[spacetimedb::table(accessor = ${accessor})]\npub struct ${structName} {\n pub id: u64,\n}\n`;
}
function createGitResolver(entries: Record<string, string | null>) {
return (args: string[]) => entries[args.join(' ')] ?? null;
}
describe('SpacetimeDB schema guard base resolution', () => {
it('prefers an explicit Jenkins-provided base ref', () => {
expect(
resolveBaseRef({
argv: ['node', 'check-spacetime-schema-guard.mjs'],
env: {
CI: 'true',
SPACETIME_SCHEMA_BASE_REF: 'parent-commit',
},
git: createGitResolver({}),
}),
).toBe('parent-commit');
});
it('uses HEAD parent when the remote master ref resolves to HEAD', () => {
expect(
resolveBaseRef({
argv: ['node', 'check-spacetime-schema-guard.mjs'],
env: { CI: 'true' },
git: createGitResolver({
'rev-parse --verify HEAD^{commit}': 'current-commit',
'merge-base HEAD origin/master': 'current-commit',
'rev-parse --verify origin/master^{commit}': 'current-commit',
'rev-parse --verify HEAD^': 'parent-commit',
}),
}),
).toBe('parent-commit');
});
it('fails closed in formal gates when no distinct commit is available', () => {
expect(() =>
resolveBaseRef({
argv: ['node', 'check-spacetime-schema-guard.mjs'],
env: { JENKINS_URL: 'https://jenkins.example.test/' },
git: createGitResolver({
'rev-parse --verify HEAD^{commit}': 'current-commit',
'merge-base HEAD origin/master': 'current-commit',
'rev-parse --verify origin/master^{commit}': 'current-commit',
}),
}),
).toThrow(/无法取得与 HEAD 不同/u);
});
it('keeps HEAD fallback for a local initial repository', () => {
expect(
resolveBaseRef({
argv: ['node', 'check-spacetime-schema-guard.mjs'],
env: {},
git: createGitResolver({
'rev-parse --verify HEAD^{commit}': 'initial-commit',
}),
}),
).toBe('HEAD');
expect(isFormalGateEnvironment({ CI: 'false' })).toBe(false);
});
});
describe('SpacetimeDB schema guard module reachability', () => {
it('ignores duplicate accessors in retained but unreachable legacy sources', () => {
const activePath = `${sourceRoot}/active.rs`;
const shellPath = `${sourceRoot}/legacy_schema/example.rs`;
const retiredPath = `${sourceRoot}/example.rs`;
const { paths, result } = collectReachableTables({
[manifestPath]: '[lib]\npath = "src/active.rs"\n',
[activePath]: '#[path = "legacy_schema/example.rs"]\nmod example;\n',
[shellPath]: tableSource('ExampleSchemaShell', 'example'),
[retiredPath]: tableSource('RetiredExample', 'example'),
});
expect(paths).toEqual([activePath, shellPath]);
expect(result.failures).toEqual([]);
expect([...result.tables.keys()]).toEqual(['example']);
});
it('still rejects duplicate accessors when both definitions are reachable', () => {
const activePath = `${sourceRoot}/active.rs`;
const firstPath = `${sourceRoot}/first.rs`;
const secondPath = `${sourceRoot}/second.rs`;
const { result } = collectReachableTables({
[manifestPath]: '[lib]\npath = "src/active.rs"\n',
[activePath]: 'mod first;\nmod second;\n',
[firstPath]: tableSource('FirstExample', 'example'),
[secondPath]: tableSource('SecondExample', 'example'),
});
expect(result.failures).toHaveLength(1);
expect(result.failures[0]).toMatch(/table accessor example 重复定义/u);
});
});