Files
Genarrative/scripts/lint-staged-rustfmt.test.ts
T
kdletters 54d0fb75ea 接入 Godot 编辑器插件与受控执行链路
新增 GDExtension 自动引导、GDScript 执行和实例隔离缓存
接入 AGC 插件开关、Runner、Agent 工具与权限审计
完善执行回执确认、不确定状态阻断及卸载恢复
补齐 Windows 分发资源、定向测试与实机验收文档
2026-09-20 16:35:04 +08:00

118 lines
4.4 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, test } from 'vitest';
import {
RUSTFMT_EXCLUDED_WORKSPACES,
RUSTFMT_WORKSPACES,
selectRustfmtWorkspaces,
} from './lint-staged-rustfmt-workspaces.mjs';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
function selectedManifests(stagedFiles: string[], root = repoRoot) {
return selectRustfmtWorkspaces({
stagedFiles,
repoRoot: root,
}).workspaces.map((workspace) => workspace.manifestPath);
}
describe('lint-staged Rust 格式检查的 workspace 选择', () => {
test('只暂存 server-rs 时不再连带检查 src-tauri', () => {
// 回归判据:`cargo fmt --all` 会连未暂存的在改文件一起查,全量跑两个 workspace
// 会让「只暂存了 server-rs 干净文件」的提交被别人的 src-tauri 改动挡下来。
expect(
selectedManifests([
'server-rs/crates/api-server/src/editor_project.rs',
'server-rs/crates/shared-contracts/src/game_creation_app.rs',
]),
).toEqual(['server-rs/Cargo.toml']);
});
test('只暂存 src-tauri 时不再连带检查 server-rs', () => {
expect(
selectedManifests(['apps/ai-game-creator-shell/src-tauri/src/assets.rs']),
).toEqual(['apps/ai-game-creator-shell/src-tauri/Cargo.toml']);
});
test('多个 workspace 都有暂存文件时分别检查', () => {
expect(
selectedManifests([
'server-rs/crates/api-server/src/editor_project.rs',
'apps/ai-game-creator-shell/src-tauri/src/assets.rs',
'plugins/agc-unity-editor/native/unity-editor-bridge/src/lib.rs',
'plugins/agc-godot-editor/native/godot-editor-bridge/src/lib.rs',
]),
).toEqual([
'server-rs/Cargo.toml',
'apps/ai-game-creator-shell/src-tauri/Cargo.toml',
'plugins/agc-unity-editor/native/unity-editor-bridge/Cargo.toml',
'plugins/agc-godot-editor/native/godot-editor-bridge/Cargo.toml',
]);
});
test('手工执行(没有暂存列表)时保持全量检查语义', () => {
const selection = selectRustfmtWorkspaces({ stagedFiles: [], repoRoot });
expect(selection.usedStagedList).toBe(false);
expect(selection.workspaces).toEqual(RUSTFMT_WORKSPACES);
});
test('排除的 workspace 与未登记路径都会被显式报出,不静默通过', () => {
const selection = selectRustfmtWorkspaces({
stagedFiles: [
'apps/desktop-shell/src-tauri/src/host_bridge/mod.rs',
'somewhere-new/src/lib.rs',
],
repoRoot,
});
// desktop-shell 当前整体未过 `cargo fmt --check`,所以它不参与检查……
expect(selection.workspaces).toEqual([]);
// ……但必须被点名,否则「glob 命中了、谁都没查」就是静默通过。
expect(selection.excluded).toEqual([
'apps/desktop-shell/src-tauri/src/host_bridge/mod.rs',
]);
expect(selection.unmanaged).toEqual(['somewhere-new/src/lib.rs']);
});
test('Windows 反斜杠与绝对路径都归一化成仓库相对路径', () => {
const root = 'C:\\repo\\genarrative';
expect(
selectedManifests(
[
'server-rs\\crates\\api-server\\src\\editor_project.rs',
`${root}\\apps\\ai-game-creator-shell\\src-tauri\\src\\assets.rs`,
],
root,
),
).toEqual([
'server-rs/Cargo.toml',
'apps/ai-game-creator-shell/src-tauri/Cargo.toml',
]);
});
test('检查清单与 package.json 的 check:rustfmt 保持同一份 workspace 表', () => {
// 两处各写一份 workspace 列表就会漂移:hook 查的与 `npm run check:rustfmt` 查的必须同集。
const packageJson = JSON.parse(
readFileSync(resolve(repoRoot, 'package.json'), 'utf8'),
) as { scripts: Record<string, string> };
const manifests = Array.from(
packageJson.scripts['check:rustfmt']!.matchAll(
/--manifest-path\s+(\S+)\s+--\s+--check/gu,
),
).map((match) => match[1]!);
expect(manifests).toEqual(
RUSTFMT_WORKSPACES.map((workspace) => workspace.manifestPath),
);
// 排除项必须是「检查清单之外」的,否则这段注释与实际行为不符。
for (const excluded of RUSTFMT_EXCLUDED_WORKSPACES) {
expect(
RUSTFMT_WORKSPACES.some((workspace) =>
workspace.prefix.startsWith(excluded.prefix),
),
).toBe(false);
}
});
});