In a versioned bucket, HEAD on a key whose latest version is a delete
marker returns 405 MethodNotAllowed instead of 404. GetObjectSizeAsync
threw on it, so re-adding a previously deleted attachment (same hash,
same key) failed with a 500 before the upload started.
DeleteDirectoryAsync sent every key in one DeleteObjects request, which S3 caps at 1000, and ignored keys S3 failed to delete. It now uses the same batching as DeleteObjectsAsync, and lists objects under "{userId}/" so the prefix can't match another user whose ID starts with the same characters.
Account deletion and reset delete attachments last, after the user's data is already gone, so a failure there is now logged instead of turning a completed deletion into an error.
MinioFixture starts a MinIO-compatible server in Docker with Testcontainers (Silo, since MinIO no longer publishes images) and points S3Clients at it. S3ServiceTests cover signed uploads and object sizes, deleting an object and a user's directory, and multipart uploads within and past the storage limit. The interface stub moves out of ApiErrorResponseTests so both can use it. Running the tests now needs Docker.