getCandidates, getMentorPool and bulkAssign matched MENTOR via roles[] only, so a user with role=MENTOR but an empty roles[] array (legacy/seeded records, e.g. Arnaud Blandin on prod) was excluded from the mentor picker and rejected by bulk assign. Match MENTOR as primary role OR in roles[], mirroring userHasRole. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest'
|
|
|
|
vi.mock('@/lib/email', async () => {
|
|
const actual = await vi.importActual<typeof import('@/lib/email')>('@/lib/email')
|
|
return {
|
|
...actual,
|
|
sendMentorBulkAssignmentEmail: vi.fn(async () => true),
|
|
sendTeamMentorIntroductionEmail: vi.fn(async () => true),
|
|
}
|
|
})
|
|
|
|
import { prisma, createCaller } from '../setup'
|
|
import {
|
|
createTestProgram,
|
|
createTestCompetition,
|
|
createTestRound,
|
|
createTestProject,
|
|
createTestProjectRoundState,
|
|
createTestUser,
|
|
cleanupTestData,
|
|
uid,
|
|
} from '../helpers'
|
|
import { mentorRouter } from '../../src/server/routers/mentor'
|
|
|
|
/**
|
|
* Regression: a user whose MENTOR role is the PRIMARY role but whose roles[]
|
|
* array is empty (legacy/seeded records, e.g. prod user "Arnaud Blandin") must
|
|
* still be selectable + assignable on the mentor config page. Previously the
|
|
* candidate query filtered `roles: { has: 'MENTOR' }`, which excludes an empty
|
|
* roles[] even when role === 'MENTOR'.
|
|
*/
|
|
describe('mentor.getCandidates / bulkAssign — primary-role-only mentors are assignable', () => {
|
|
let programId = ''
|
|
let projectId = ''
|
|
let mentorId = ''
|
|
let adminId = ''
|
|
const userIds: string[] = []
|
|
|
|
beforeAll(async () => {
|
|
const program = await createTestProgram()
|
|
programId = program.id
|
|
const competition = await createTestCompetition(program.id)
|
|
const round = await createTestRound(competition.id, {
|
|
roundType: 'MENTORING',
|
|
status: 'ROUND_DRAFT', // draft → assignment emails are deferred
|
|
})
|
|
const project = await createTestProject(program.id)
|
|
projectId = project.id
|
|
await createTestProjectRoundState(project.id, round.id)
|
|
|
|
// The crux: primary role MENTOR, but roles[] is EMPTY.
|
|
const mentor = await prisma.user.create({
|
|
data: {
|
|
id: uid('u'),
|
|
email: `${uid('arnaud')}@test.local`,
|
|
name: 'Primary Only Mentor',
|
|
role: 'MENTOR',
|
|
roles: [],
|
|
status: 'ACTIVE',
|
|
},
|
|
})
|
|
mentorId = mentor.id
|
|
|
|
const admin = await createTestUser('SUPER_ADMIN')
|
|
adminId = admin.id
|
|
userIds.push(mentor.id, admin.id)
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await cleanupTestData(programId, userIds)
|
|
})
|
|
|
|
it('getCandidates includes a primary-role-only mentor', async () => {
|
|
const caller = createCaller(mentorRouter, {
|
|
id: adminId,
|
|
email: 'admin@test.local',
|
|
role: 'SUPER_ADMIN',
|
|
})
|
|
const res = await caller.getCandidates({ projectId })
|
|
const ids = res.candidates.map((c: { id: string }) => c.id)
|
|
expect(ids).toContain(mentorId)
|
|
})
|
|
|
|
it('bulkAssign accepts a primary-role-only mentor and creates the assignment', async () => {
|
|
const caller = createCaller(mentorRouter, {
|
|
id: adminId,
|
|
email: 'admin@test.local',
|
|
role: 'SUPER_ADMIN',
|
|
})
|
|
// Must not throw "None of the selected users have the MENTOR role".
|
|
await caller.bulkAssign({ mentorIds: [mentorId], projectIds: [projectId] })
|
|
|
|
const assignment = await prisma.mentorAssignment.findFirst({
|
|
where: { projectId, mentorId },
|
|
})
|
|
expect(assignment).not.toBeNull()
|
|
})
|
|
})
|