feat(student-portal): 完成Docker构建验证+全量测试通过+nextstep下游依赖文档

This commit is contained in:
SpecialX
2026-07-13 16:49:31 +08:00
parent 64b2016fa8
commit 40f07b7a16
27 changed files with 4742 additions and 31 deletions

View File

@@ -0,0 +1,229 @@
/**
* Mock Fixture JSON 文件测试
*
* 验证每个 fixture 文件存在且为有效 JSON。
* Fixtures 存储原始业务数据(非 ActionState 信封),由 handlers.ts 包裹为 ActionState。
*/
import { describe, it, expect } from "vitest";
import currentUser from "./fixtures/currentUser.json";
import myClasses from "./fixtures/myClasses.json";
import myExams from "./fixtures/myExams.json";
import examDetail from "./fixtures/examDetail.json";
import myHomework from "./fixtures/myHomework.json";
import homeworkDetail from "./fixtures/homeworkDetail.json";
import myGrades from "./fixtures/myGrades.json";
import myAttendance from "./fixtures/myAttendance.json";
import textbooks from "./fixtures/textbooks.json";
import chapters from "./fixtures/chapters.json";
import learningPath from "./fixtures/learningPath.json";
import studentDashboard from "./fixtures/studentDashboard.json";
import myWeakness from "./fixtures/myWeakness.json";
import myTrend from "./fixtures/myTrend.json";
import myNotifications from "./fixtures/myNotifications.json";
import serverTime from "./fixtures/serverTime.json";
import mySchedule from "./fixtures/mySchedule.json";
import studentGrowth from "./fixtures/studentGrowth.json";
import assignmentAnalysis from "./fixtures/assignmentAnalysis.json";
import myProfile from "./fixtures/myProfile.json";
import errorBook from "./fixtures/errorBook.json";
import leaveRequests from "./fixtures/leaveRequests.json";
import electiveCourses from "./fixtures/electiveCourses.json";
import practiceSessions from "./fixtures/practiceSessions.json";
import announcements from "./fixtures/announcements.json";
import lessonPlans from "./fixtures/lessonPlans.json";
import coursePlans from "./fixtures/coursePlans.json";
import reportCard from "./fixtures/reportCard.json";
import diagnosticReports from "./fixtures/diagnosticReports.json";
import masterySummary from "./fixtures/masterySummary.json";
/** 检查值为对象(非数组、非 null */
function isObject(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
}
/** 检查值为数组 */
function isArray(v: unknown): v is unknown[] {
return Array.isArray(v);
}
describe("fixture 文件有效性", () => {
const fixtures: Array<[string, unknown]> = [
["currentUser", currentUser],
["myClasses", myClasses],
["myExams", myExams],
["examDetail", examDetail],
["myHomework", myHomework],
["homeworkDetail", homeworkDetail],
["myGrades", myGrades],
["myAttendance", myAttendance],
["textbooks", textbooks],
["chapters", chapters],
["learningPath", learningPath],
["studentDashboard", studentDashboard],
["myWeakness", myWeakness],
["myTrend", myTrend],
["myNotifications", myNotifications],
["serverTime", serverTime],
["mySchedule", mySchedule],
["studentGrowth", studentGrowth],
["assignmentAnalysis", assignmentAnalysis],
["myProfile", myProfile],
["errorBook", errorBook],
["leaveRequests", leaveRequests],
["electiveCourses", electiveCourses],
["practiceSessions", practiceSessions],
["announcements", announcements],
["lessonPlans", lessonPlans],
["coursePlans", coursePlans],
["reportCard", reportCard],
["diagnosticReports", diagnosticReports],
["masterySummary", masterySummary],
];
it("应加载 30 个 fixture 文件", () => {
expect(fixtures).toHaveLength(30);
});
it.each(fixtures)("fixture %s 应为有效的 JSON 对象或数组", (_name, data) => {
expect(data).toBeDefined();
expect(data).not.toBeNull();
expect(typeof data === "object").toBe(true);
});
});
describe("currentUser fixture", () => {
it("应包含学生信息字段", () => {
expect(isObject(currentUser)).toBe(true);
expect(currentUser).toHaveProperty("id");
expect(currentUser).toHaveProperty("name");
expect(currentUser).toHaveProperty("studentNo");
expect(currentUser).toHaveProperty("roles");
expect(currentUser).toHaveProperty("permissions");
expect(currentUser).toHaveProperty("dataScope");
});
it("roles 应为数组", () => {
expect(isObject(currentUser)).toBe(true);
expect(isArray(currentUser.roles)).toBe(true);
});
it("permissions 应为数组", () => {
expect(isObject(currentUser)).toBe(true);
expect(isArray(currentUser.permissions)).toBe(true);
});
it("dataScope 应为字符串", () => {
expect(isObject(currentUser)).toBe(true);
expect(typeof currentUser.dataScope).toBe("string");
});
});
describe("serverTime fixture", () => {
it("应包含 timestamp 和 offset 字段", () => {
expect(isObject(serverTime)).toBe(true);
expect(serverTime).toHaveProperty("timestamp");
expect(serverTime).toHaveProperty("offset");
expect(typeof serverTime.timestamp).toBe("string");
expect(typeof serverTime.offset).toBe("number");
});
});
describe("myClasses fixture", () => {
it("应为数组", () => {
expect(isArray(myClasses)).toBe(true);
});
it("每个班级应包含 id 和 name", () => {
expect(isArray(myClasses)).toBe(true);
if (myClasses.length > 0) {
const first = myClasses[0];
expect(isObject(first)).toBe(true);
expect(first).toHaveProperty("id");
expect(first).toHaveProperty("name");
}
});
});
describe("examDetail fixture", () => {
it("应包含考试详情字段", () => {
expect(isObject(examDetail)).toBe(true);
expect(examDetail).toHaveProperty("id");
expect(examDetail).toHaveProperty("name");
expect(examDetail).toHaveProperty("questions");
expect(examDetail).toHaveProperty("durationSeconds");
});
it("questions 应为数组", () => {
expect(isObject(examDetail)).toBe(true);
expect(isArray(examDetail.questions)).toBe(true);
});
});
describe("studentDashboard fixture", () => {
it("应包含仪表盘聚合字段", () => {
expect(isObject(studentDashboard)).toBe(true);
expect(studentDashboard).toHaveProperty("upcomingHomework");
expect(studentDashboard).toHaveProperty("upcomingExams");
expect(studentDashboard).toHaveProperty("recentGrades");
expect(studentDashboard).toHaveProperty("attendanceRate");
expect(studentDashboard).toHaveProperty("learningStreakDays");
expect(studentDashboard).toHaveProperty("avgScore");
expect(studentDashboard).toHaveProperty("classRank");
});
});
describe("myNotifications fixture", () => {
it("应为通知数组", () => {
expect(isArray(myNotifications)).toBe(true);
expect(myNotifications.length).toBeGreaterThan(0);
});
it("每个通知应包含 id / type / title / content / read / createdAt", () => {
expect(isArray(myNotifications)).toBe(true);
const first = (myNotifications as unknown[])[0];
expect(isObject(first)).toBe(true);
expect(first).toHaveProperty("id");
expect(first).toHaveProperty("type");
expect(first).toHaveProperty("title");
expect(first).toHaveProperty("content");
expect(first).toHaveProperty("read");
expect(first).toHaveProperty("createdAt");
});
});
describe("errorBook fixture", () => {
it("应包含 items 和 stats", () => {
expect(isObject(errorBook)).toBe(true);
expect(errorBook).toHaveProperty("items");
expect(errorBook).toHaveProperty("stats");
});
});
describe("practiceSessions fixture", () => {
it("应包含 sessions 和 stats", () => {
expect(isObject(practiceSessions)).toBe(true);
expect(practiceSessions).toHaveProperty("sessions");
expect(practiceSessions).toHaveProperty("stats");
});
});
describe("reportCard fixture", () => {
it("应包含成绩报告卡字段", () => {
expect(isObject(reportCard)).toBe(true);
expect(reportCard).toHaveProperty("studentId");
expect(reportCard).toHaveProperty("studentName");
expect(reportCard).toHaveProperty("subjects");
expect(reportCard).toHaveProperty("overallAverage");
expect(reportCard).toHaveProperty("overallRank");
});
});
describe("masterySummary fixture", () => {
it("应包含掌握度摘要字段", () => {
expect(isObject(masterySummary)).toBe(true);
expect(masterySummary).toHaveProperty("studentId");
expect(masterySummary).toHaveProperty("averageMastery");
expect(masterySummary).toHaveProperty("mastery");
});
});