Files
Edu/services/core-edu/test/grpc-smoke.mjs

191 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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);
});