=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,199 @@
import { beforeEach, describe, expect, it, vi } from "vitest"
const mocks = vi.hoisted(() => {
const whereMock = vi.fn()
const innerJoinMock = vi.fn(() => ({ where: whereMock }))
const fromMock = vi.fn(() => ({ innerJoin: innerJoinMock, where: whereMock }))
const selectMock = vi.fn(() => ({ from: fromMock }))
const updateWhereMock = vi.fn()
const updateSetMock = vi.fn(() => ({ where: updateWhereMock }))
const updateMock = vi.fn(() => ({ set: updateSetMock }))
const insertOnDuplicateKeyUpdateMock = vi.fn()
const insertValuesMock = vi.fn(() => ({ onDuplicateKeyUpdate: insertOnDuplicateKeyUpdateMock }))
const insertMock = vi.fn(() => ({ values: insertValuesMock }))
const enrollStudentByInvitationCodeMock = vi.fn()
return {
authMock: vi.fn(),
whereMock,
fromMock,
selectMock,
roleFindFirstMock: vi.fn(),
updateWhereMock,
updateSetMock,
updateMock,
insertOnDuplicateKeyUpdateMock,
insertValuesMock,
insertMock,
enrollStudentByInvitationCodeMock,
}
})
vi.mock("@/auth", () => ({
auth: mocks.authMock,
}))
vi.mock("@/shared/db", () => ({
db: {
select: mocks.selectMock,
query: {
roles: {
findFirst: mocks.roleFindFirstMock,
},
},
update: mocks.updateMock,
insert: mocks.insertMock,
},
}))
vi.mock("@/shared/db/schema", () => ({
classes: { id: "id", invitationCode: "invitationCode" },
classSubjectTeachers: { classId: "classId", subjectId: "subjectId", teacherId: "teacherId", updatedAt: "updatedAt" },
roles: { id: "id", name: "name" },
users: { id: "id", onboardedAt: "onboardedAt", name: "name", phone: "phone", address: "address" },
usersToRoles: { userId: "userId", roleId: "roleId" },
subjects: { id: "id", name: "name" },
}))
vi.mock("@/modules/classes/data-access", () => ({
enrollStudentByInvitationCode: mocks.enrollStudentByInvitationCodeMock,
}))
import { POST } from "@/app/api/onboarding/complete/route"
describe("POST /api/onboarding/complete", () => {
beforeEach(() => {
vi.resetAllMocks()
mocks.whereMock.mockResolvedValue([])
mocks.roleFindFirstMock.mockResolvedValue({ id: "role_student" })
})
it("returns 401 when session is missing", async () => {
mocks.authMock.mockResolvedValue(null)
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({ role: "student", name: "A" }),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(401)
expect(data).toEqual({ success: false, message: "Unauthorized" })
})
it("returns 400 for invalid payload structure", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_1" } })
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: "not-json",
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(400)
expect(data).toEqual({ success: false, message: "Invalid payload" })
})
it("returns 400 for invalid role", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_1" } })
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({ role: "guest", name: "A" }),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(400)
expect(data).toEqual({ success: false, message: "Invalid role" })
})
it("returns 403 when non-admin selects admin role", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_2" } })
mocks.whereMock.mockResolvedValue([{ name: "teacher" }])
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({ role: "admin", name: "Admin User" }),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(403)
expect(data).toEqual({ success: false, message: "Forbidden" })
})
it("returns 400 when name is missing", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_3" } })
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({ role: "student", name: " " }),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(400)
expect(data).toEqual({ success: false, message: "Name is required" })
})
it("completes student onboarding and enrolls deduplicated class codes", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_student" } })
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({
role: "student",
name: "Student A",
classCodes: "C1, C1;C2 C3",
}),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(200)
expect(data).toEqual({ success: true })
expect(mocks.enrollStudentByInvitationCodeMock).toHaveBeenCalledTimes(3)
expect(mocks.enrollStudentByInvitationCodeMock).toHaveBeenNthCalledWith(1, "u_student", "C1")
expect(mocks.enrollStudentByInvitationCodeMock).toHaveBeenNthCalledWith(2, "u_student", "C2")
expect(mocks.enrollStudentByInvitationCodeMock).toHaveBeenNthCalledWith(3, "u_student", "C3")
expect(mocks.updateSetMock).toHaveBeenCalled()
expect(mocks.insertValuesMock).toHaveBeenCalled()
})
it("completes teacher onboarding and upserts class-subject mapping", async () => {
mocks.authMock.mockResolvedValue({ user: { id: "u_teacher" } })
mocks.whereMock
.mockResolvedValueOnce([])
.mockResolvedValueOnce([{ id: "class_1", invitationCode: "T1" }])
.mockResolvedValueOnce([
{ id: "sub_math", name: "数学" },
{ id: "sub_music", name: "音乐" },
])
const req = new Request("http://localhost/api/onboarding/complete", {
method: "POST",
body: JSON.stringify({
role: "teacher",
name: "Teacher A",
classCodes: "T1",
teacherSubjects: ["数学", "音乐", "invalid"],
}),
headers: { "Content-Type": "application/json" },
})
const response = await POST(req)
const data = await response.json()
expect(response.status).toBe(200)
expect(data).toEqual({ success: true })
expect(mocks.enrollStudentByInvitationCodeMock).not.toHaveBeenCalled()
expect(mocks.insertOnDuplicateKeyUpdateMock).toHaveBeenCalledTimes(3)
})
})