Introduces the SDD root: a v1.0.0 constitution and machine-readable AGENTS.md grounded in the project's real conventions; six EARS-aware persona spec-review checklists that cross-reference .claude/personas/; feature-spec/ADR/threat-model/ api-contract templates; a fully worked _example feature; a living RTM; and an adrs/ pointer that reuses the existing docs/adr/ archive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
3.1 KiB
Markdown
48 lines
3.1 KiB
Markdown
# Tasks — Profile picture upload
|
|
|
|
> Red/Green TDD order: each implementation task is preceded by the failing test that
|
|
> requires it. Task IDs are referenced from `spec.md` → Traceability and from `.specify/rtm.md`.
|
|
> Check off as work lands; reference the issue in each commit (`Refs #<n>`).
|
|
|
|
## Backend
|
|
|
|
- [ ] **T-1** Add `ErrorCode.AVATAR_TOO_LARGE` in all four sites at once: `ErrorCode.java`,
|
|
`frontend/src/lib/shared/errors.ts`, `getErrorMessage()`, `messages/{de,en,es}.json`.
|
|
*(No new behavior yet — enables REQ-008's error.)* → covers REQ-008 (error plumbing)
|
|
- [ ] **T-2** `@WebMvcTest` `UserAvatarControllerTest`: write failing slice tests —
|
|
`unauthenticatedReturns401`, `rejectsNonImage` (400 UNSUPPORTED_FILE_TYPE),
|
|
`rejectsOversize` (400 AVATAR_TOO_LARGE). Then implement `UserAvatarController` +
|
|
`@RequirePermission` to green. → REQ-006, REQ-007, REQ-008
|
|
- [ ] **T-3** Unit `UserServiceAvatarTest`: failing tests `storesUnderUserKey`,
|
|
`replaceLeavesNoOrphan`, validation maps to `DomainException`. Then implement
|
|
`UserService.setAvatar`/`removeAvatar` (mock `FileService`) to green. → REQ-001, REQ-002, REQ-003
|
|
- [ ] **T-4** Flyway `V78__add_app_user_avatar_object_key.sql` (verify next free number on
|
|
disk) adding nullable `avatar_object_key VARCHAR(512)`; add the column + `@Schema` to
|
|
`AppUser` / `UserProfileView` (`avatarUrl` derived). Test: repository round-trip. → REQ-002
|
|
- [ ] **T-5** `deleteMyAvatar` controller test + impl (clears key, deletes object, returns
|
|
`avatarUrl: null`). → REQ-003
|
|
- [ ] **T-6** Admin path: failing tests `adminDeletesOthersAvatar` (200),
|
|
`nonAdminForbiddenOnOthers` (403). Implement ownership/`ADMIN_USER` check to green. → REQ-005, REQ-009
|
|
- [ ] **T-7** Authenticated proxy `getUserAvatar` streaming endpoint + `Content-Type` +
|
|
`X-Content-Type-Options: nosniff`; test 200 bytes / 404 when no avatar. → REQ-004 (view side)
|
|
- [ ] **T-A** Run `npm run generate:api` after T-4/T-7 so `avatarUrl` lands in `api.ts`.
|
|
|
|
## Frontend
|
|
|
|
- [ ] **T-8** i18n keys for the new strings in `messages/{de,en,es}.json` (button labels,
|
|
validation errors mapped via `getErrorMessage`). → REQ-007, REQ-008 (UX)
|
|
- [ ] **T-9** Component test `avatar-placeholder.svelte.spec.ts`: failing test asserting
|
|
initials render when `avatarUrl` is null; implement the placeholder. → REQ-004
|
|
- [ ] **T-10** `/profile` upload control: file picker, client-side type/size pre-check,
|
|
instant preview, confirm/remove. States: idle/preview/uploading/error/done. → REQ-002, REQ-003
|
|
- [ ] **T-11** Render avatar where names appear (comments, activity feed, `/users/[id]`),
|
|
falling back to the placeholder. → REQ-004
|
|
- [ ] **T-12** E2E `avatar.spec.ts`: upload → preview → confirm → avatar visible; remove →
|
|
initials return. → REQ-002, REQ-003, REQ-004
|
|
|
|
## Cross-cutting
|
|
|
|
- [ ] **T-13** Set `spring.servlet.multipart.max-file-size` to a 2 MB-matching ceiling so an
|
|
oversized body is rejected at the container edge (defense in depth for REQ-008).
|
|
- [ ] **T-14** Update `.specify/rtm.md` Status column to `Done` per REQ as each test goes green.
|