=test_update_homework_tests_and_work_log
Some checks failed
CI / build-deploy (push) Has been cancelled

This commit is contained in:
SpecialX
2026-03-19 13:16:49 +08:00
parent eb08c0ab68
commit 99f116cb64
70 changed files with 7470 additions and 20220 deletions

View File

@@ -0,0 +1,298 @@
import { beforeEach, describe, expect, it, vi } from "vitest"
const mocks = vi.hoisted(() => {
const authMock = vi.fn()
const revalidatePathMock = vi.fn()
const createIdMock = vi.fn()
const ensureLimitMock = vi.fn()
const classLimitMock = vi.fn()
const enrollmentWhereMock = vi.fn()
const subjectTeacherWhereMock = vi.fn()
const examFindFirstMock = vi.fn()
const txInsertValuesMock = vi.fn()
const txInsertMock = vi.fn(() => ({ values: txInsertValuesMock }))
const transactionMock = vi.fn(async (callback: (tx: { insert: typeof txInsertMock }) => unknown) =>
callback({ insert: txInsertMock })
)
const schema = {
classes: { id: "id", teacherId: "teacherId" },
classEnrollments: { classId: "classId", studentId: "studentId", status: "status" },
classSubjectTeachers: { classId: "classId", teacherId: "teacherId", subjectId: "subjectId" },
exams: { id: "id", subjectId: "subjectId", title: "title", structure: "structure" },
homeworkAnswers: { id: "id" },
homeworkAssignmentQuestions: { assignmentId: "assignmentId", questionId: "questionId" },
homeworkAssignmentTargets: { assignmentId: "assignmentId", studentId: "studentId" },
homeworkAssignments: { id: "id" },
homeworkSubmissions: { id: "id" },
roles: { id: "id", name: "name" },
users: { id: "id" },
usersToRoles: { userId: "userId", roleId: "roleId" },
}
return {
authMock,
revalidatePathMock,
createIdMock,
ensureLimitMock,
classLimitMock,
enrollmentWhereMock,
subjectTeacherWhereMock,
examFindFirstMock,
txInsertValuesMock,
txInsertMock,
transactionMock,
schema,
}
})
vi.mock("@/auth", () => ({
auth: mocks.authMock,
}))
vi.mock("next/cache", () => ({
revalidatePath: mocks.revalidatePathMock,
}))
vi.mock("@paralleldrive/cuid2", () => ({
createId: mocks.createIdMock,
}))
vi.mock("@/shared/db/schema", () => mocks.schema)
vi.mock("@/shared/db", () => ({
db: {
select: () => ({
from: (table: unknown) => {
if (table === mocks.schema.users) {
return {
innerJoin: () => ({
innerJoin: () => ({
where: () => ({
limit: mocks.ensureLimitMock,
}),
}),
}),
}
}
if (table === mocks.schema.classes) {
return {
where: () => ({
limit: mocks.classLimitMock,
}),
}
}
if (table === mocks.schema.classEnrollments) {
return {
innerJoin: () => ({
where: mocks.enrollmentWhereMock,
}),
}
}
if (table === mocks.schema.classSubjectTeachers) {
return {
where: mocks.subjectTeacherWhereMock,
}
}
return {
where: () => ({
limit: vi.fn().mockResolvedValue([]),
}),
}
},
}),
query: {
exams: {
findFirst: mocks.examFindFirstMock,
},
},
transaction: mocks.transactionMock,
},
}))
import { createHomeworkAssignmentAction } from "@/modules/homework/actions"
describe("createHomeworkAssignmentAction", () => {
beforeEach(() => {
vi.resetAllMocks()
})
it("creates published assignment from exam with targets", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_admin" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_admin", role: "admin" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_1", teacherId: "teacher_1" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_1",
title: "Exam A",
subjectId: "subject_1",
structure: { sections: [] },
questions: [{ questionId: "q_1", score: 10, order: 1 }],
})
mocks.enrollmentWhereMock.mockResolvedValue([{ studentId: "stu_1" }, { studentId: "stu_2" }])
mocks.createIdMock.mockReturnValue("assignment_1")
const formData = new FormData()
formData.set("sourceExamId", "exam_1")
formData.set("classId", "class_1")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: true, message: "Assignment created", data: "assignment_1" })
expect(mocks.txInsertValuesMock).toHaveBeenCalledTimes(3)
expect(mocks.revalidatePathMock).toHaveBeenCalledWith("/teacher/homework/assignments")
expect(mocks.revalidatePathMock).toHaveBeenCalledWith("/teacher/homework/submissions")
})
it("returns not found when source exam does not exist", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_admin" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_admin", role: "admin" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_1", teacherId: "teacher_1" }])
mocks.examFindFirstMock.mockResolvedValue(null)
const formData = new FormData()
formData.set("sourceExamId", "missing_exam")
formData.set("classId", "class_1")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "Exam not found" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
it("blocks publish when class has no active students", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_admin" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_admin", role: "admin" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_2", teacherId: "teacher_2" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_2",
title: "Exam B",
subjectId: "subject_2",
structure: { sections: [] },
questions: [{ questionId: "q_2", score: 5, order: 1 }],
})
mocks.enrollmentWhereMock.mockResolvedValue([])
const formData = new FormData()
formData.set("sourceExamId", "exam_2")
formData.set("classId", "class_2")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "No active students in this class" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
it("blocks teacher when not assigned to class", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_teacher" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_teacher", role: "teacher" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_3", teacherId: "owner_teacher" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_3",
title: "Exam C",
subjectId: "subject_3",
structure: { sections: [] },
questions: [{ questionId: "q_3", score: 5, order: 1 }],
})
mocks.subjectTeacherWhereMock.mockResolvedValue([])
const formData = new FormData()
formData.set("sourceExamId", "exam_3")
formData.set("classId", "class_3")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "Not assigned to this class" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
it("blocks teacher when exam subject is not assigned", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_teacher" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_teacher", role: "teacher" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_4", teacherId: "owner_teacher" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_4",
title: "Exam D",
subjectId: "subject_math",
structure: { sections: [] },
questions: [{ questionId: "q_4", score: 10, order: 1 }],
})
mocks.subjectTeacherWhereMock.mockResolvedValue([{ subjectId: "subject_english" }])
const formData = new FormData()
formData.set("sourceExamId", "exam_4")
formData.set("classId", "class_4")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "Not assigned to this subject" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
it("allows teacher assigned subject to publish", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_teacher" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_teacher", role: "teacher" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_5", teacherId: "owner_teacher" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_5",
title: "Exam E",
subjectId: "subject_science",
structure: { sections: [] },
questions: [{ questionId: "q_5", score: 8, order: 1 }],
})
mocks.subjectTeacherWhereMock.mockResolvedValue([{ subjectId: "subject_science" }])
mocks.enrollmentWhereMock.mockResolvedValue([{ studentId: "stu_5" }])
mocks.createIdMock.mockReturnValue("assignment_5")
const formData = new FormData()
formData.set("sourceExamId", "exam_5")
formData.set("classId", "class_5")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: true, message: "Assignment created", data: "assignment_5" })
expect(mocks.txInsertValuesMock).toHaveBeenCalledTimes(3)
})
it("returns exam subject missing for teacher-assigned class", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_teacher" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_teacher", role: "teacher" }])
mocks.classLimitMock.mockResolvedValue([{ id: "class_6", teacherId: "owner_teacher" }])
mocks.examFindFirstMock.mockResolvedValue({
id: "exam_6",
title: "Exam F",
subjectId: null,
structure: { sections: [] },
questions: [{ questionId: "q_6", score: 10, order: 1 }],
})
mocks.subjectTeacherWhereMock.mockResolvedValue([{ subjectId: "subject_history" }])
const formData = new FormData()
formData.set("sourceExamId", "exam_6")
formData.set("classId", "class_6")
formData.set("publish", "true")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "Exam subject not set" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
it("returns class not found when class is missing", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_admin" } })
mocks.ensureLimitMock.mockResolvedValue([{ id: "u_admin", role: "admin" }])
mocks.classLimitMock.mockResolvedValue([])
const formData = new FormData()
formData.set("sourceExamId", "exam_7")
formData.set("classId", "missing_class")
const result = await createHomeworkAssignmentAction(null, formData)
expect(result).toEqual({ success: false, message: "Class not found" })
expect(mocks.transactionMock).not.toHaveBeenCalled()
})
})