chore: 强化代码质量与风格检查体系
ESLint 升级到 recommended-type-checked + stylistic-type-checked, 引入 perfectionist 导入排序和 import 插件导入验证。 Prettier 显式声明全部格式化参数,消除跨环境差异。 TypeScript 启用 noUnusedLocals 和 noPropertyAccessFromIndexSignature。 完善 ignore 列表,排除 .agents/、bun.lock、data/ 等。 引入 husky + lint-staged(pre-commit)+ commitlint(commit-msg)。 更新 DEVELOPMENT.md 代码质量章节。 修复所有新增规则检测到的类型和风格违规。
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { checkExitCode } from "../../../../../src/server/checker/runner/command/expect";
|
||||
|
||||
describe("checkExitCode", () => {
|
||||
|
||||
@@ -1,31 +1,11 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { CommandChecker } from "../../../../../src/server/checker/runner/command/runner";
|
||||
|
||||
import type { CheckerContext } from "../../../../../src/server/checker/runner/types";
|
||||
import type { ResolvedCommandTarget } from "../../../../../src/server/checker/types";
|
||||
|
||||
const checker = new CommandChecker();
|
||||
import { CommandChecker } from "../../../../../src/server/checker/runner/command/runner";
|
||||
|
||||
function makeTarget(
|
||||
command: Partial<ResolvedCommandTarget["command"]>,
|
||||
overrides?: Partial<ResolvedCommandTarget>,
|
||||
): ResolvedCommandTarget {
|
||||
return {
|
||||
type: "command",
|
||||
name: "test-cmd",
|
||||
group: "default",
|
||||
command: {
|
||||
exec: "echo",
|
||||
args: ["hello"],
|
||||
cwd: "/tmp",
|
||||
env: {},
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
...command,
|
||||
},
|
||||
intervalMs: 60000,
|
||||
timeoutMs: 5000,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
const checker = new CommandChecker();
|
||||
|
||||
function makeCtx(timeoutMs = 5000): CheckerContext {
|
||||
const controller = new AbortController();
|
||||
@@ -33,23 +13,48 @@ function makeCtx(timeoutMs = 5000): CheckerContext {
|
||||
return { signal: controller.signal };
|
||||
}
|
||||
|
||||
function makeTarget(
|
||||
command: Partial<ResolvedCommandTarget["command"]>,
|
||||
overrides?: Partial<ResolvedCommandTarget>,
|
||||
): ResolvedCommandTarget {
|
||||
return {
|
||||
command: {
|
||||
args: ["hello"],
|
||||
cwd: "/tmp",
|
||||
env: {},
|
||||
exec: "echo",
|
||||
maxOutputBytes: 1024 * 1024,
|
||||
...command,
|
||||
},
|
||||
group: "default",
|
||||
intervalMs: 60000,
|
||||
name: "test-cmd",
|
||||
timeoutMs: 5000,
|
||||
type: "command",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("CommandChecker", () => {
|
||||
test("exitCode=0 成功", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "true", args: [] }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "true" }), makeCtx());
|
||||
expect(result.matched).toBe(true);
|
||||
expect(result.statusDetail).toBe("exitCode=0");
|
||||
expect(result.failure).toBeNull();
|
||||
});
|
||||
|
||||
test("exitCode=1 不匹配默认 [0]", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "false", args: [] }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "false" }), makeCtx());
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.statusDetail).toBe("exitCode=1");
|
||||
expect(result.failure!.phase).toBe("exitCode");
|
||||
});
|
||||
|
||||
test("exitCode=1 匹配自定义 [1]", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "false", args: [] }, { expect: { exitCode: [1] } }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: [], exec: "false" }, { expect: { exitCode: [1] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
expect(result.statusDetail).toBe("exitCode=1");
|
||||
});
|
||||
@@ -62,54 +67,69 @@ describe("CommandChecker", () => {
|
||||
});
|
||||
|
||||
test("超时返回错误", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "sleep", args: ["10"] }, { timeoutMs: 100 }), makeCtx(100));
|
||||
const result = await checker.execute(makeTarget({ args: ["10"], exec: "sleep" }, { timeoutMs: 100 }), makeCtx(100));
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.failure!.message).toContain("超时");
|
||||
});
|
||||
|
||||
test("stdout 输出捕获", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "echo", args: ["hello world"] }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: ["hello world"], exec: "echo" }), makeCtx());
|
||||
expect(result.matched).toBe(true);
|
||||
});
|
||||
|
||||
test("stdout 匹配 expect", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "echo", args: ["hello"] }, { expect: { stdout: [{ contains: "hello" }] } }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["hello"], exec: "echo" }, { expect: { stdout: [{ contains: "hello" }] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
});
|
||||
|
||||
test("stdout 不匹配 expect", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "echo", args: ["hello"] }, { expect: { stdout: [{ contains: "nonexistent" }] } }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["hello"], exec: "echo" }, { expect: { stdout: [{ contains: "nonexistent" }] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.failure!.phase).toBe("stdout");
|
||||
});
|
||||
|
||||
test("stderr 匹配 expect", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "bash", args: ["-c", "echo error >&2"] }, { expect: { stderr: [{ contains: "error" }] } }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-c", "echo error >&2"], exec: "bash" }, { expect: { stderr: [{ contains: "error" }] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
});
|
||||
|
||||
test("输出超过 maxOutputBytes", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "bash", args: ["-c", "yes | head -1000"], maxOutputBytes: 10 }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["-c", "yes | head -1000"], exec: "bash", maxOutputBytes: 10 }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(false);
|
||||
expect(result.failure!.message).toContain("超过限制");
|
||||
});
|
||||
|
||||
test("durationMs 非空", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "true", args: [] }), makeCtx());
|
||||
const result = await checker.execute(makeTarget({ args: [], exec: "true" }), makeCtx());
|
||||
expect(result.durationMs).not.toBeNull();
|
||||
expect(result.durationMs!).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test("不使用 shell,通配符不被展开", async () => {
|
||||
const result = await checker.execute(makeTarget({ exec: "echo", args: ["*"] }, { expect: { stdout: [{ contains: "*" }] } }), makeCtx());
|
||||
const result = await checker.execute(
|
||||
makeTarget({ args: ["*"], exec: "echo" }, { expect: { stdout: [{ contains: "*" }] } }),
|
||||
makeCtx(),
|
||||
);
|
||||
expect(result.matched).toBe(true);
|
||||
});
|
||||
|
||||
test("serialize 返回命令摘要和 config JSON", () => {
|
||||
const target = makeTarget({ exec: "echo", args: ["hello"] });
|
||||
const target = makeTarget({ args: ["hello"], exec: "echo" });
|
||||
const s = checker.serialize(target);
|
||||
expect(s.target).toBe("exec echo hello");
|
||||
const config = JSON.parse(s.config);
|
||||
const config = JSON.parse(s.config) as { args: string[]; exec: string };
|
||||
expect(config.exec).toBe("echo");
|
||||
expect(config.args).toEqual(["hello"]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user