|
2 | 2 |
|
3 | 3 | Lightweight S3 API mock server for local integration testing. |
4 | 4 |
|
| 5 | +> **AGENTS.md Convention**: Module-level `AGENTS.md` files inherit from this root file and contain |
| 6 | +> **only module-specific additions** — never duplicate rules already stated here. |
| 7 | +> Keep all AGENTS.md files concise: no redundant sections, no generic troubleshooting, |
| 8 | +> no restating of rules from the root. |
| 9 | +
|
5 | 10 | ## Tech Stack |
6 | 11 | - **Kotlin 2.3** (JVM 17), Spring Boot 4.0.x, Maven 3.9+ |
7 | 12 | - **Testing**: JUnit 5, Mockito, AssertJ, Testcontainers |
@@ -54,26 +59,13 @@ docker/ # Docker image build |
54 | 59 | - DON'T share mutable state between tests — each test must be self-contained |
55 | 60 | - DON'T hardcode bucket names in tests — use `UUID.randomUUID()` for uniqueness |
56 | 61 | - DON'T use legacy `testSomething` camelCase naming for new tests — use backtick names instead |
57 | | -- DON'T add new functionality to deprecated modules (`junit4/`) |
58 | 62 |
|
59 | 63 | ## Code Style |
60 | 64 |
|
61 | 65 | **Kotlin idioms**: Data classes for DTOs, null safety, expression bodies, constructor injection |
62 | 66 |
|
63 | 67 | **Spring**: `@RestController`, `@Service`, `@Component`, constructor injection over field injection |
64 | 68 |
|
65 | | -**Example**: |
66 | | -```kotlin |
67 | | -@RestController |
68 | | -class ObjectController(private val objectService: ObjectService) { |
69 | | - @GetMapping("/{bucketName:.+}/{*key}") |
70 | | - fun getObject(@PathVariable bucketName: String, @PathVariable key: String) = |
71 | | - objectService.getObject(bucketName, key).let { |
72 | | - ResponseEntity.ok().header("ETag", it.etag).body(it.data) |
73 | | - } |
74 | | -} |
75 | | -``` |
76 | | - |
77 | 69 | ## XML Serialization |
78 | 70 |
|
79 | 71 | Jackson XML with AWS-compatible structure. Key annotations: |
@@ -107,9 +99,9 @@ Environment variables (prefix: `COM_ADOBE_TESTING_S3MOCK_STORE_`): |
107 | 99 |
|
108 | 100 | ## Error Handling |
109 | 101 |
|
110 | | -`S3Exception` constants: `NO_SUCH_BUCKET`, `NO_SUCH_KEY`, `BUCKET_ALREADY_OWNED_BY_YOU`, `INVALID_BUCKET_NAME`, etc. |
111 | | - |
112 | | -HTTP codes: 200, 204, 404, 409, 500 |
| 102 | +Services throw `S3Exception` constants (`NO_SUCH_BUCKET`, `NO_SUCH_KEY`, `INVALID_BUCKET_NAME`, etc.). |
| 103 | +Spring exception handlers convert them to XML `ErrorResponse` with the correct HTTP status. |
| 104 | +See `server/AGENTS.md` for details. |
113 | 105 |
|
114 | 106 | ## Testing |
115 | 107 |
|
@@ -176,19 +168,3 @@ All PRs and pushes are validated by the `maven-ci-and-prb.yml` GitHub Actions wo |
176 | 168 | - Self-signed SSL certificate |
177 | 169 | - KMS validation only, no encryption |
178 | 170 | - Not for production |
179 | | - |
180 | | -## Common Patterns |
181 | | - |
182 | | -```kotlin |
183 | | -// ETag (using project's own DigestUtil, not Apache Commons) |
184 | | -val etag = DigestUtil.hexDigest(data) |
185 | | - |
186 | | -// Response |
187 | | -ResponseEntity.ok() |
188 | | - .eTag(normalizeEtag(s3ObjectMetadata.etag)) |
189 | | - .header("Last-Modified", lastModified) |
190 | | - .body(data) |
191 | | - |
192 | | -// Dates |
193 | | -Instant.now().toString() // ISO 8601 |
194 | | -``` |
0 commit comments