import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { prisma, createCaller } from '../setup' import { createTestUser, createTestProgram, createTestProject, cleanupTestData, uid, } from '../helpers' import { mentorRouter } from '../../src/server/routers/mentor' import { signMentorUploadToken } from '../../src/lib/mentor-upload-token' describe('mentor.workspace files end-to-end', () => { let programId: string let projectId: string let mentor: { id: string; email: string; role: 'MENTOR' } let outsider: { id: string; email: string; role: 'JURY_MEMBER' } let assignmentId: string const userIds: string[] = [] beforeAll(async () => { process.env.NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET || 'test-secret-123' const program = await createTestProgram({ name: `mentor-files-${uid()}` }) programId = program.id const project = await createTestProject(programId, { title: 'Test Project' }) projectId = project.id const m = await createTestUser('MENTOR') userIds.push(m.id) mentor = { id: m.id, email: m.email, role: 'MENTOR' } const o = await createTestUser('JURY_MEMBER') userIds.push(o.id) outsider = { id: o.id, email: o.email, role: 'JURY_MEMBER' } const assignment = await prisma.mentorAssignment.create({ data: { id: uid('ma'), projectId: project.id, mentorId: m.id, method: 'MANUAL', workspaceEnabled: true, }, }) assignmentId = assignment.id }) afterAll(async () => { await cleanupTestData(programId, userIds) }) it('issues an upload URL + signed token to the assigned mentor', async () => { const caller = createCaller(mentorRouter, mentor) const result = await caller.workspaceGetUploadUrl({ mentorAssignmentId: assignmentId, fileName: 'plan.pdf', mimeType: 'application/pdf', size: 1024, }) expect(typeof result.uploadUrl).toBe('string') expect(result.uploadUrl).toContain('plan.pdf') expect(typeof result.uploadToken).toBe('string') expect(result.objectKey).toMatch(/^Test_Project\/mentorship\/\d+-plan\.pdf$/) }) it('rejects upload-url request from a user who is neither mentor nor team member', async () => { const caller = createCaller(mentorRouter, outsider) await expect( caller.workspaceGetUploadUrl({ mentorAssignmentId: assignmentId, fileName: 'x.pdf', mimeType: 'application/pdf', size: 1, }) ).rejects.toThrow(/not a member|FORBIDDEN/i) }) it('records a file when given a valid token', async () => { const caller = createCaller(mentorRouter, mentor) const presign = await caller.workspaceGetUploadUrl({ mentorAssignmentId: assignmentId, fileName: 'a.pdf', mimeType: 'application/pdf', size: 99, }) const file = await caller.workspaceUploadFile({ uploadToken: presign.uploadToken, description: 'first file', }) expect(file.fileName).toBe('a.pdf') expect(file.objectKey).toBe(presign.objectKey) expect(file.bucket).toBeTruthy() }) it('rejects workspaceUploadFile with a token whose uploader differs from the caller', async () => { const forged = signMentorUploadToken({ mentorAssignmentId: assignmentId, projectId, uploaderUserId: 'someone-else', fileName: 'x.pdf', mimeType: 'application/pdf', size: 1, bucket: 'mopc-files', objectKey: 'a/mentorship/0-x.pdf', exp: Math.floor(Date.now() / 1000) + 60, }) const caller = createCaller(mentorRouter, mentor) await expect(caller.workspaceUploadFile({ uploadToken: forged })).rejects.toThrow(/does not belong/i) }) it('lists files for the assigned mentor, sorted newest first', async () => { const caller = createCaller(mentorRouter, mentor) const a = await caller.workspaceGetUploadUrl({ mentorAssignmentId: assignmentId, fileName: 'b.pdf', mimeType: 'application/pdf', size: 50, }) await caller.workspaceUploadFile({ uploadToken: a.uploadToken }) const files = await caller.workspaceGetFiles({ projectId }) expect(files.length).toBeGreaterThanOrEqual(2) expect(new Date(files[0].createdAt).getTime()).toBeGreaterThanOrEqual( new Date(files[1].createdAt).getTime(), ) }) it('refuses workspaceGetFiles to outsiders', async () => { const caller = createCaller(mentorRouter, outsider) await expect( caller.workspaceGetFiles({ projectId }) ).rejects.toThrow(/FORBIDDEN|not a member/i) }) it('deletes a file the user uploaded', async () => { const caller = createCaller(mentorRouter, mentor) const p = await caller.workspaceGetUploadUrl({ mentorAssignmentId: assignmentId, fileName: 'kill.pdf', mimeType: 'application/pdf', size: 10, }) const file = await caller.workspaceUploadFile({ uploadToken: p.uploadToken }) const result = await caller.workspaceDeleteFile({ mentorFileId: file.id }) expect(result.success).toBe(true) const after = await prisma.mentorFile.findUnique({ where: { id: file.id } }) expect(after).toBeNull() }) })