feat(mentor-workspace): re-scope files from assignment to project for team-wide visibility
- MentorFile.projectId is the new access boundary; mentorAssignmentId stays as informational audit FK (nullable). - uploadFile derives projectId from the assignment; getFiles takes projectId directly; deleteFile/addFileComment auth checks any mentor on the project OR a project team member. - HMAC upload token now binds to projectId (in addition to assignmentId). - promoteFile reads file.projectId directly (no more mentorAssignment null navigation). - Removes 3 placeholder NOT_FOUND guards added in Task 4.
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
|
||||
const samplePayload: MentorUploadPayload = {
|
||||
mentorAssignmentId: 'ma-123',
|
||||
projectId: 'proj-789',
|
||||
uploaderUserId: 'user-456',
|
||||
fileName: 'doc.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
@@ -18,6 +19,7 @@ describe('mentor.workspace files end-to-end', () => {
|
||||
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)
|
||||
@@ -79,6 +81,7 @@ describe('mentor.workspace files end-to-end', () => {
|
||||
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',
|
||||
@@ -94,7 +97,7 @@ describe('mentor.workspace files end-to-end', () => {
|
||||
mentorAssignmentId: assignmentId, fileName: 'b.pdf', mimeType: 'application/pdf', size: 50,
|
||||
})
|
||||
await caller.workspaceUploadFile({ uploadToken: a.uploadToken })
|
||||
const files = await caller.workspaceGetFiles({ mentorAssignmentId: assignmentId })
|
||||
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(),
|
||||
@@ -104,7 +107,7 @@ describe('mentor.workspace files end-to-end', () => {
|
||||
it('refuses workspaceGetFiles to outsiders', async () => {
|
||||
const caller = createCaller(mentorRouter, outsider)
|
||||
await expect(
|
||||
caller.workspaceGetFiles({ mentorAssignmentId: assignmentId })
|
||||
caller.workspaceGetFiles({ projectId })
|
||||
).rejects.toThrow(/FORBIDDEN|not a member/i)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user