Skip to content

Commit 7d46922

Browse files
committed
feat: enhance image upload tests with HEIC and WebP fixtures
1 parent baf9ad5 commit 7d46922

7 files changed

Lines changed: 60 additions & 38 deletions

File tree

backend/src/main/java/com/openelements/crm/company/CompanyController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public void delete(@Parameter(description = "The company ID") @PathVariable fina
194194
@ApiResponse(responseCode = "400", description = "Invalid file format or size")
195195
@ApiResponse(responseCode = "404", description = "Company not found")
196196
public void uploadLogo(@Parameter(description = "The company ID") @PathVariable final UUID id,
197-
@Parameter(description = "The logo image file (JPEG, PNG, or SVG; max 2 MB)") @RequestParam("file") final MultipartFile file) {
197+
@Parameter(description = "The logo image file") @RequestParam("file") final MultipartFile file) {
198198
try {
199199
companyService.updateLogo(id, ImageData.of(file));
200200
} catch (final java.io.IOException e) {
1.03 MB
Binary file not shown.
1.37 MB
Loading
850 KB
Loading
551 KB
Loading

specs/102-contact-photo-heic-webp/behaviors.md

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@
22

33
This document inherits all scenarios from spec 101's `behaviors.md` (JPEG and PNG paths). The scenarios below add HEIC and WebP coverage, plus regressions to ensure the existing paths remain green.
44

5-
Scenarios marked **[fixture-disabled]** are implemented as `@Disabled` tests in v1, waiting on the fixture-provision TODO (`TODO.md`: "HEIC- und WebP-Testfixtures bereitstellen + Tests aktivieren"). They become active once the binary fixtures land.
5+
**Test fixtures.** Four happy-path fixtures live under `backend/src/test/resources/images/`:
6+
7+
- `images/test.jpeg` — iPhone 15 Pro JPEG with EXIF/GPS, orientation 1
8+
- `images/test.png` — 800×800 RGB, opaque
9+
- `images/test.webp` — VP8 lossy, opaque
10+
- `images/test.heic` — iPhone HEIF/HEVC, opaque
11+
12+
Scenarios marked **[fixture-disabled]** are implemented as `@Disabled` tests in v1 because they need fixture variants not yet on disk (rotated HEIC, alpha images, animated WebP, oversize, probe sample). They become active once those fixtures land (tracked in `TODO.md`: "HEIC- und WebP-Testfixtures bereitstellen + Tests aktivieren").
613

714
## Backend — HEIC upload (new path)
815

9-
### Opaque HEIC upload is transcoded to JPEG [fixture-disabled]
16+
### Opaque HEIC upload is transcoded to JPEG
1017

1118
- **Given** an authenticated user and an existing contact
1219
- **And** `HeicSupportCheck.isHeicAvailable() == true`
13-
- **And** an opaque HEIC file ≤ 2 MB (no alpha, no animation)
20+
- **And** the fixture `images/test.heic` (≤ 2 MB, opaque iPhone HEIC)
1421
- **When** the client posts to `POST /api/contacts/{id}/photo` with `Content-Type: image/heic`
1522
- **Then** the response is `200 OK`
1623
- **And** the contact's `photo_content_type` is `image/jpeg` (not `image/heic`)
1724
- **And** the stored bytes are valid JPEG and decode to a `BufferedImage` of the original HEIC's dimensions
1825
- **And** the stored bytes are NOT byte-equal to the uploaded HEIC
1926

20-
### `image/heif` content type is accepted equivalently [fixture-disabled]
27+
### `image/heif` content type is accepted equivalently
2128

22-
- **Given** an HEIC file uploaded with `Content-Type: image/heif` (the parent-format MIME type some clients use)
29+
- **Given** the fixture `images/test.heic` uploaded with `Content-Type: image/heif` (the parent-format MIME type some clients use)
2330
- **When** the client posts it (size ≤ 2 MB, `heicAvailable == true`)
2431
- **Then** the response is `200 OK`
2532
- **And** the stored bytes are a valid JPEG
@@ -40,14 +47,16 @@ Scenarios marked **[fixture-disabled]** are implemented as `@Disabled` tests in
4047
- **And** the `HeicTranscoderService` was never invoked (verifiable via mock assertions in unit tests)
4148
- **And** the contact's `photo` column is not modified
4249

43-
### Malformed HEIC is rejected with 400 [fixture-disabled]
50+
### Malformed HEIC is rejected with 400
4451

45-
- **Given** an upload with `Content-Type: image/heic` whose bytes are not a valid HEIC (random bytes, truncated, or another format mislabelled), size ≤ 2 MB, `heicAvailable == true`
52+
- **Given** an upload with `Content-Type: image/heic` whose bytes are a programmatically-generated random `byte[]` of length ≤ 2 MB, `heicAvailable == true`
4653
- **When** the client posts it
4754
- **Then** the response is `400 BAD REQUEST`
4855
- **And** the response body contains a recognizable "could not decode HEIC" message
4956
- **And** the contact's `photo` column is not modified
5057

58+
Note: this scenario uses an in-test-generated random byte array, not a disk fixture — no `@Disabled` needed.
59+
5160
### HEIC upload returns 415 when libheif is unavailable
5261

5362
- **Given** the backend has booted in an environment where `libheif` / `libheif-plugin-libde265` are missing or broken
@@ -60,10 +69,10 @@ Scenarios marked **[fixture-disabled]** are implemented as `@Disabled` tests in
6069

6170
## Backend — WebP upload (new path)
6271

63-
### Opaque lossy WebP upload is transcoded to JPEG [fixture-disabled]
72+
### Opaque lossy WebP upload is transcoded to JPEG
6473

6574
- **Given** an authenticated user and an existing contact
66-
- **And** a lossy opaque WebP file ≤ 2 MB
75+
- **And** the fixture `images/test.webp` (VP8 lossy, opaque, ≤ 2 MB)
6776
- **When** the client posts to `POST /api/contacts/{id}/photo` with `Content-Type: image/webp`
6877
- **Then** the response is `200 OK`
6978
- **And** the contact's `photo_content_type` is `image/jpeg`
@@ -98,14 +107,16 @@ Scenarios marked **[fixture-disabled]** are implemented as `@Disabled` tests in
98107
- **And** no transcoder invocation occurred
99108
- **And** the contact's `photo` column is not modified
100109

101-
### Malformed WebP is rejected with 400 [fixture-disabled]
110+
### Malformed WebP is rejected with 400
102111

103-
- **Given** an upload with `Content-Type: image/webp` whose bytes are not a valid WebP, size ≤ 2 MB
112+
- **Given** an upload with `Content-Type: image/webp` whose bytes are a programmatically-generated random `byte[]` of length ≤ 2 MB
104113
- **When** the client posts it
105114
- **Then** the response is `400 BAD REQUEST`
106115
- **And** the response body contains a recognizable "could not decode WebP" message
107116
- **And** the contact's `photo` column is not modified
108117

118+
Note: this scenario uses an in-test-generated random byte array, not a disk fixture — no `@Disabled` needed.
119+
109120
## Backend — startup probe
110121

111122
### Successful startup probe sets heicAvailable = true [fixture-disabled — depends on probe sample]

specs/102-contact-photo-heic-webp/design.md

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -264,36 +264,47 @@ No migration. The `photo` (`BYTEA`) and `photo_content_type` (`VARCHAR(50)`) col
264264

265265
### 7. Tests
266266

267-
Backend (`backend/src/test/.../contact/`):
267+
**Fixture inventory.** The user has placed real-world fixtures under `backend/src/test/resources/images/`:
268268

269-
| Scenario | Active in CI? |
270-
|---|---|
271-
| Existing JPEG / PNG cases from spec 101 | Active |
272-
| Happy-path opaque HEIC upload | `@Disabled` (awaits fixture) |
273-
| HEIC with EXIF orientation 6 (90° CW) is decoded upright | `@Disabled` |
274-
| HEIC > 2 MB → 400 | `@Disabled` |
275-
| Malformed HEIC bytes → 400 | `@Disabled` |
276-
| Happy-path lossy opaque WebP upload | `@Disabled` |
277-
| Lossless WebP + alpha → flattened on white | `@Disabled` |
278-
| Animated WebP → first frame stored, no error | `@Disabled` |
279-
| WebP > 2 MB → 400 | `@Disabled` |
280-
| Malformed WebP bytes → 400 | `@Disabled` |
281-
| `HeicSupportCheck` reports `heicAvailable = true` after startup probe | `@Disabled` (needs `/heic-probe/sample.heic`) |
282-
| HEIC upload when `heicAvailable = false` → 415 | Active (uses a mock `HeicSupportCheck`, no fixture needed) |
283-
| Rejected MIME types (`image/gif`, `image/bmp`, `image/svg+xml`, `image/avif`, `application/pdf`) → 400 | Active |
269+
| File | Bytes | Properties |
270+
|---|---|---|
271+
| `images/test.jpeg` | 1.4 MB | iPhone 15 Pro JPEG with EXIF (orientation 1, GPS data) |
272+
| `images/test.png` | 870 KB | 800×800 RGB, opaque (no alpha channel) |
273+
| `images/test.webp` | 565 KB | VP8 lossy, 3840×5783, opaque |
274+
| `images/test.heic` | 1.0 MB | iPhone HEIF/HEVC, opaque |
275+
276+
Each is under 2 MB so each is **accepted** by the size cap and exercises the full decode → transcode → store path.
277+
278+
Several edge-case fixtures are **not yet present** and are tracked in `TODO.md` ("HEIC- und WebP-Testfixtures bereitstellen + Tests aktivieren"). Tests that require them ship `@Disabled` with the message `"Awaiting fixture — see TODO.md: HEIC- und WebP-Testfixtures bereitstellen"`. Missing fixtures:
284279

285-
All `@Disabled` annotations carry the message `"Awaiting test fixtures — see TODO.md: HEIC- und WebP-Testfixtures bereitstellen"` so the trail is unambiguous when a developer or reviewer trips over them.
280+
- **EXIF-orientation HEIC** (e.g. `iphone-portrait-orient-6.heic`) — for the "decoded upright" test.
281+
- **Transparent PNG / lossless WebP + alpha** — for the "flatten on white" tests.
282+
- **Animated WebP** — for the "silent first frame" test.
283+
- **Oversized fixtures (> 2 MB) per format** — for the size-cap test. The existing four fixtures are all under 2 MB so a separate `oversize.heic` / `oversize.webp` / `oversize.png` is required.
284+
- **Probe sample `heic-probe/sample.heic`** (< 10 KB target) — bundled into the production JAR for `HeicSupportCheck`. The existing 1 MB `test.heic` is too large to ship inside the production artifact.
286285

287-
Fixture files (when produced): tiny binaries (< 2 KB each) under `backend/src/test/resources/contact-photo/`:
286+
**Malformed fixtures** are generated programmatically at test time (random byte arrays with the matching `Content-Type`); no disk asset needed.
288287

289-
- `iphone-portrait-orient-6.heic` — real iPhone HEIC with non-trivial EXIF orientation
290-
- `opaque.heic` — simplest HEIC
291-
- `lossy-opaque.webp`
292-
- `lossless-alpha.webp`
293-
- `animated.webp`
294-
- `oversize.heic` / `oversize.webp` — > 2 MB
295-
- `malformed.heic` / `malformed.webp` — random bytes with the matching content-type header
296-
- `heic-probe/sample.heic` — also shipped under `backend/src/main/resources/` for the startup probe
288+
**Test matrix:**
289+
290+
| Scenario | Active in CI? |
291+
|---|---|
292+
| Existing JPEG / PNG cases from spec 101, using `images/test.jpeg` and `images/test.png` | Active |
293+
| JPEG passthrough preserves EXIF including GPS (uses `images/test.jpeg`) | Active (spec 101 contract; verified here as a regression) |
294+
| Happy-path opaque HEIC upload (uses `images/test.heic`) | Active |
295+
| Happy-path lossy opaque WebP upload (uses `images/test.webp`) | Active |
296+
| HEIC upload when `heicAvailable = false` → 415 (uses a mock `HeicSupportCheck`) | Active |
297+
| Rejected MIME types (`image/gif`, `image/bmp`, `image/svg+xml`, `image/avif`, `application/pdf`) → 400 | Active |
298+
| Malformed HEIC bytes → 400 (programmatic random bytes) | Active |
299+
| Malformed WebP bytes → 400 (programmatic random bytes) | Active |
300+
| HEIC with EXIF orientation 6 (90° CW) is decoded upright | `@Disabled` (awaits fixture) |
301+
| HEIC > 2 MB → 400 | `@Disabled` (awaits fixture) |
302+
| WebP > 2 MB → 400 | `@Disabled` (awaits fixture) |
303+
| PNG > 2 MB → 400 (also spec 101) | `@Disabled` (awaits fixture) |
304+
| Lossless WebP + alpha → flattened on white | `@Disabled` (awaits fixture) |
305+
| Transparent PNG → flattened on white (also spec 101) | `@Disabled` (awaits fixture) |
306+
| Animated WebP → first frame stored, no error | `@Disabled` (awaits fixture) |
307+
| `HeicSupportCheck` reports `heicAvailable = true` after startup probe | `@Disabled` (awaits small probe sample) |
297308

298309
Frontend (`frontend/src/components/__tests__/`):
299310

0 commit comments

Comments
 (0)