Files
MOPC-Portal/tests/unit/mentor-key-construction.test.ts
Matt 2e7b545a1b feat: mentor workspace files end-to-end with secure presign
Adds generateMentorObjectKey helper producing
<projectName>/mentorship/<timestamp>-<file>. Replaces the
client-supplied bucket/objectKey on workspaceUploadFile with an
HMAC-signed upload token that binds bucket, objectKey, uploader,
and a 1h expiry — paths can no longer be forged from the client.

Adds workspaceGetUploadUrl, workspaceGetFiles,
workspaceGetFileDownloadUrl, workspaceDeleteFile procedures with
mentor-or-team-member auth. Builds <WorkspaceFilesPanel> and
wires it into the mentor workspace Files tab and the applicant
/applicant/mentor page. Replaces the file-promotion-panel mock
array with a real workspaceGetFiles query.

Tests cover token sign/verify (5), key construction (5), and
end-to-end procedure flow including auth + tampered tokens (7).

Spec: docs/superpowers/specs/2026-04-28-mentor-round-readiness-design.md §F.1
Plan: docs/superpowers/plans/2026-04-28-pr2-mentor-workspace-files.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 13:33:18 +02:00

32 lines
1.3 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { generateMentorObjectKey } from '../../src/lib/minio'
describe('generateMentorObjectKey', () => {
it('produces a path under <projectName>/mentorship/<timestamp>-<file>', () => {
const key = generateMentorObjectKey('Revamp Flips', 'meeting-notes.pdf')
expect(key).toMatch(/^Revamp_Flips\/mentorship\/\d+-meeting-notes\.pdf$/)
})
it('sanitizes special characters in the project title', () => {
const key = generateMentorObjectKey('Côté & Bro 2026!', 'file.pdf')
expect(key.startsWith('Ct_Bro_2026/mentorship/')).toBe(true)
})
it('sanitizes special characters in the file name', () => {
const key = generateMentorObjectKey('Project', 'rapport final 2026 — version 2.docx')
expect(key).toMatch(/^Project\/mentorship\/\d+-rapport_final_2026___version_2\.docx$/)
})
it('falls back to "unnamed" for an empty project title', () => {
const key = generateMentorObjectKey('', 'doc.pdf')
expect(key.startsWith('unnamed/mentorship/')).toBe(true)
})
it('uses a different timestamp for sequential calls in different milliseconds', async () => {
const a = generateMentorObjectKey('P', 'a.pdf')
await new Promise((r) => setTimeout(r, 5))
const b = generateMentorObjectKey('P', 'a.pdf')
expect(a).not.toEqual(b)
})
})