107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import Database from "better-sqlite3";
|
|
import { initSchema } from "./schema";
|
|
import {
|
|
querySymbolRefs,
|
|
querySymbolForwardCalls,
|
|
queryModuleDeps,
|
|
queryModuleReverseDeps,
|
|
queryViolations,
|
|
} from "./query";
|
|
|
|
function setupTestDb(): Database.Database {
|
|
const db = new Database(":memory:");
|
|
initSchema(db);
|
|
// 插入测试数据
|
|
db.prepare(
|
|
"INSERT INTO modules (name, path, layer, created_at) VALUES (?, ?, ?, ?)"
|
|
).run("exams", "src/modules/exams", "modules", "2026-07-07");
|
|
db.prepare(
|
|
"INSERT INTO modules (name, path, layer, created_at) VALUES (?, ?, ?, ?)"
|
|
).run("grades", "src/modules/grades", "modules", "2026-07-07");
|
|
db.prepare(
|
|
"INSERT INTO files (module_id, path, kind, lines) VALUES (?, ?, ?, ?)"
|
|
).run(1, "src/modules/exams/actions.ts", "actions", 100);
|
|
db.prepare(
|
|
"INSERT INTO files (module_id, path, kind, lines) VALUES (?, ?, ?, ?)"
|
|
).run(1, "src/modules/exams/data-access.ts", "data-access", 200);
|
|
db.prepare(
|
|
"INSERT INTO symbols (file_id, name, kind, is_exported, is_async, is_server_action, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
).run(1, "createExamAction", "function", 1, 1, 1, 10, 50);
|
|
db.prepare(
|
|
"INSERT INTO symbols (file_id, name, kind, is_exported, is_async, is_server_action, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
).run(2, "createExam", "function", 1, 1, 0, 20, 80);
|
|
db.prepare(
|
|
"INSERT INTO calls (caller_id, callee_id, call_line, count) VALUES (?, ?, ?, ?)"
|
|
).run(1, 2, 30, 1); // createExamAction 调用 createExam
|
|
db.prepare(
|
|
"INSERT INTO module_deps (source_module_id, target_module_id, dep_type) VALUES (?, ?, ?)"
|
|
).run(1, 2, "data-access-call"); // exams 依赖 grades
|
|
return db;
|
|
}
|
|
|
|
describe("querySymbolRefs", () => {
|
|
it("should find callers of a symbol (reverse)", () => {
|
|
const db = setupTestDb();
|
|
const refs = querySymbolRefs(db, "createExam");
|
|
expect(refs).toHaveLength(1);
|
|
expect(refs[0].caller_name).toBe("createExamAction");
|
|
});
|
|
});
|
|
|
|
describe("querySymbolForwardCalls", () => {
|
|
it("should find callees of a symbol (forward)", () => {
|
|
const db = setupTestDb();
|
|
const calls = querySymbolForwardCalls(db, "createExamAction");
|
|
expect(calls).toHaveLength(1);
|
|
expect(calls[0].callee_name).toBe("createExam");
|
|
});
|
|
});
|
|
|
|
describe("queryModuleDeps", () => {
|
|
it("should find module dependencies (forward)", () => {
|
|
const db = setupTestDb();
|
|
const deps = queryModuleDeps(db, "exams");
|
|
expect(deps).toContainEqual({
|
|
target_module: "grades",
|
|
dep_type: "data-access-call",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("queryModuleReverseDeps", () => {
|
|
it("should find module reverse dependencies", () => {
|
|
const db = setupTestDb();
|
|
const deps = queryModuleReverseDeps(db, "grades");
|
|
expect(deps).toContainEqual({
|
|
source_module: "exams",
|
|
dep_type: "data-access-call",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("queryViolations", () => {
|
|
it("should detect files over 800 lines", () => {
|
|
const db = setupTestDb();
|
|
// 插入一个超长文件
|
|
db.prepare(
|
|
"INSERT INTO files (module_id, path, kind, lines) VALUES (?, ?, ?, ?)"
|
|
).run(1, "src/modules/exams/huge.ts", "lib", 900);
|
|
const violations = queryViolations(db);
|
|
expect(violations.long_files).toContainEqual({
|
|
path: "src/modules/exams/huge.ts",
|
|
lines: 900,
|
|
});
|
|
});
|
|
|
|
it("should detect Server Actions without requirePermission", () => {
|
|
const db = setupTestDb();
|
|
const violations = queryViolations(db);
|
|
// createExamAction 是 Server Action 但没有调用 requirePermission
|
|
const missing = violations.server_actions_without_permission.find(
|
|
(v) => v.name === "createExamAction"
|
|
);
|
|
expect(missing).toBeDefined();
|
|
});
|
|
});
|