191 lines
8.5 KiB
JavaScript
191 lines
8.5 KiB
JavaScript
// gRPC smoke test for core-edu service (P3.13)
|
||
// 运行: node services/core-edu/test/grpc-smoke.mjs
|
||
// 前提: edu-core-edu-test 容器已启动,gRPC 端口 50053 可达
|
||
import grpc from "@grpc/grpc-js";
|
||
import protoLoader from "@grpc/proto-loader";
|
||
import { fileURLToPath } from "node:url";
|
||
import path from "node:path";
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = path.dirname(__filename);
|
||
|
||
const PROTO_PATH = path.resolve(__dirname, "../../../packages/shared-proto/proto/core_edu.proto");
|
||
const packageDef = protoLoader.loadSync(PROTO_PATH, {
|
||
keepCase: true,
|
||
longs: String,
|
||
enums: String,
|
||
defaults: true,
|
||
oneofs: true,
|
||
});
|
||
const proto = grpc.loadPackageDefinition(packageDef);
|
||
|
||
const TARGET = "localhost:50053";
|
||
const clientMap = {};
|
||
|
||
const services = [
|
||
"ExamService",
|
||
"HomeworkService",
|
||
"GradeService",
|
||
"ClassService",
|
||
"AttendanceService",
|
||
"ScheduleService",
|
||
"LeaveRequestService",
|
||
"DashboardService",
|
||
"AdminService",
|
||
];
|
||
|
||
for (const s of services) {
|
||
const Ctor = proto.next_edu_cloud.core_edu.v1[s];
|
||
if (!Ctor) {
|
||
console.error(`MISSING service: ${s}`);
|
||
continue;
|
||
}
|
||
clientMap[s] = new Ctor(TARGET, grpc.credentials.createInsecure());
|
||
}
|
||
|
||
const results = { pass: [], fail: [] };
|
||
function record(name, ok, detail) {
|
||
const entry = { rpc: name, ok, detail: detail ? String(detail).slice(0, 300) : undefined };
|
||
if (ok) results.pass.push(entry);
|
||
else results.fail.push(entry);
|
||
const tag = ok ? "PASS" : "FAIL";
|
||
console.log(`[${tag}] ${name}${detail ? " :: " + entry.detail : ""}`);
|
||
}
|
||
|
||
function call(client, method, req) {
|
||
return new Promise((resolve) => {
|
||
if (!client) {
|
||
resolve({ error: new Error("client missing") });
|
||
return;
|
||
}
|
||
if (typeof client[method] !== "function") {
|
||
resolve({ error: new Error(`method ${method} not found`) });
|
||
return;
|
||
}
|
||
client[method](req, (error, response) => {
|
||
resolve({ error, response });
|
||
});
|
||
});
|
||
}
|
||
|
||
async function main() {
|
||
console.log(`\n=== core-edu gRPC smoke test @ ${TARGET} ===\n`);
|
||
|
||
// ---- ExamService ----
|
||
let r;
|
||
|
||
r = await call(clientMap.ExamService, "ListExamsByClass", { class_id: "cls-nonexistent" });
|
||
record("ExamService.ListExamsByClass", !r.error, r.error?.message || `exams=${r.response?.exams?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.ExamService, "GetExam", { id: "exam-nonexistent" });
|
||
// NotFound 是正常的业务错误(说明 RPC 可达且执行了)
|
||
record("ExamService.GetExam", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || "ok");
|
||
|
||
r = await call(clientMap.ExamService, "SaveExamDraft", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", student_id: "stu-1", answers: [] });
|
||
record("ExamService.SaveExamDraft", !r.error, r.error?.message || `draftId=${r.response?.draftId}`);
|
||
|
||
r = await call(clientMap.ExamService, "RecordExamViolation", { exam_id: "1b7f27aa-f45d-495d-b74f-cecbd0230e2e", student_id: "stu-1", violation_type: "tab_switch", detail: "test", severity: 1 });
|
||
record("ExamService.RecordExamViolation", !r.error, r.error?.message || `violationId=${r.response?.violationId}`);
|
||
|
||
// ---- HomeworkService ----
|
||
r = await call(clientMap.HomeworkService, "ListHomeworkByClass", { class_id: "cls-nonexistent" });
|
||
record("HomeworkService.ListHomeworkByClass", !r.error, r.error?.message || `homework=${r.response?.homework?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.HomeworkService, "GetHomework", { id: "hw-nonexistent" });
|
||
record("HomeworkService.GetHomework", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || "ok");
|
||
|
||
// ---- GradeService ----
|
||
r = await call(clientMap.GradeService, "ListGradesByStudent", { student_id: "stu-nonexistent" });
|
||
record("GradeService.ListGradesByStudent", !r.error, r.error?.message || `grades=${r.response?.grades?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.GradeService, "GetReportCard", { student_id: "stu-nonexistent" });
|
||
record("GradeService.GetReportCard", !r.error, r.error?.message || `overallGrade=${r.response?.overallGrade}`);
|
||
|
||
// ---- ClassService ----
|
||
r = await call(clientMap.ClassService, "GetClass", { id: "cls-nonexistent" });
|
||
record("ClassService.GetClass", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || "ok");
|
||
|
||
r = await call(clientMap.ClassService, "GetClassesByTeacher", { teacher_id: "t-nonexistent" });
|
||
record("ClassService.GetClassesByTeacher", !r.error, r.error?.message || `classes=${r.response?.classes?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.ClassService, "BatchGetClasses", { ids: [] });
|
||
record("ClassService.BatchGetClasses", !r.error, r.error?.message || `classes=${r.response?.classes?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.ClassService, "ListStudentsByClass", { class_id: "cls-nonexistent" });
|
||
record("ClassService.ListStudentsByClass", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || `students=${r.response?.students?.length ?? 0}`);
|
||
|
||
// ---- AttendanceService ----
|
||
r = await call(clientMap.AttendanceService, "ListAttendanceByStudent", { student_id: "stu-nonexistent" });
|
||
record("AttendanceService.ListAttendanceByStudent", !r.error, r.error?.message || `attendance=${r.response?.attendance?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.AttendanceService, "ListAttendanceByClass", { class_id: "cls-nonexistent" });
|
||
record("AttendanceService.ListAttendanceByClass", !r.error, r.error?.message || `attendance=${r.response?.attendance?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.AttendanceService, "GetAttendance", { id: "att-nonexistent" });
|
||
record("AttendanceService.GetAttendance", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || "ok");
|
||
|
||
// ---- ScheduleService ----
|
||
r = await call(clientMap.ScheduleService, "GetScheduleByStudent", { student_id: "stu-nonexistent" });
|
||
record("ScheduleService.GetScheduleByStudent", !r.error, r.error?.message || `slots=${r.response?.slots?.length ?? 0}`);
|
||
|
||
// ---- LeaveRequestService ----
|
||
r = await call(clientMap.LeaveRequestService, "ListLeaveRequestsByStudent", { student_id: "stu-nonexistent" });
|
||
record("LeaveRequestService.ListLeaveRequestsByStudent", !r.error, r.error?.message || `requests=${r.response?.leaveRequests?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.LeaveRequestService, "CreateLeaveRequest", {
|
||
student_id: "stu-smoke",
|
||
class_id: "cls-smoke",
|
||
leave_type: "personal",
|
||
start_date: "2026-07-14",
|
||
end_date: "2026-07-14",
|
||
reason: "smoke test",
|
||
submitted_by: "dev-teacher",
|
||
school_id: "sch-smoke",
|
||
});
|
||
record("LeaveRequestService.CreateLeaveRequest", !r.error, r.error?.message || `id=${r.response?.id}`);
|
||
|
||
// ---- DashboardService ----
|
||
r = await call(clientMap.DashboardService, "GetDashboard", { teacher_id: "t-nonexistent" });
|
||
record("DashboardService.GetDashboard", !r.error, r.error?.message || `totalClasses=${r.response?.totalClasses}`);
|
||
|
||
r = await call(clientMap.DashboardService, "GetClassPerformance", { class_id: "cls-nonexistent" });
|
||
record("DashboardService.GetClassPerformance", !r.error || r.error.code === grpc.status.NOT_FOUND, r.error?.message || `avg=${r.response?.averageScore}`);
|
||
|
||
// ---- AdminService ----
|
||
r = await call(clientMap.AdminService, "ListSchools", {});
|
||
record("AdminService.ListSchools", !r.error, r.error?.message || `schools=${r.response?.schools?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.AdminService, "ListGradeLevels", { school_id: "sch-nonexistent" });
|
||
record("AdminService.ListGradeLevels", !r.error, r.error?.message || `gradeLevels=${r.response?.gradeLevels?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.AdminService, "ListDepartments", { school_id: "sch-nonexistent" });
|
||
record("AdminService.ListDepartments", !r.error, r.error?.message || `departments=${r.response?.departments?.length ?? 0}`);
|
||
|
||
r = await call(clientMap.AdminService, "ListAcademicYears", {});
|
||
record("AdminService.ListAcademicYears", !r.error, r.error?.message || `academicYears=${r.response?.academicYears?.length ?? 0}`);
|
||
|
||
// ---- 汇总 ----
|
||
console.log(`\n=== 汇总 ===`);
|
||
console.log(`PASS: ${results.pass.length} FAIL: ${results.fail.length} TOTAL: ${results.pass.length + results.fail.length}`);
|
||
if (results.fail.length > 0) {
|
||
console.log("\n失败项:");
|
||
for (const f of results.fail) {
|
||
console.log(` - ${f.rpc}: ${f.detail}`);
|
||
}
|
||
process.exit(1);
|
||
} else {
|
||
console.log("全部 RPC 可达 ✓");
|
||
}
|
||
|
||
for (const s of services) {
|
||
clientMap[s]?.close?.();
|
||
}
|
||
grpc.getClientChannelMap?.();
|
||
process.exit(0);
|
||
}
|
||
|
||
main().catch((e) => {
|
||
console.error("smoke test crashed:", e);
|
||
process.exit(2);
|
||
});
|