S3 compatibility report
corpus 1.0.0 92.3% pass (779/844) test time 11m17s
| Group | Pass % | Pass | Fail | Blocked | Errors | Skipped | Total | |
|---|---|---|---|---|---|---|---|---|
| acl | 100.0% | 8 | 0 | 0 | 0 | 73 | 81 | |
| anon-access | 100.0% | 1 | 0 | 0 | 0 | 11 | 12 | |
| bucket | 100.0% | 30 | 0 | 0 | 0 | 19 | 49 | |
| bucket-logging | — | 0 | 0 | 0 | 0 | 11 | 11 | |
| checksums | 90.9% | 40 | 4 | 0 | 0 | 0 | 44 | |
| conditional | 100.0% | 29 | 0 | 0 | 0 | 29 | 58 | |
| copy | 84.8% | 39 | 7 | 0 | 0 | 2 | 48 | |
| cors | 100.0% | 10 | 0 | 0 | 0 | 11 | 21 | |
| encoding | 100.0% | 20 | 0 | 0 | 0 | 2 | 22 | |
| lifecycle-config | — | 0 | 0 | 0 | 0 | 29 | 29 | |
| listing | 100.0% | 105 | 0 | 0 | 0 | 6 | 111 | |
| misc | 100.0% | 5 | 0 | 0 | 0 | 6 | 11 | |
| multipart | 85.1% | 137 | 24 | 0 | 0 | 5 | 166 | |
| object-crud | 100.0% | 161 | 0 | 0 | 0 | 1 | 162 | |
| object-lock | 72.1% | 49 | 19 | 0 | 0 | 0 | 68 | |
| policy | 100.0% | 4 | 0 | 0 | 0 | 49 | 53 | |
| presigned | 100.0% | 5 | 0 | 0 | 0 | 2 | 7 | |
| sse | — | 0 | 0 | 0 | 0 | 44 | 44 | |
| tagging | 82.1% | 32 | 7 | 0 | 0 | 5 | 44 | |
| versioning | 95.6% | 86 | 4 | 0 | 0 | 2 | 92 | |
| wire-headers | 100.0% | 18 | 0 | 0 | 0 | 3 | 21 |
Canned ACL public-read at create then PutBucketAcl private resets grants to owner FULL_CONTROL only
skipped: ACL is not supported
Canned ACL authenticated-read at create grants READ to AuthenticatedUsers plus owner FULL_CONTROL
skipped: ACL is not supported
CreateBucket with canned ACL public-read grants READ to AllUsers plus owner FULL_CONTROL
skipped: quirk vector skipped by default (run it with NoSkip)
PutBucketAcl private on an already-private bucket succeeds with 200
skipped: ACL is not supported
Canned ACL public-read-write at create grants READ and WRITE to AllUsers plus owner FULL_CONTROL
skipped: ACL is not supported
Default bucket ACL is a single FULL_CONTROL grant to the bucket owner
The source test compares Owner/grantee against configured main-user identity; here the owner identity is captured from GetBucketAcl and the grant list is asserted against it.
skipped: ACL is not supported
PutBucketAcl granting by a nonexistent email address returns UnresolvableGrantByEmailAddress
skipped: ACL is not supported
PutBucketAcl granting to a nonexistent canonical user ID returns InvalidArgument
skipped: ACL is not supported
Bucket ACL user grant FULL_CONTROL lets the grantee read, read ACL, write and write ACL; owner is unchanged
skipped: quirk vector skipped by default (run it with NoSkip)
Bucket ACL user grant READ lets the grantee read the bucket but not read ACL, write, or write ACL
skipped: quirk vector skipped by default (run it with NoSkip)
Bucket ACL user grant READ_ACP lets the grantee read the ACL but not read, write, or write ACL
skipped: quirk vector skipped by default (run it with NoSkip)
Bucket ACL user grant WRITE lets the grantee write objects but not read, read ACL, or write ACL
skipped: quirk vector skipped by default (run it with NoSkip)
Bucket ACL user grant WRITE_ACP lets the grantee write the ACL but not read, read ACL, or write objects
skipped: quirk vector skipped by default (run it with NoSkip)
PutBucketAcl with an empty grant list revokes all grants including the owner's
The final step restores the owner FULL_CONTROL grant, mirroring the source test's restore so the bucket can be cleaned up.
skipped: ACL is not supported
PutObjectAcl replacing the owner grant with FULL_CONTROL reads back the same single grant
skipped: quirk vector skipped by default (run it with NoSkip)
Object canned ACL public-read then PutObjectAcl private resets grants to owner FULL_CONTROL only
skipped: ACL is not supported
PutObject with canned ACL authenticated-read grants READ to AuthenticatedUsers plus owner FULL_CONTROL
skipped: ACL is not supported
Canned ACL bucket-owner-full-control on an alt-owned object grants FULL_CONTROL to the bucket owner
The source test creates the bucket with ACL=public-read-write; here the ACL is applied to a prerequisite bucket via PutBucketAcl. Bucket-owner identity is captured from GetBucketAcl.
skipped: ACL is not supported
Canned ACL bucket-owner-read on an alt-owned object grants READ to the bucket owner
The source test creates the bucket with ACL=public-read-write; here the ACL is applied to a prerequisite bucket via PutBucketAcl. Bucket-owner identity is captured from GetBucketAcl.
skipped: ACL is not supported
PutObject with canned ACL public-read grants READ to AllUsers plus owner FULL_CONTROL
skipped: ACL is not supported
PutObject with canned ACL public-read-write grants READ and WRITE to AllUsers plus owner FULL_CONTROL
skipped: ACL is not supported
Default object ACL is a single FULL_CONTROL grant to the object owner
skipped: ACL is not supported
Replacing an object ACL does not change the object's ContentType or ETag
The source test creates the bucket with ACL=public-read-write (here PutBucketAcl on a prerequisite bucket) and injects an inert x-amz-foo request header via a boto3 event hook, which is dropped here; the asserted behavior is that attributes survive PutObjectAcl.
skipped: ACL is not supported
Grantee with FULL_CONTROL can replace the object ACL but object ownership stays with the original owner
The source test creates the bucket with ACL=public-read-write; here the ACL is applied to a prerequisite bucket via PutBucketAcl. Owner identity is captured from GetBucketAcl.
skipped: quirk vector skipped by default (run it with NoSkip)
PutObjectAcl replacing the owner grant with READ reads back the same single grant
skipped: quirk vector skipped by default (run it with NoSkip)
PutObjectAcl replacing the owner grant with READ_ACP reads back the same single grant
skipped: quirk vector skipped by default (run it with NoSkip)
PutObjectAcl replacing the owner grant with WRITE reads back the same single grant
skipped: quirk vector skipped by default (run it with NoSkip)
PutObjectAcl replacing the owner grant with WRITE_ACP reads back the same single grant
skipped: quirk vector skipped by default (run it with NoSkip)
PutBucketAcl with an explicit AllUsers group READ grant added to the owner grant is readable back
skipped: ACL is not supported
Private bucket with private object denies alt user all object reads, writes and listing
skipped: ACL is not supported
Private bucket with public-read object lets alt user read only that object
skipped: ACL is not supported
Private bucket with public-read-write object: alt user can only read that object, all writes denied
skipped: ACL is not supported
Private bucket with private object denies alt user all object reads, writes and ListObjectsV2
skipped: ACL is not supported
Private bucket with public-read object lets alt user read only that object (ListObjectsV2 denied)
skipped: ACL is not supported
Private bucket with public-read-write object: alt user can only read that object (ListObjectsV2 denied)
skipped: ACL is not supported
Public-read bucket with private objects: alt user can list but not read or write objects
skipped: ACL is not supported
Public-read bucket with public-read object: alt user can list and read the ACLed object, no writes
skipped: ACL is not supported
Public-read bucket with public-read-write object: alt user can list and read the ACLed object, all writes denied
skipped: ACL is not supported
Public-read-write bucket with private objects: alt user can list and write but not read
skipped: ACL is not supported
Public-read-write bucket with public-read object: alt user can list, write, and read only the ACLed object
skipped: ACL is not supported
Public-read-write bucket with public-read-write object: alt user can list, write, and read only the ACLed object
skipped: ACL is not supported
GetBucketOwnershipControls on a bucket created without ownership controls returns OwnershipControlsNotFoundError
skipped: quirk vector skipped by default (run it with NoSkip)
PutBucketOwnershipControls BucketOwnerEnforced: rejected while bucket ACL is public-read, then enforces bucket-owner ownership and rejects non-default ACLs
The source test creates the bucket with ACL=public-read; here the ACL is applied to a prerequisite bucket via PutBucketAcl before setting ownership controls. Bucket-owner identity is captured from GetBucketAcl instead of test configuration.
skipped: ACL is not supported
PutBucketOwnershipControls BucketOwnerPreferred: bucket-owner-full-control ACL transfers ownership to bucket owner, other writes stay owned by writer
Bucket-owner identity is captured from GetBucketAcl; the source test's owner-inequality assertions are expressed as equality with the alt writer's canonical ID.
skipped: ACL is not supported
PutBucketOwnershipControls ObjectWriter: all objects stay owned by the writing identity regardless of ACL
The source test's owner-inequality assertions (owner != bucket owner) are expressed as equality with the alt writer's canonical ID.
skipped: ACL is not supported
CreateBucket with x-amz-grant headers grants all five permissions to the grantee
skipped: quirk vector skipped by default (run it with NoSkip)
BucketOwnerEnforced ownership: bucket owner owns all objects and non-default ACLs are rejected
The source compares object owners against the main account's user id from test config; here the bucket owner identity is captured from GetBucketAcl on the newly created bucket.
skipped: ACL is not supported
BucketOwnerPreferred ownership: bucket-owner-full-control transfers object ownership
The source asserts writer-owned objects have an owner different from the bucket owner; since the writer is the alt credential, this is expressed as equality with the alt identity. The bucket owner identity is captured from GetBucketAcl.
skipped: ACL is not supported
ObjectWriter ownership: the writer keeps object ownership regardless of ACL
The source asserts written objects have an owner different from the bucket owner; since the writer is the alt credential, this is expressed as equality with the alt identity.
skipped: ACL is not supported
PutObject with x-amz-grant-* headers grants each permission to a second user
The source test injects five raw x-amz-grant-<perm> headers (read, write, read-acp, write-acp, full-control) for the alt user before signing. x-amz-grant-write has NO PutObject API parameter (AWS rejects object WRITE grants, hence quirk:not-aws), so the PUT must be a raw-HTTP step rather than an operation step. GetObjectAcl must then return all five grants for that user; check_grants compares element-wise, so the ordered array asserts the order READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL.
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject combining a canned ACL with grant headers is rejected with InvalidRequest
skipped: ACL is not supported
PutObjectAcl on a non-existent key returns NoSuchKey
skipped: ACL is not supported
PutObjectAcl with malformed grantee id or URI is rejected with InvalidArgument
skipped: ACL is not supported
PutObjectAcl with an empty grant list is accepted and the owner retains read access
skipped: ACL is not supported
Owner can read back an object written with ACL private
skipped: ACL is not supported
DeleteBucketOwnershipControls on a non-existing bucket returns NoSuchBucket
After DeleteBucketOwnershipControls, GetBucketOwnershipControls returns not-found
Original test accepts OwnershipControlsNotFoundError, OwnershipControlsNotFound or NoSuchOwnershipControls for the final get; all are HTTP 404, so only the status is asserted.
skipped: ACL is not supported
New bucket has default ObjectOwnership of BucketOwnerEnforced
skipped: ACL is not supported
GetBucketOwnershipControls on a non-existing bucket returns NoSuchBucket
GetBucketOwnershipControls returns the ownership rule that was set
skipped: ACL is not supported
PutBucketOwnershipControls with an invalid ObjectOwnership value is rejected
Valid values are BucketOwnerPreferred, BucketOwnerEnforced, ObjectWriter. Original test accepts MalformedXML or InvalidArgument; both are HTTP 400, so only the status is asserted.
PutBucketOwnershipControls with more than one rule is rejected
Only one ownership rule is allowed. Original test accepts MalformedXML or InvalidArgument; both are HTTP 400, so only the status is asserted.
PutBucketOwnershipControls on a non-existing bucket returns NoSuchBucket
PutBucketOwnershipControls with ObjectWriter succeeds
skipped: ACL is not supported
GetBucketAcl on a non-existing bucket returns NoSuchBucket
PutBucketAcl accepts canned ACL public-read
skipped: ACL is not supported
PutBucketAcl accepts canned ACL public-read-write
skipped: ACL is not supported
PutBucketAcl accepts canned ACL authenticated-read
skipped: ACL is not supported
PutBucketAcl GrantReadACP to owner id produces a READ_ACP grant
skipped: ACL is not supported
PutBucketAcl GrantWriteACP to owner id produces a WRITE_ACP grant
skipped: ACL is not supported
PutBucketAcl with an invalid canned ACL value returns InvalidArgument
PutBucketAcl with both a canned ACL and a grant header returns InvalidRequest
skipped: ACL is not supported
PutBucketAcl on a non-existing bucket returns NoSuchBucket
PutBucketAcl canned ACL private succeeds and GetBucketAcl returns Owner and Grants
skipped: ACL is not supported
PutBucketAcl canned ACL public-read succeeds and GetBucketAcl returns grants
skipped: ACL is not supported
Bucket ACL can be updated repeatedly across canned values
skipped: ACL is not supported
PutBucketAcl with a full AccessControlPolicy granting owner FULL_CONTROL
skipped: ACL is not supported
PutBucketAcl GrantFullControl to owner id produces a FULL_CONTROL grant
skipped: ACL is not supported
PutBucketAcl GrantRead to owner id produces a READ grant
skipped: ACL is not supported
PutBucketAcl GrantWrite to owner id produces a WRITE grant
skipped: ACL is not supported
Default bucket ACL has an owner and a FULL_CONTROL grant
skipped: ACL is not supported
Anonymous PutObject to a private bucket returns AccessDenied
Anonymous PutObject succeeds on a public-read-write bucket
skipped: ACL is not supported
Authenticated GetObject succeeds on public-read bucket and object
skipped: ACL is not supported
Owner GetObject succeeds on a public-read object despite a private bucket ACL
The source creates the bucket with ACL 'private', which is the default canned ACL for a new bucket, so the bucket prerequisite carries no ACL step.
skipped: ACL is not supported
Authenticated GetObject on a deleted bucket returns NoSuchBucket
skipped: ACL is not supported
Owner GetObject succeeds on a private object in a public-read bucket
skipped: ACL is not supported
Authenticated GetObject on a deleted object returns NoSuchKey
skipped: ACL is not supported
Anonymous GetObject succeeds on public-read bucket and object
skipped: ACL is not supported
Anonymous GetObject succeeds on a public-read object in a private bucket
The source creates the bucket with ACL 'private', which is the default canned ACL for a new bucket, so the bucket prerequisite carries no ACL step.
skipped: ACL is not supported
Anonymous GetObject on a deleted bucket returns NoSuchBucket
skipped: ACL is not supported
Anonymous GetObject on a private object in a public-read bucket returns AccessDenied
skipped: ACL is not supported
Anonymous GetObject on a deleted object in a public-read bucket returns NoSuchKey
skipped: ACL is not supported
DeleteBucket succeeds once, then repeating it returns NoSuchBucket
CreateBucket on a name owned by another user returns BucketAlreadyExists
CreateBucket with an IP-address-formatted name returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
CreateBucket with a one-character name returns InvalidBucketName
CreateBucket with a two-character name returns InvalidBucketName
CreateBucket with a name starting with underscore returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
Raw CreateBucket with a name ending in a dash returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
Raw CreateBucket with dash-dot in the name returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
Raw CreateBucket with dot-dash in the name returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
Raw CreateBucket with consecutive dots in the name returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
CreateBucket with a maximum-length 63-character DNS name succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
Raw CreateBucket with an underscore in the name returns InvalidBucketName
skipped: quirk vector skipped by default (run it with NoSkip)
CreateBucket with a 60-character name succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
CreateBucket with a 61-character name succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
CreateBucket with a 62-character name succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteBucket on a non-empty bucket returns BucketNotEmpty
DeleteBucket on a nonexistent bucket returns NoSuchBucket
HeadBucket on an existing bucket returns 200
HeadBucket on a nonexistent bucket returns 404
ListObjects on a nonexistent bucket returns NoSuchBucket
Recreating an existing bucket with a new ACL returns BucketAlreadyExists
skipped: ACL is not supported
Recreating a bucket created with public-read ACL returns BucketAlreadyExists
skipped: ACL is not supported
ListBuckets includes every created bucket
ListObjectsV2 on a nonexistent bucket returns NoSuchBucket
Put, get and delete bucket ownership controls; get after delete returns 404
skipped: PutBucketOwnershipControls are not supported
CreateBucket succeeds for a name containing a hyphen
CreateBucket succeeds for a name containing a period
CreateBucket succeeds for a name starting with a lowercase letter
CreateBucket succeeds for a name starting with a digit
Re-creating an existing bucket does not remove its objects
Re-creating a bucket you own succeeds here (200) — the AWS us-east-1 legacy behavior. Every other region returns BucketAlreadyOwnedByYou (see bucket-0038); the quirk:us-east-1-legacy tag lets non-us-east-1 targets skip this vector.
skipped: quirk vector skipped by default (run it with NoSkip)
Anonymous ListBuckets succeeds and returns an empty bucket list
skipped: quirk vector skipped by default (run it with NoSkip)
ListBuckets with an unknown access key is rejected with 403
Create and list a bucket with a 61-character name
The source builds a 61-character bucket name and verifies the bucket is usable (an object listing succeeds and is empty).
skipped: quirk vector skipped by default (run it with NoSkip)
Created bucket appears in ListBuckets and HeadBucket returns 404 after deletion
Bucket creation is covered by the prerequisite; the final not-in-list assertion of the source test is expressed as HeadBucket returning 404 after DeleteBucket.
Request payment configuration defaults to BucketOwner and can be set to Requester
skipped: Bucket request-payment configuration is not supported
Transfer acceleration status can be enabled, suspended and re-enabled
skipped: Bucket transfer acceleration is not supported
CreateBucket rejects invalid bucket names
Server-side bucket-naming rules: uppercase, underscores, leading/trailing hyphens, consecutive dots, dot-hyphen sequences, IP-address form, xn-- prefix, reserved -s3alias/--ol-s3 affixes and length limits (3-63). Original test accepted any rejection error; AWS returns InvalidBucketName. Name cases that common SDKs reject client-side (empty, space, slash, punctuation) were dropped, as were the tolerant valid-name and reserved-prefix branches that pass on either outcome; the duplicate-name check is covered by the CreateBucket-on-existing-bucket vector.
CreateBucket on an existing bucket you own returns BucketAlreadyOwnedByYou
Original test also accepted BucketAlreadyExists. Note that AWS us-east-1 has legacy behavior returning 200 for re-creating a bucket you own.
CreateBucket succeeds and the bucket is accessible via HeadBucket
Full bucket lifecycle: create, put and read an object, delete object, delete bucket
DeleteBucket removes the bucket
GetBucketLocation on a non-existent bucket returns NoSuchBucket
GetBucketLocation succeeds on an existing bucket
Original test only asserted the LocationConstraint key was present in the boto3 response shape (it is null for us-east-1), so this vector asserts the call succeeds.
ListBuckets returns a Buckets list
ListBuckets includes created buckets
Empty bucket lists zero keys and can be deleted
ListBuckets rejects a negative max-buckets value
Raw HTTP so the out-of-range value reaches the server rather than being caught client-side. AWS's valid MaxBuckets range is 1-10000 and a negative value is rejected with InvalidArgument. The original also probed an over-large value (2000000) but its primary branch accepted success (some implementations cap silently) and only tolerated rejection as an alternative; that undecidable success-or-error case is omitted.
ListBuckets MaxBuckets truncates the listing and ContinuationToken resumes it
Six runner-provisioned buckets guarantee the account holds more than MaxBuckets=3 buckets, so the first page must be truncated. The original tolerated implementations without pagination support.
ListBuckets with Prefix returns only buckets whose names match the prefix
Buckets are created in steps as suffixes of the runner-provisioned bucket name so the prefix is unique to this vector. The original tolerated implementations that ignore Prefix (MinIO); this vector asserts the AWS filtering behavior.
Only the bucket owner may set or remove bucket logging, even with a permissive bucket policy
The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format). The source-bucket policy is written exactly as in the source test (no Resource element).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging round-trips minimal, simple-prefix and partitioned-prefix configurations
Converted from ceph s3-tests test_put_bucket_logging. The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format). The TargetGrants portion of the source test is omitted for the same reason. The ceph-extension fields (ObjectRollTime, LoggingType, RecordsBatchSize) are omitted.
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging with a missing source or target bucket returns NoSuchBucket / NoSuchKey
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging targeting a bucket that itself has logging enabled returns InvalidArgument
The log-bucket policies use a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging with an invalid PartitionDateSource returns MalformedXML
The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging with the source bucket as its own target returns InvalidArgument
The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging targeting a bucket with default encryption returns InvalidArgument
The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging targeting a requester-pays bucket returns InvalidArgument
The log-bucket policy uses a wildcard aws:SourceAccount (the source test uses the main user id, which is not expressible in the vector format).
skipped: PutBucketPolicy and PutBucketLogging are not supported
PutBucketLogging is denied unless the target bucket policy grants the logging service principal correctly
Each stage sets a log-bucket policy with exactly one invalid element (principal, action, resource, source ARN, or source account) and asserts PutBucketLogging returns AccessDenied. Where the source test uses the main user id for a valid aws:SourceAccount, a wildcard StringLike condition is used instead (the main account id is not expressible in the vector format); the tenant-qualified invalid-account variant is omitted for the same reason.
skipped: PutBucketPolicy and PutBucketLogging are not supported
Wildcard source account and source-bucket ARN in the log bucket policy allow logging from any source
A second identity creates its own source bucket. With per-bucket statements both owners can enable logging; after removing the second statement the alt owner is denied; with wildcard aws:SourceAccount and aws:SourceArn both succeed again. The main-account statement uses a wildcard aws:SourceAccount throughout (the main account id is not expressible in the vector format); the alt statement uses the alt credential's canonical id.
skipped: PutBucketPolicy and PutBucketLogging are not supported
Disable bucket logging with an empty BucketLoggingStatus
Grant the S3 logging service PutObject on the target bucket via bucket policy, enable logging on the source bucket, then disable it by putting an empty BucketLoggingStatus; GetBucketLogging must succeed and no longer return LoggingEnabled. The source test's policy also carries an aws:SourceAccount condition on the caller's account id, which is not expressible in the vector format and is omitted here.
skipped: PutBucketPolicy and PutBucketLogging are not supported
GetObjectAttributes on a SHA256-checksummed single-part object reports size, ETag, storage class and checksum with no ObjectParts
GetObjectAttributes on a SHA256-checksummed 13-part multipart object reports size, composite checksum and per-part checksums
PutObject with CRC64NVME checksum: echoed on put, hidden on plain HEAD, shown with ChecksumMode, bad digest rejected
PutObject with SHA256 checksum: echoed on put, hidden on plain HEAD, shown with ChecksumMode, bad digest rejected
Object uploaded with ChecksumAlgorithm SHA256 is retrievable with intact body
HeadObject with ChecksumMode=ENABLED returns the CRC32 checksum from PutObject
HeadObject with ChecksumMode=ENABLED returns the SHA1 checksum from PutObject
HeadObject with ChecksumMode=ENABLED returns the SHA256 checksum from PutObject
PutObject with an incorrect ChecksumSHA256 returns BadDigest
The supplied checksum is a valid base64 32-byte value that does not match the body. The original test accepts several codes (InvalidRequest, BadDigest, XAmzContentSHA256Mismatch, XAmzContentChecksumMismatch); AWS returns BadDigest.
PutObject with a correct ChecksumCRC32 succeeds and echoes the checksum
PutObject with a correct ChecksumSHA256 succeeds and echoes the checksum
UploadPart with ChecksumAlgorithm CRC32 but a CRC32C checksum header is rejected
The multipart upload and the part both declare CRC32, but the part carries an x-amz-checksum-crc32c value instead. The source test accepted InvalidRequest or InvalidArgument; both are HTTP 400.
UploadPart declaring a checksum algorithm different from the multipart upload's is rejected
The multipart upload was initialized with CRC32 but the part declares ChecksumAlgorithm SHA1. The source test accepted InvalidRequest or InvalidArgument; both are HTTP 400.
UploadPart with a checksum value of a different algorithm than the upload's is rejected
The multipart upload was initialized with CRC32 but the part carries an x-amz-checksum-sha256 value. The source test accepted InvalidRequest or InvalidArgument; both are HTTP 400.
UploadPartCopy succeeds when source and multipart upload use different checksum algorithms
The source object carries a SHA1 checksum while the multipart upload declares SHA256; the copy must succeed and return a CopyPartResult with an ETag. The source test skipped on InvalidArgument/NotImplemented (MinIO tolerance); on AWS the copy succeeds.
UploadPartCopy from a CRC32-checksummed source into a CRC32 multipart upload succeeds
Source object and multipart upload use the same checksum algorithm; the copy must succeed and return a CopyPartResult with an ETag. The source test skipped on NotImplemented/InvalidArgument (MinIO tolerance); on AWS the copy succeeds.
UploadPartCopy into a checksum-less multipart upload returns no checksums in CopyPartResult
The source object was uploaded with a SHA1 checksum but the multipart upload declares no checksum algorithm, so the CopyPartResult must not contain any checksum members.
UploadPart with two different checksum headers is rejected
The part carries both an x-amz-checksum-sha1 and an x-amz-checksum-crc32c value. The source test accepted InvalidRequest or InvalidArgument; both are HTTP 400.
UploadPart with ChecksumAlgorithm CRC32 succeeds and returns an ETag
UploadPart with ChecksumAlgorithm SHA1 succeeds and returns an ETag
UploadPart with ChecksumAlgorithm SHA256 succeeds and returns an ETag
GetObjectAttributes checksum matches PutObject checksum (CRC32)
One of four per-algorithm cases from the source loop.
GetObjectAttributes checksum matches PutObject checksum (CRC32C)
One of four per-algorithm cases from the source loop.
GetObjectAttributes checksum matches PutObject checksum (SHA1)
One of four per-algorithm cases from the source loop.
ListObjectsV2 lists all objects uploaded with checksum algorithms
Uploads four objects with CRC32/CRC32C/SHA1/SHA256 checksum algorithms and lists the bucket. The source test's per-entry ChecksumAlgorithm shape check was conditional on the field being present and is not asserted here.
PutObject of an empty body with ChecksumAlgorithm CRC32 returns a CRC32 checksum
One of four per-algorithm cases from the source loop; this iteration's body size is 200 * 0 = 0 bytes.
PutObject with ChecksumAlgorithm CRC32C returns a CRC32C checksum
One of four per-algorithm cases from the source loop.
PutObject with ChecksumAlgorithm SHA1 returns a SHA1 checksum
One of four per-algorithm cases from the source loop.
PutObject with ChecksumAlgorithm SHA256 returns a SHA256 checksum (600-byte body)
One of four per-algorithm cases from the source loop.
PutObject with ChecksumAlgorithm CRC32 returns a CRC32 checksum
The source test also asserted ChecksumType == FULL_OBJECT only when the field was present; that conditional check is omitted.
PutObject with an incorrect client-provided CRC32 checksum is rejected
AWS returns 400 BadDigest for an x-amz-checksum-crc32 mismatch. The source test also accepted InvalidRequest, InvalidDigest and XAmzContentChecksumMismatch from other implementations.
PutObject with a correct client-provided CRC32 checksum succeeds and echoes it
Multipart upload with CRC32 part checksums completes successfully
Multipart upload with SHA256 part checksums completes successfully
CopyObject across buckets preserves the source SHA256 checksum
CopyObject with MetadataDirective=COPY preserves the source checksum
CopyObject with ChecksumAlgorithm=CRC32 computes a CRC32 checksum for the destination
CopyObject with ChecksumAlgorithm=SHA256 computes a checksum for the destination
Source object has no checksum; copying with ChecksumAlgorithm=SHA256 returns the computed SHA256 in CopyObjectResult.
CopyObject with ChecksumAlgorithm=SHA1 computes a SHA1 checksum
CopyObject with ChecksumAlgorithm=SHA256 computes a SHA256 checksum
CopyObject preserves the source object's existing SHA256 checksum
CopyObject with ChecksumAlgorithm=SHA256 replaces the source's CRC32 checksum
CopyObject onto itself with MetadataDirective=REPLACE adds a SHA256 checksum
CreateMultipartUpload with ChecksumAlgorithm CRC32 echoes the algorithm
DeleteObject with If-Match ETag precondition against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with last-modified-time precondition against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with size precondition against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with If-Match on a nonexistent key is idempotent
RGW ignores an If-Match precondition when the key does not exist and returns 204; AWS evaluates the precondition and returns NoSuchKey.
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with last-modified-time precondition on an unversioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with size precondition on an unversioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with If-Match ETag precondition on a specific version
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with last-modified-time precondition on a specific version
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObject with size precondition on a specific version
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key ETag precondition against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key last-modified-time precondition against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with a per-key ETag on a nonexistent key succeeds
RGW ignores a per-key ETag precondition when the key does not exist and reports it Deleted; AWS returns a NoSuchKey error entry.
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key last-modified-time precondition on an unversioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key size precondition on an unversioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key ETag precondition on a specific version
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects with per-key last-modified-time precondition on a specific noncurrent version
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject If-Match is evaluated against the current version on a versioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject If-None-Match: * is evaluated against the current version on a versioned bucket
PutObject preconditions treat a delete marker as a nonexistent current object
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject If-Match/If-None-Match precondition matrix on an unversioned bucket
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with non-matching If-Match ETag fails with PreconditionFailed and leaves object intact
PutObject with If-Match: * on a nonexistent key fails with NoSuchKey and creates nothing
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with If-Match: * overwrites an existing object
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with matching If-None-Match ETag fails with PreconditionFailed and leaves object intact
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with non-matching If-None-Match ETag overwrites the object
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with If-None-Match: * creates a nonexistent key
PutObject with If-None-Match: * on an existing key fails with PreconditionFailed and leaves object intact
DeleteObjects Size precondition on current version: mismatch errors, match creates delete marker, then delete marker blocks further match
skipped: quirk vector skipped by default (run it with NoSkip)
DeleteObjects by VersionId with Size precondition: mismatch errors, match deletes the version, repeat on gone version returns 200
skipped: quirk vector skipped by default (run it with NoSkip)
Conditional DeleteObject with matching IfMatch ETag succeeds with 204
Conditional DeleteObject with a wrong IfMatch ETag returns PreconditionFailed
Original tolerated implementations that ignore IfMatch on DeleteObject (MinIO) or return InvalidArgument; AWS enforces the precondition.
PutObject with a non-matching If-Match returns 412 and leaves the object unchanged
GetObject with If-Unmodified-Since in the past returns 412 PreconditionFailed
The source used utcnow() minus one day; a fixed date guaranteed to precede any object creation preserves the semantics.
GetObject with non-matching If-Match ETag returns PreconditionFailed
GetObject with matching If-Match ETag returns the object
PutObject with If-None-Match on a non-existent key succeeds
The original test also sends If-Match on a non-existent key but accepts either success (AWS) or PreconditionFailed/NoSuchKey (MinIO); that either-or expectation is not expressible and is omitted here.
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with matching If-Match plus matching If-None-Match returns PreconditionFailed
When both conditional headers are sent and both match the current ETag, the If-None-Match condition fails and the write is rejected.
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with non-matching If-Match returns PreconditionFailed
PutObject with If-Match matching the current ETag succeeds and returns a new ETag
PutObject with If-None-Match matching the current ETag returns PreconditionFailed
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with non-matching If-None-Match on an existing object succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
CompleteMultipartUpload with non-matching If-Match returns PreconditionFailed
CompleteMultipartUpload with If-Match matching the existing object's ETag succeeds
CopyObject with a non-matching CopySourceIfMatch returns 412 PreconditionFailed
CopyObject with a matching CopySourceIfMatch copies the object
CopyObject with a past CopySourceIfModifiedSince copies the object
The source used now-1h; a fixed past date guaranteed to precede the object's LastModified preserves the semantics.
CopyObject with a matching CopySourceIfNoneMatch returns 412 PreconditionFailed
CopyObject with a past CopySourceIfUnmodifiedSince returns 412 PreconditionFailed
The source used now-1h; a fixed past date guaranteed to precede the object's LastModified preserves the semantics.
GetObject with a future If-Modified-Since is ignored and returns the object
The source used now+1h; a fixed far-future date guaranteed to postdate the object's LastModified preserves the semantics.
GetObject with a matching If-None-Match returns 304 Not Modified
The source accepted error code NotModified or the bare '304'.
HeadObject with a non-matching If-Match returns 412 PreconditionFailed
HeadObject with a matching If-Match returns the object metadata
HeadObject with a future If-Modified-Since is ignored and returns the object
The source used now+1h; a fixed far-future date guaranteed to postdate the object's LastModified preserves the semantics.
HeadObject with a matching If-None-Match returns 304 Not Modified
The source accepted error code NotModified or the bare '304'.
HeadObject with a future If-Unmodified-Since succeeds
The source used now+1h; a fixed far-future date guaranteed to postdate the object's LastModified preserves the semantics.
Conditional DeleteObject with If-Match rejects stale ETag, succeeds on match
DeleteObject with If-Match: * deletes an existing object
DeleteObjects with a per-key If-Match ETag precondition on an unversioned bucket
CopyObject with non-matching x-amz-copy-source-if-match returns PreconditionFailed
CopyObject with matching x-amz-copy-source-if-match succeeds
CopyObject with non-matching x-amz-copy-source-if-none-match succeeds
CopyObject with matching x-amz-copy-source-if-none-match returns PreconditionFailed
Copy a 16 MiB object within a bucket
Copy from a non-existent source bucket returns 404
CopyObject applies a canned public-read ACL to the destination
skipped: ACL is not supported
Copy an object to a different bucket
Copy from another user's private object into own bucket is denied
Grantee with FULL_CONTROL on object and bucket can copy within the owner's bucket
skipped: ACL is not supported
Copy with REPLACE directive overwrites metadata and content type (3-byte object)
Copy retains metadata and content type (3-byte object)
Copy an object onto itself without changes returns InvalidRequest
Copy an object onto itself with the REPLACE metadata directive
Copy preserves the source object's Content-Type
Copy specific versions across versioned and non-versioned buckets
Copy a specific version of an object whose key needs URL encoding
Copy versions of a multipart object across versioned and non-versioned buckets, retaining body, metadata and content type
Copy a zero-length object
Checksums are preserved across overwrite and server-side copy
Reduced to the firm assertions of the source test: STANDARD upload ETag equals MD5, overwrite keeps content identical, and a server-side copy has identical content. Vendor-tolerant storage-class transition loops are omitted.
CopyObject to a new key within the same bucket succeeds
CopySource with a leading slash is accepted
CopyObject onto itself with an invalid MetadataDirective is rejected
Original test accepts InvalidArgument or InvalidRequest; both are HTTP 400, so only the status is asserted.
CopySource without a bucket/key slash separator is rejected
Original test accepts InvalidArgument, InvalidRequest, NoSuchKey or NoSuchBucket; AWS returns 400 InvalidArgument for a copy source lacking a slash, which is asserted here.
CopyObject with an invalid TaggingDirective is rejected
Original test accepts InvalidArgument or InvalidRequest; both are HTTP 400, so only the status is asserted.
CopyObject with MetadataDirective=COPY preserves metadata and Content-Type
CopyObject with MetadataDirective=REPLACE applies new metadata and Content-Type
CopyObject of a non-existent directory-style key returns NoSuchKey
CopyObject with MetadataDirective=REPLACE updates content headers
CopyObject with spaces in the source key
CopyObject with TaggingDirective=COPY preserves source tags
CopyObject with TaggingDirective=REPLACE applies new tags
CopyObject with a malformed x-amz-copy-source (no bucket/key separator) is rejected
The original test accepted InvalidArgument or InvalidRequest; AWS returns InvalidArgument for a copy source without a bucket/key separator. Expressed as a raw-HTTP step so the malformed header bypasses SDK client-side validation.
CopyObject to a non-existing destination bucket returns NoSuchBucket
CopyObject with a non-existing source key returns NoSuchKey
CopyObject preserves the source ContentType by default
CopyObject response includes a CopyObjectResult and the copy has an ETag
CopyObject with default tagging directive copies the source tags
CopyObject with TaggingDirective REPLACE replaces the source tags
CopyObject between buckets preserves ContentType and custom metadata
CopyObject preserves all content headers and custom metadata by default
The original test set Expires to a dynamic now+10h timestamp; a fixed future HTTP-date is used instead (Expires is not asserted here).
CopyObject onto itself with REPLACE sets the new metadata map
CopyObject preserves CacheControl and Expires headers
The original test set Expires to a dynamic now+24h timestamp; a fixed future HTTP-date is used instead, and only the presence of Expires is asserted (as in the original).
CopyObject preserves the CRC32 checksum of the source object
CopyObject tagging directives: default copies tags, REPLACE substitutes them
CopyObject with CopySourceIfMatch: matching ETag succeeds, wrong ETag fails
Original test accepted any error for the wrong-ETag copy (bare except); the canonical S3 behavior of 412 PreconditionFailed is asserted here.
CopyObject of a 6 MiB object preserves its size
Prefix rename via CopyObject + DeleteObject leaves old prefix empty and content intact
Directory rename emulated as copy-to-new-prefix then delete-from-old. Object count reduced from the source's 1000 to a representative sample.
CORS preflight requesting a non-allowed header is rejected
A rule allows origin '*' for GET and exposes x-amz-meta-header1 but declares no AllowedHeaders; an OPTIONS preflight asking for x-amz-meta-header2 returns 403 with no CORS headers. The source test sleeps 3s after PutBucketCors for propagation; the sleep is omitted here.
skipped: PutBucketAcl is not supported
CORS origin wildcard matching on unauthenticated GET, PUT and OPTIONS requests
With rules for origins '*suffix', 'start*end', 'prefix*' (GET) and '*.put' (PUT) on a public-read bucket, checks access-control-allow-origin/access-control-allow-methods on plain requests and OPTIONS preflights for matching and non-matching origins. The source test sleeps 3s after PutBucketCors for propagation; the sleep is omitted here.
skipped: Anonymous access is not supported
CORS wildcard '*' origin echoes '*' in access-control-allow-origin
With a single GET rule allowing origin '*' on a public-read bucket, a request without an Origin header gets no CORS headers and a request with any Origin gets access-control-allow-origin '*'. The source test sleeps 3s after PutBucketCors for propagation; the sleep is omitted here.
skipped: Anonymous access is not supported
Put, get and delete bucket CORS configuration
GetBucketCors returns 404 before any configuration is set, the stored rules round-trip exactly, and GetBucketCors returns 404 again after DeleteBucketCors.
skipped: PutBucketCors is not supported
CORS configuration: put, replace with multiple rules, update, and delete returns NoSuchCORSConfiguration
skipped: PutBucketCors is not supported
CORS rule limit: 100 rules accepted, 101 rules rejected
The original test accepted either TooManyRules or InvalidRequest for the 101-rule case; only the shared 400 status is asserted here.
skipped: PutBucketCors is not supported
PutBucketCors rejects unsupported HTTP methods in AllowedMethods
The original test accepted either InvalidArgument or MalformedXML; only the shared 400 status is asserted here.
MaxAgeSeconds: negative rejected, zero and large values accepted
The original test accepted any error for the negative case; a 400 rejection is asserted here.
skipped: PutBucketCors is not supported
DeleteBucketCors removes the configuration; GetBucketCors then returns NoSuchCORSConfiguration
skipped: PutBucketCors is not supported
DeleteBucketCors on a non-existent bucket returns NoSuchBucket
DeleteBucketCors succeeds on unset configuration and removes a set configuration
skipped: DeleteBucketCors is not supported
GetBucketCors on a bucket without CORS configuration returns NoSuchCORSConfiguration
GetBucketCors on a non-existent bucket returns NoSuchBucket
PutBucketCors then GetBucketCors round-trips the rules
Original test compared the list members as sets; this vector asserts them in the order they were put, which is the order S3 returns them.
skipped: PutBucketCors is not supported
PutBucketCors with an empty CORSRules array returns MalformedXML
Original test also accepted InvalidRequest as the error code.
PutBucketCors rejects invalid header names in AllowedHeaders
Header names containing spaces, colons, parentheses, slashes, brackets, equals or quotes are invalid. Original test also accepted InvalidArgument or AccessControlAllowRequestHeaderNotAllowed as the error code.
PutBucketCors rejects invalid header names in ExposeHeaders
Header names containing spaces, colons, parentheses, slashes, brackets, equals or quotes are invalid. Original test also accepted InvalidArgument or UnexpectedContent as the error code.
PutBucketCors rejects invalid HTTP methods in AllowedMethods
Only uppercase GET, PUT, POST, DELETE, HEAD are valid CORS methods. Original test also accepted CORSInvalidAccessControlMethod or InvalidArgument as the error code.
PutBucketCors with malformed Content-MD5 returns InvalidDigest and wrong Content-MD5 returns BadDigest
Original test accepted InvalidDigest or InvalidRequest for the malformed value, and BadDigest or InvalidDigest for the well-formed-but-wrong value.
PutBucketCors on a non-existent bucket returns NoSuchBucket
PutBucketCors accepts multiple rules with wildcard origins and negative MaxAgeSeconds
skipped: PutBucketCors is not supported
Keys with special characters list correctly, read back their bodies and accept ACLs
skipped: ACL is not supported
Keys containing URL-encoding-like characters round-trip literally
Keys that look percent-encoded or contain '+', '?' and '#' must be stored and retrieved literally, not decoded.
Path-traversal-like keys and Windows reserved names round-trip
Keys containing ../ or ..\ sequences must be stored literally (no path normalization); Windows reserved device names are ordinary keys.
Keys containing CR/LF and escape control characters round-trip
The original also tried a key containing a NUL (0x00) byte, accepting either success or InvalidArgument rejection; that case is omitted because the accept-or-reject disjunction is not expressible.
Slash-only keys and a 1024-byte maximum-length key round-trip
skipped: Known deviation: slash-only object keys route to the bucket handler: fiber trims trailing slashes when matching, so PUT /bucket// resolves to CreateBucket
Key longer than 1024 bytes is rejected
The original accepted KeyTooLong* or InvalidRequest; AWS returns KeyTooLongError.
Unicode object key round-trip
Object key containing '+' is listable by literal '+' prefix and HEADable
The source test branches on an SDK capability flag for legacy '+'-as-space URL encoding; the correct S3 behavior (default branch) is that '+' in a key is a literal plus sign.
Real-world object keys mixing paths, dates, '@', parentheses, colons, '#' and brackets round-trip
Listing order assertion relies on S3's guaranteed ascending UTF-8 binary key order.
Path-like object keys (nested, leading/trailing/double slashes, directory markers) list correctly
Listing order assertion relies on S3's guaranteed ascending UTF-8 binary key order.
Object keys with 24 common special characters can be created, listed and read back
Listing order assertion relies on S3's guaranteed ascending UTF-8 binary key order.
Object keys with non-ASCII Unicode characters (CJK, accents, Cyrillic, emoji) list correctly
Listing order assertion relies on S3's guaranteed ascending UTF-8 binary key order.
Object keys requiring URL encoding round-trip through put, get and list
Keys with URL-special ASCII characters round-trip through PutObject/GetObject
Space, plus, percent, question mark, hash, ampersand, equals and at-sign in object keys. The original accepted InvalidArgument rejection as an alternative outcome for lenient vendors; AWS S3 accepts all of these keys, so the vector asserts the round-trip.
Path-separator variations in keys are stored as-is and listable by prefix
Double/triple slashes, leading slash, dot and dot-dot segments must not be normalized: each key appears verbatim in a prefix listing (prefixes are the first five characters of the key, as in the original test).
A raw space and a percent-encoded space are distinct object keys
'test space.txt' and 'test%20space.txt' are two different objects. The original also tolerated normalization to a single form; AWS S3 stores both distinctly, so the listing asserts exactly the two keys.
UTF-8 international keys round-trip through PutObject/GetObject
Accented Latin, Cyrillic, Chinese, Japanese and Korean keys. The original accepted InvalidArgument rejection as an alternative outcome; AWS S3 accepts any UTF-8 key, so the vector asserts the round-trip.
Spaces and percent sequences in directory and file parts are accepted
PutObject with reserved characters at different path positions, then ListObjectsV2 with the directory part as Prefix and '/' Delimiter. The original recorded the listing outcome without failing on absence, so the converted steps assert only that both operations succeed.
Query-string-like characters in keys are accepted and retrievable
Keys containing '?', '&', '=' and '#' sequences that resemble query strings or fragments must not be interpreted as such. The original accepted InvalidArgument rejection as an alternative outcome; AWS S3 accepts these keys.
Object keys are case-sensitive
GetObject with the case-swapped form of each stored key must fail. The original accepted any error; AWS S3 returns 404 NoSuchKey since the case-swapped key does not exist.
An 819-character key round-trips
A long key approaching URL length limits (within the 1024-byte S3 key limit). The original accepted KeyTooLong/RequestURITooLong as an alternative outcome; AWS S3 accepts keys up to 1024 bytes, so the vector asserts the round-trip.
CreateMultipartUpload preserves keys with non-ASCII (Greek, Chinese) characters
DeleteBucketLifecycle returns 204 with or without a configuration and removes it
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle expiration rule with Days of 0 is rejected with InvalidArgument
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
No x-amz-expiration header when object tags do not match an And tag-filtered rule
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
HeadObject response carries x-amz-expiration header for a matching lifecycle rule
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutObject response carries x-amz-expiration header for a matching lifecycle rule
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
x-amz-expiration header follows tag-filtered lifecycle rule matches on HeadObject
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
GetBucketLifecycleConfiguration returns the rules that were put
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rules stored without an ID are returned with a generated ID
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule ID longer than 255 characters is rejected with InvalidArgument
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with invalid Status casing or value is rejected with MalformedXML
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle configuration with duplicate rule IDs is rejected with InvalidArgument
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutBucketLifecycleConfiguration with two prefix rules succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle expiration rule with an absolute Date succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutBucketLifecycleConfiguration with ExpiredObjectDeleteMarker rule succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutBucketLifecycleConfiguration with an empty Filter succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutBucketLifecycleConfiguration with a Filter Prefix succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle expiration Date not in ISO 8601 extended format is rejected
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
PutBucketLifecycleConfiguration with NoncurrentVersionExpiration rules succeeds
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Set lifecycle configuration with AbortIncompleteMultipartUpload rules
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Set lifecycle transition with a malformed date is rejected with 400
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle configuration: put, replace with multiple rules, disabled rules, and delete
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Basic lifecycle configuration with prefix filter, expiration and transition round-trips
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with multiple correctly ordered storage-class transitions is accepted
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with out-of-order transition days is rejected
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with And filter combining prefix and tags is accepted
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with AbortIncompleteMultipartUpload is accepted
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle rule with date-based expiration is accepted
The source computed now+30 days; a fixed far-future midnight-UTC date preserves the assertion (a Date expiration is accepted) without time dependence.
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
Lifecycle configuration with 100 rules is accepted
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
DeleteBucketLifecycle removes configuration; subsequent get returns NoSuchLifecycleConfiguration
skipped: GetBucketLifecycleConfiguration and PutBucketLifecycleConfiguration are not supported
ListObjectsV2 KeyCount matches the number of keys in the bucket
ListObjects with letter delimiter groups keys into common prefixes
ListObjects with slash delimiter rolls keys up into common prefixes
ListObjects with dot delimiter groups keys into common prefixes
ListObjects with empty delimiter behaves as no delimiter and echoes none
ListObjects without a delimiter returns all keys and echoes no delimiter
ListObjects with percent delimiter groups keys into common prefixes
ListObjects delimiter and prefix pagination with Marker and NextMarker
ListObjects with prefix ending in the delimiter returns the exact-match key
ListObjects with newline delimiter matches no keys and returns all objects
ListObjects with whitespace delimiter groups keys into common prefixes
Objects in one bucket do not appear when listing a different bucket
ListObjects with url encoding-type URL-encodes keys and common prefixes
ListObjects paginates with MaxKeys and Marker
ListObjectsV2 with letter delimiter groups keys into common prefixes
ListObjectsV2 with slash delimiter rolls keys up into common prefixes and counts them in KeyCount
ListObjectsV2 with dot delimiter groups keys into common prefixes
ListObjectsV2 with empty delimiter behaves as no delimiter and echoes none
ListObjectsV2 without a delimiter returns all keys and echoes no delimiter
ListObjectsV2 with percent delimiter groups keys into common prefixes
ListObjectsV2 delimiter and prefix pagination with continuation tokens
ListObjectsV2 with prefix ending in the delimiter returns the exact-match key
ListObjectsV2 with newline delimiter matches no keys and returns all objects
ListObjectsV2 with whitespace delimiter groups keys into common prefixes
ListObjectsV2 with url encoding-type URL-encodes keys and common prefixes
ListObjectsV2 with FetchOwner=true includes Owner in listed objects
ListObjectsV2 paginates with MaxKeys and StartAfter
ListObjects with a delimiter matching no keys returns all keys and no common prefixes
ListObjects with MaxKeys=1 truncates after the first key and Marker resumes the listing
ListObjects with MaxKeys=0 returns no keys and is not truncated
ListObjects prefix filtering works on non-slash prefixes
ListObjects with a directory prefix returns only keys under that prefix
ListObjects prefix and delimiter logic works with non-slash delimiter
ListObjects with prefix and delimiter returns direct keys and rolls up deeper keys
ListObjects with a matching prefix but non-matching delimiter returns all prefixed keys uncollapsed
ListObjects with non-matching prefix and non-matching delimiter returns an empty listing
ListObjects with an empty prefix returns all keys
ListObjects with a prefix matching no keys returns an empty listing
ListObjects with an unprintable (newline) prefix returns an empty listing and echoes the prefix
ListObjectsV2 with a delimiter matching no keys returns all keys and no common prefixes
ListObjectsV2 without FetchOwner omits Owner from listed objects
ListObjectsV2 with FetchOwner=false omits Owner from listed objects
ListObjectsV2 with MaxKeys=1 truncates after the first key and StartAfter resumes the listing
ListObjectsV2 prefix filtering works on non-slash prefixes
ListObjectsV2 with a directory prefix returns only keys under that prefix
ListObjectsV2 prefix and delimiter logic works with non-slash delimiter
ListObjectsV2 with prefix and delimiter returns direct keys and rolls up deeper keys
ListObjectsV2 with a matching prefix but non-matching delimiter returns all prefixed keys uncollapsed
ListObjectsV2 with non-matching prefix and non-matching delimiter returns an empty listing
ListObjectsV2 with an empty prefix returns all keys
ListObjectsV2 with a prefix matching no keys returns an empty listing
ListObjectsV2 with an unprintable (newline) prefix returns an empty listing and echoes the prefix
ListObjects marker past the last key returns no keys
ListObjects with an empty marker returns all keys
ListObjects without a marker echoes an empty Marker
ListObjects marker between keys starts listing after its sort position
ListObjects with an unprintable marker echoes it and returns all keys
ListObjects with non-numeric max-keys returns InvalidArgument
ListObjects default MaxKeys is 1000 and returns all keys
Anonymous ListObjects succeeds on a public-read bucket
skipped: ACL is not supported
Anonymous ListObjects on a private bucket returns AccessDenied
skipped: Anonymous access is not supported
ListObjects entries carry the per-object ETag, Size and Owner
The source test also compares LastModified (truncated to whole seconds) between HeadObject and the listing; that sub-second-tolerant comparison is not expressible and is omitted.
skipped: GetObjectAcl is not supported
ListObjectVersions entries carry the per-object ETag, Size, Owner and VersionId
The source test also compares LastModified (truncated to whole seconds) between HeadObject and the listing; that sub-second-tolerant comparison is not expressible and is omitted.
skipped: GetObjectAcl is not supported
ListObjects with an underscore-prefixed key prefix returns the matching keys
ListObjectsV2 with both ContinuationToken and StartAfter resumes from the token
ListObjectsV2 resumes listing from NextContinuationToken
ListObjectsV2 with an empty ContinuationToken returns InvalidArgument
AWS rejects an empty continuation token with 400 InvalidArgument (verified against AWS S3); an empty token is not treated as "no token".
ListObjectsV2 default MaxKeys is 1000 and returns all keys
ListObjectsV2 with MaxKeys=0 returns no keys and is not truncated
Anonymous ListObjectsV2 succeeds on a public-read bucket
skipped: ACL is not supported
Anonymous ListObjectsV2 on a private bucket returns AccessDenied
skipped: Anonymous access is not supported
ListObjectsV2 StartAfter past the last key returns no keys
ListObjectsV2 StartAfter between keys starts listing after its sort position
ListObjectsV2 with an unprintable StartAfter echoes it and returns all keys
ListObjectsV2 with a prefix returns all 10 seeded objects
ListObjectsV2 accepts an empty Prefix and an empty Delimiter
ListObjects (v1) on an empty bucket returns no contents and is not truncated
The original test accepts an absent or empty Contents list; absence (AWS behavior) is asserted here.
ListObjects (v1) with negative MaxKeys is rejected
The original test only asserts that some error is raised for MaxKeys=-1; AWS returns 400 InvalidArgument, but only the 400 status is asserted here.
ListObjects (v1) Marker need not be an existing key
ListObjects (v1) with MaxKeys=0 returns no objects and is not truncated
The original test accepts an absent or empty Contents list; absence (AWS behavior) is asserted here.
ListObjects (v1) Prefix on a nested hierarchy returns only keys under the prefix
ListObjects (v1) on a non-existing bucket returns NoSuchBucket
ListObjects (v1) sets IsTruncated when MaxKeys is smaller than the object count
ListObjects (v1) with Prefix returns only matching keys and echoes the prefix
ListObjectsV2 returns empty results for an empty bucket and a non-matching prefix
The original test accepts an absent or empty Contents list; absence (AWS behavior) is asserted here. The object is created mid-test, so PutObject is a step.
ListObjectsV2 with negative MaxKeys is rejected
The original test accepts error code InvalidArgument or InvalidRequest; code alternation is not expressible, so only the 400 status is asserted.
ListObjectsV2 accepts MaxKeys above the 1000 cap
MaxKeys=2000 must succeed; with 10 objects in the bucket all 10 are returned (the original test asserted at most 1000).
ListObjectsV2 with MaxKeys=0 returns no objects
The original test accepts an absent or empty Contents list; absence (AWS behavior) is asserted here.
ListObjectsV2 with Delimiter navigates a nested hierarchy level by level
ListObjectsV2 paginates 25 objects in three pages via NextContinuationToken
ListObjectsV2 StartAfter beyond all keys returns an empty result
The original test accepts an absent or empty Contents list; absence (AWS behavior) is asserted here.
ListObjectsV2 StartAfter need not be an existing key
ListObjectsV2 truncates when common prefixes exceed MaxKeys
15 single-object folders listed with Delimiter and MaxKeys=10: response is truncated and common prefixes are capped at MaxKeys (the original test asserted <= 10; the deterministic count is 10).
ListObjectsV2 with FetchOwner returns the single object listing
The original test only checks Owner sub-fields conditionally when Owner is present, so only the Contents count is asserted.
ListObjectsV2 Prefix filter returns only keys under each prefix
ListObjectsV2 StartAfter returns only keys strictly after the given key
ListObjectsV2 MaxKeys limits the page and sets IsTruncated
ListObjectsV2 continuation tokens page through all 15 objects in 5 pages
Original test looped with ContinuationToken until IsTruncated was false and checked all 15 keys were returned; unrolled here as 5 pages of 3.
ListObjectsV2 with delimiter rolls directories into CommonPrefixes
Original test asserted at least the 3 expected directory prefixes; AWS returns exactly dir1/, dir2/ and special/ sorted.
ListObjectsV2 with Prefix returns only matching keys
ListObjectsV2 with Prefix and Delimiter returns subdirectory CommonPrefixes
Original test asserted dir1/subdir1/ and dir1/subdir2/ both appear in CommonPrefixes.
ListObjectsV2 StartAfter skips keys up to and including the marker
Original test asserted dir1/file1.txt is excluded and dir1/file2.txt is included; KeyCount 13 is the 13 keys sorting after the marker, which implies the exclusion.
ListObjectsV2 on an empty bucket returns KeyCount 0 and no Contents
ListObjectsV2 with a prefix lists keys containing special characters
ListObjects (v1) and ListObjectsV2 return the same count for MaxKeys 5
ListObjectsV2 with FetchOwner includes Owner in Contents entries
100 small objects in one bucket are all reported by ListObjectsV2
Source asserted KeyCount >= 100; the bucket contains exactly 100 objects so KeyCount 100 is the equivalent assertion.
ListObjectsV2 pagination returns every key exactly once in order
Listing with a small MaxKeys and following ContinuationToken returns all keys across pages with no duplicates or omissions, in lexical order.
Deleted objects do not appear in ListObjectsV2 (no phantoms)
Read-after-delete for listings: after deleting every key under a prefix an immediate list is empty. Object count reduced from the source's 50 to a representative sample.
Objects are immediately visible in ListObjectsV2 after PUT
Read-after-write for listings: keys PUT under a prefix appear in an immediate list. Object count reduced from the source's 100 to a representative sample; no pagination boundary is crossed.
ListObjectsV2 with delimiter groups top-level prefixes; full list returns every object
Lists a multi-level hierarchy with Delimiter="/" (CommonPrefixes) and without a delimiter (all keys). Object count reduced from the source's 1000 to a representative sample.
Requests with a wrong ExpectedBucketOwner are denied even on a public bucket
The source test uses the main user's id with 'foo' appended as the incorrect owner; the main account id is not referenceable in vectors, so a fixed wrong account id is used instead.
skipped: ACL is not supported
GetObject with PartNumber on a non-multipart object: part 2 fails, part 1 returns the whole object
GetObject with PartNumber on a non-multipart SSE-C object: part 2 fails, part 1 returns the whole object
skipped: Server Side Encryption is not supported
GetObjectAttributes on a 6-part multipart object reports size, parts and composite ETag
GetObjectAttributes paginates the multipart part list with MaxParts and PartNumberMarker
GetObjectAttributes on a single-part multipart object lists one part
GetObjectAttributes returns VersionId and honors an explicit VersionId on a versioned object
Website configuration: put with index/error documents, update, and minimal index-only form
skipped: PutBucketWebsite is not supported
Inventory configuration: create daily CSV report config, verify, and update in place
skipped: PutBucketInventoryConfiguration is not supported
Metrics configuration: create, add prefix-filtered config, update filter, and delete
skipped: PutBucketMetricsConfiguration is not supported
Analytics configuration: create storage-class analysis configs, verify, update, and delete
skipped: PutBucketAnalyticsConfiguration is not supported
Aborting a multipart upload leaves no object
AbortMultipartUpload with an unknown UploadId returns NoSuchUpload
An in-progress multipart upload does not replace the existing object until completed
ListMultipartUploads reports all in-progress uploads
ListMultipartUploads reports each upload's initiator and owner identity
skipped: ACL is not supported
CompleteMultipartUpload with a bad top-level SHA256 checksum returns BadDigest
CompleteMultipartUpload with a missing per-part checksum and bad top-level checksum returns BadDigest
Multipart upload with SHA256 checksum completes and reports the composite checksum on demand
UploadPartCopy with syntactically invalid CopySourceRange values returns InvalidArgument
UploadPartCopy with a CopySourceRange past the end of the source returns InvalidRange
Original accepts HTTP status 400 or 416 with error code InvalidRange; only the error code is asserted here.
Multipart copy of a prefix of a 12MiB source at sizes straddling the part boundary
Original copies six prefix sizes; this vector covers representative 1-, 2- and 3-part copy sizes and verifies each destination equals the source prefix.
UploadPartCopy of a single byte from a 7MiB source object
Multipart copy from source keys with special names (space, underscores, '?versionId')
Multipart copy of each version of a versioned source object
UploadPartCopy without CopySourceRange copies the whole source object
GetObject/HeadObject with PartNumber on a completed multipart object
Three-part SHA256 multipart upload yields a stable composite ETag and checksum across repeated completes
GetObject/HeadObject with PartNumber on a single-part multipart object
GetObject/HeadObject with PartNumber on an SSE-C multipart object
SSE-C customer key is the well-known s3-tests test key, supplied as raw key bytes via $base64.
skipped: Server Side Encryption is not supported
30MiB multipart upload with metadata and content-type, full and ranged read-back
Original also verifies the whole object via _check_content_using_range in 1MB and 10MB steps; a representative ranged GET of the first 1MB is asserted here.
CompleteMultipartUpload with an unknown UploadId returns NoSuchUpload
Multipart upload of three 5MiB parts plus a 3MiB tail reads back concatenated
CompleteMultipartUpload with no parts (empty body) returns MalformedXML
boto3 sends an empty request body when MultipartUpload is omitted; expressed as a raw POST ?uploadId with an empty body.
CompleteMultipartUpload with a wrong part ETag returns InvalidPart
CompleteMultipartUpload referencing a part number that was never uploaded returns InvalidPart
Multipart uploads at sizes straddling the 5MiB part boundary complete
Original exercises six sizes (5MiB, +100KiB, +600KiB, 10MiB+100KiB, 10MiB+600KiB, 10MiB); this vector covers representative 1-, 2- and 3-part sizes on the same key.
Multipart upload succeeds on a bucket carrying an allow-all bucket policy
Original uploads 50MiB; a 10MiB two-part upload is used here since the point is that the bucket policy does not block the owner's multipart upload.
skipped: Bucket policies are not supported
Multipart upload overwrites an existing single-PUT object
Re-uploading a part with identical data before completing produces the correct object
Original repeats this for several resend sets ([0],[1],[2],[1,2],[0..5]); this vector re-sends part 2 as a representative case.
Completing a multipart upload whose non-final parts are below 5MiB returns EntityTooSmall
One-byte multipart upload completes; retrying the complete is idempotent
CompleteMultipartUpload If-Match is evaluated against the current version on a versioned bucket
CompleteMultipartUpload If-None-Match is evaluated against the current version on a versioned bucket
CompleteMultipartUpload If-Match/If-None-Match conditions on an unversioned bucket, including after delete
Three-part multipart upload with CRC32 FULL_OBJECT checksums: complete, HEAD, object attributes, per-part GET
Three-part multipart upload with CRC32C FULL_OBJECT checksums: complete, HEAD, object attributes, per-part GET
Three-part multipart upload with CRC64NVME FULL_OBJECT checksums: complete, HEAD, object attributes, per-part GET
Three-part multipart upload with SHA1 COMPOSITE checksums: complete, HEAD, object attributes, per-part GET
Three-part multipart upload with SHA256 COMPOSITE checksums: complete, HEAD, object attributes, per-part GET
10 MiB two-part multipart upload preserves content integrity and size
In-progress multipart upload is discoverable via ListMultipartUploads/ListParts and can be resumed and completed
Multipart ETag differs from the single-part ETag of the same content and contains a dash
Aborting an already-aborted multipart upload is idempotent and returns 204
The source tolerated MinIO's idempotent second abort; converted to the AWS behavior where a second AbortMultipartUpload on the same upload id fails with NoSuchUpload.
CacheControl set on CreateMultipartUpload is preserved on the completed object
ContentType set on CreateMultipartUpload is preserved on the completed object
ListMultipartUploads accepts a KeyMarker parameter
The source tolerates both full KeyMarker filtering (AWS) and implementations that return all uploads (MinIO); only the unconditional assertion (the request succeeds and Uploads is present) is converted.
ListParts with PartNumberMarker returns only the parts after the marker
ListMultipartUploads reports an Initiated timestamp for an in-progress upload
Completed multipart upload no longer appears in ListMultipartUploads
The source asserts the specific upload id is absent from the listing; in this dedicated bucket that is equivalent to Uploads being absent from the parsed response.
UploadPart accepts a zero-byte part and returns an ETag
The source tolerated implementations rejecting zero-byte parts; converted to the AWS behavior where UploadPart accepts an empty body and returns an ETag (the 5 MiB minimum is enforced only at CompleteMultipartUpload, never on an individual UploadPart).
GetObject with PartNumber returns exactly that part's bytes and PartsCount
GetObjectAttributes on a completed multipart object reports total size and ObjectParts count
ListMultipartUploads on a bucket with no active uploads returns no uploads
ListMultipartUploads accepts a MaxUploads value above the server cap
The original additionally asserted the echoed MaxUploads is >= 1000 (AWS caps at 1000, MinIO at 10000, others echo the request); a numeric lower-bound is not expressible, so only presence is asserted.
UploadIdMarker without KeyMarker is ignored by ListMultipartUploads
ListMultipartUploads KeyMarker past all keys yields an empty upload list
Per the source test's stated intent: a KeyMarker lexicographically after every upload key returns no uploads. The literal boto3 assertion ('Uploads' key present) contradicts AWS, which omits the element when empty.
ListMultipartUploads with negative MaxUploads returns InvalidArgument
The original accepted InvalidArgument or InvalidRequest; AWS returns InvalidArgument.
ListMultipartUploads MaxUploads truncates with continuation markers
The original tolerated implementations that do not truncate (MinIO); this vector asserts the AWS truncation behavior.
ListMultipartUploads lists active uploads with key, upload id and storage class
StorageClass is asserted as STANDARD, REDUCED_REDUNDANCY or empty, matching the original's accepted set.
ListMultipartUploads reports ChecksumAlgorithm CRC32
ListMultipartUploads reports ChecksumAlgorithm CRC32C
ListMultipartUploads reports ChecksumAlgorithm SHA1
ListMultipartUploads reports ChecksumAlgorithm SHA256
ListMultipartUploads reports ChecksumAlgorithm CRC64NVME
ListParts after AbortMultipartUpload returns NoSuchUpload
ListParts default MaxParts is 1000
Original test also accepted 10000 (MinIO); AWS S3 defaults MaxParts to 1000.
ListParts on an upload with no parts returns an empty, non-truncated listing
Original test accepted Parts absent or an empty list; AWS returns no Part elements so the parsed response has no Parts member.
ListParts with a valid upload id but wrong object key returns NoSuchUpload
ListParts with a nonexistent upload id returns NoSuchUpload
ListParts with negative MaxParts returns InvalidArgument
ListParts returns all uploaded parts with number, ETag, size and storage class
ListParts pagination: MaxParts truncation and NextPartNumberMarker resume
AbortMultipartUpload with a valid upload id but wrong key returns NoSuchUpload
Original test also tolerated an idempotent success (MinIO); AWS S3 scopes the upload id to the key and returns NoSuchUpload.
AbortMultipartUpload with a nonexistent upload id returns NoSuchUpload
Original test also tolerated an idempotent success (MinIO); AWS S3 returns NoSuchUpload.
AbortMultipartUpload on a nonexistent bucket returns NoSuchBucket
AbortMultipartUpload returns HTTP 204 No Content
AbortMultipartUpload removes the upload from ListMultipartUploads
Original test asserted zero uploads remain; AWS returns no Upload elements so the parsed response has no Uploads member.
Aborted upload disappears from ListMultipartUploads
ListMultipartUploads lists all in-progress uploads
Original test compared the key set; AWS returns uploads sorted by key so an ordered match is equivalent.
ListMultipartUploads on a nonexistent bucket returns NoSuchBucket
ListMultipartUploads honors MaxUploads and sets IsTruncated
Original test tolerated MinIO ignoring MaxUploads but asserted IsTruncated when the page was short; AWS returns exactly 2 uploads with IsTruncated true.
ListMultipartUploads lists a single in-progress upload with key and upload id
ListMultipartUploads with a Delimiter parameter succeeds
Original test only asserted a non-empty combined result (uploads plus common prefixes), which is not directly expressible; this vector requires the delimited listing call to succeed.
ListMultipartUploads with a Prefix parameter succeeds
Original test made no effective assertion on the filtering result (its check was vacuous), so this vector only requires the prefixed listing call to succeed.
Three-part multipart upload of 5 MiB parts reports total ContentLength
UploadPart after AbortMultipartUpload returns NoSuchUpload
UploadPart with part numbers outside 1-10000 returns InvalidArgument
The source test accepted InvalidArgument, InvalidPartNumber or InvalidPart; AWS returns InvalidArgument for out-of-range part numbers.
Parts uploaded out of order (3, 1, 2) are listed in ascending part-number order
UploadPart to a non-existent bucket returns NoSuchBucket
UploadPart with a valid upload ID but a different key returns NoSuchUpload
UploadPart with a non-existent upload ID returns NoSuchUpload
Re-uploading the same part number overwrites the part; ListParts shows only the latest ETag
The source asserts the two upload ETags differ; this is expressed by asserting each equals the MD5 ETag of its distinct part data.
UploadPart responds with HTTP 200 and an ETag
UploadPartCopy with malformed CopySourceRange values is rejected
AWS returns 400 InvalidArgument for a CopySourceRange that is not of the form bytes=first-last (including a start greater than the end). The source test also accepted InvalidRange.
UploadPartCopy with CopySourceRange copies exactly the requested bytes
bytes=100-200 is inclusive, so the resulting part is 101 bytes.
UploadPartCopy CopySourceIfMatch: matching ETag succeeds, wrong ETag returns PreconditionFailed
UploadPartCopy CopySourceIfModifiedSince: past date succeeds, future date returns PreconditionFailed
The source test used now-1day and now+1day; fixed far-past and far-future dates preserve the semantics. AWS returns 412 PreconditionFailed for the unmet condition; the source test also accepted NotModified from other implementations.
UploadPartCopy CopySourceIfNoneMatch: non-matching ETag succeeds, matching ETag returns PreconditionFailed
AWS returns 412 PreconditionFailed when x-amz-copy-source-if-none-match matches the source ETag. The source test also accepted NotModified from other implementations.
UploadPartCopy CopySourceIfUnmodifiedSince: future date succeeds, past date returns PreconditionFailed
The source test used now+1day and now-1day; fixed far-future and far-past dates preserve the semantics.
UploadPartCopy with a range extending past the source object end is rejected
Unlike a ranged GET, UploadPartCopy strictly validates CopySourceRange against the source size: bytes=0-2000 on a 1024-byte object is 400 InvalidArgument on AWS. The source test also accepted InvalidRange.
UploadPartCopy with a range starting beyond the source object end is rejected
bytes=2000-3000 on a 1024-byte source object is 400 InvalidArgument on AWS. The source test also accepted InvalidRange.
UploadPartCopy with a key different from the multipart upload's returns NoSuchUpload
An upload id is tied to the key it was created for; using it with another key must fail.
UploadPartCopy with an unknown upload id returns NoSuchUpload
UploadPartCopy with a malformed CopySource is rejected
CopySource must be of the form bucket/key. AWS returns 400 InvalidArgument for each malformed value; the source test also accepted NoSuchBucket, NoSuchKey and InvalidRequest from implementations that parse the source before validating it.
UploadPartCopy with part numbers outside 1-10000 is rejected
AWS returns 400 InvalidArgument for part numbers 0, -1 and 10001. The source test also accepted InvalidPartNumber and InvalidPart from other implementations.
UploadPartCopy to a non-existent destination bucket returns NoSuchBucket
UploadPartCopy from a non-existent source bucket returns NoSuchBucket
UploadPartCopy from a non-existent source key returns NoSuchKey
UploadPartCopy copies a whole 5 MiB object into a part; ListParts shows its size and ETag
CompleteMultipartUpload after AbortMultipartUpload returns NoSuchUpload
CompleteMultipartUpload with an empty Parts list returns MalformedXML
AWS returns MalformedXML for an empty CompleteMultipartUpload Parts list. The source test also accepted InvalidRequest from other implementations.
CompleteMultipartUpload with parts out of ascending order returns InvalidPartOrder
CompleteMultipartUpload with an invalid ETag returns InvalidPart
CompleteMultipartUpload with a negative part number is rejected
AWS returns 400 InvalidArgument for a part number outside 1-10000. The source test also accepted InvalidPart from other implementations.
Single-part multipart upload completes and has the correct size
CompleteMultipartUpload with non-final parts under 5 MiB returns EntityTooSmall
CompleteMultipartUpload succeeds when MpuObjectSize matches the actual object size
Multipart ETag has hash-partcount format and HeadObject returns the same ETag
CompleteMultipartUpload replaces an existing object at the same key
Multipart upload initiated with a private canned ACL yields grants on the completed object
skipped: ACL is not supported
CreateMultipartUpload accepts an Expires header and the upload completes
The original test treats preservation of Expires as implementation-specific and only asserts the operations succeed, so no Expires response assertion is made.
Multipart upload initiated with SSE-S3 preserves AES256 encryption on the completed object
skipped: Server Side Encryption is not supported
WebsiteRedirectLocation set at CreateMultipartUpload is preserved on the completed object
Assembled multipart object content equals the concatenation of its parts
50 MiB multipart upload of ten 5 MiB parts completes with correct ContentLength
Parts uploaded in reverse order assemble in part-number order on complete
Metadata, tags and content type from CreateMultipartUpload are preserved on the completed object
Multipart upload initiated with StorageClass STANDARD completes and the object is readable
The original test's StorageClass assertion is vacuous (always true); the converted assertion is that all operations succeed.
Last multipart part may be smaller than the 5 MiB minimum
Multipart upload with 50 parts assembles correctly
Each part is 5 MiB of its decimal part number repeated; the read-back digest covers the 250 MB concatenation.
Multipart upload with the maximum part number 10000 succeeds
CompleteMultipartUpload with a non-last part under 5 MiB returns EntityTooSmall
The source accepted EntityTooSmall or InvalidPart; the canonical S3 code EntityTooSmall is asserted here.
CompleteMultipartUpload naming parts that were never uploaded returns InvalidPart
The source accepted InvalidPart or NoSuchKey; the canonical S3 code InvalidPart is asserted here.
Multipart part numbers need not be consecutive
Parts 1, 5 and 10 are uploaded; the assembled object must be their concatenation (digest of 5 MiB 'a' + 5 MiB 'b' + 5 MiB 'c').
Multipart upload of a single empty part creates a zero-length object
The source skipped when the server rejected empty parts with EntityTooSmall/InvalidPart; AWS accepts a single empty part, asserted here.
CreateMultipartUpload rejects duplicate tag keys
Original test accepted InvalidTag or InvalidRequest; alternatives are not expressible, so the common HTTP 400 status is asserted.
CreateMultipartUpload accepts degenerate empty tagging strings
CreateMultipartUpload rejects tag keys and values with invalid characters
Original test accepted InvalidTag, InvalidTagKey or InvalidTagValue; alternatives are not expressible, so the common HTTP 400 status is asserted.
Multiple concurrent multipart uploads are allowed for the same key
Original test also asserted the three upload IDs differ; value inequality is not expressible, but three listed uploads for the key implies three distinct uploads.
CreateMultipartUpload on a non-existing bucket returns NoSuchBucket
CreateMultipartUpload accepts - _ . / in tag keys and values
CreateMultipartUpload returns a non-empty upload ID with bucket and key
CreateMultipartUpload metadata and content headers are preserved on the completed object
CreateMultipartUpload with StorageClass STANDARD completes successfully
Original test only verified the object exists after completion (ETag present); it did not assert the storage class on HeadObject.
CreateMultipartUpload with Tagging applies tags to the completed object
CreateMultipartUpload response contains Bucket, Key and UploadId
CreateMultipartUpload returns a non-empty UploadId for each new upload
The source test also asserts the two UploadIds differ; inequality between captured values is not expressible, so only the non-empty-string assertion is carried over.
ContentDisposition set at CreateMultipartUpload is preserved on the completed object
ContentEncoding set at CreateMultipartUpload is preserved on the completed object
ContentLanguage set at CreateMultipartUpload is preserved on the completed object
CreateMultipartUpload preserves keys with special ASCII characters
CreateMultipartUpload accepts a 1024-byte key (maximum allowed)
Basic two-part multipart upload completes
Aborted multipart upload leaves no object and invalidates the UploadId
Complete one multipart upload and abort another in the same bucket
ListParts returns an uploaded part
Parts uploaded out of order are assembled in part-number order
Re-uploading the same part number overwrites with the later part
UploadPart rejects out-of-range part numbers with InvalidArgument
Complete a 5-part multipart upload and read back with matching checksum
UploadPart returns the part ETag
ListParts reports the size of each uploaded multipart part
After uploading three 10 MiB parts to an in-progress upload, ListParts returns all three parts with correct Size, and the upload can be aborted.
ListMultipartUploads lists only in-progress uploads, not completed or aborted
Completed and aborted multipart uploads do not appear in ListMultipartUploads; abandoned (in-progress) uploads do, and can be aborted to clear the list.
Re-uploading the same part number with the same data is idempotent
Uploading part 1 three times with identical bytes returns the same ETag each time and ListParts shows a single part.
Resume a multipart upload using ListParts as a checkpoint
After uploading some parts, ListParts reports the uploaded parts; remaining parts are uploaded and the object completes with the full combined size.
CreateMultipartUpload with a 1025-byte key returns KeyTooLongError
The source test accepts KeyTooLongError or InvalidArgument; KeyTooLongError is the AWS S3 error code.
Put and get a plain text object
Put and get an empty object
Put and get a key containing subdirectories and spaces
Write, read, delete, then verify absence
GetObject response-* query overrides set the corresponding response headers
Source also passes ResponseExpires='123' (an invalid timestamp string, never asserted); it is omitted here because a typed SDK ResponseExpires member would reformat it and diverge from the raw source request. The source's bucket/object ACLs are 'private', the defaults, so plain prerequisites are used.
GetObject with a non-matching If-Match returns PreconditionFailed
GetObject with a matching If-Match returns the object
The source captures the ETag from the PutObject response; the object prerequisite's etag attribute is the same value.
GetObject with a future If-Modified-Since is ignored and returns the object
AWS ignores a future If-Modified-Since (verified against AWS S3: a future If-Modified-Since on an unmodified object returns 200, not 304). Raw-HTTP form so the header is sent verbatim.
GetObject with a past If-Modified-Since returns the object
The source passes 'Sat, 29 Oct 1994 19:43:31 GMT'; the same instant is written in ISO 8601 form for the timestamp parameter.
GetObject with a non-matching If-None-Match returns the object
GetObject with a matching If-None-Match returns 304 with the ETag header
A 304 response has no error body; the source additionally asserts boto's synthesized error message 'Not Modified' (the HTTP reason phrase). The raw-HTTP form asserts the wire-defined parts: status 304 and the ETag response header.
GetObject with a future If-Unmodified-Since returns the object
The source passes 'Sat, 29 Oct 2100 19:43:31 GMT'; the same instant is written in ISO 8601 form for the timestamp parameter.
GetObject with a past If-Unmodified-Since returns PreconditionFailed
The source passes 'Sat, 29 Oct 1994 19:43:31 GMT'; the same instant is written in ISO 8601 form for the timestamp parameter.
DeleteObjects removes multiple keys and repeating it reports the missing keys as deleted
DeleteObjects with more than 1000 keys returns 400
The source test seeds 1001 objects and verifies the count via a listing paginator before the delete; the 400 response to a 1001-key request is request validation independent of the objects existing, so the seeding and pagination count-check are omitted. The source asserts only the 400 status, not an error code.
DeleteObjects removes multiple keys (verified with ListObjectsV2) and repeating it reports the missing keys as deleted
Content-Encoding set on PutObject is returned by HeadObject
aws-chunked is stripped from the stored Content-Encoding
S3 removes the aws-chunked token from the Content-Encoding it stores; a Content-Encoding consisting only of aws-chunked tokens is stored as no Content-Encoding at all.
Anonymous DeleteObject after the bucket is deleted returns NoSuchBucket
The source sets public-read ACLs on the bucket and object via a shared helper; both are deleted before the tested request, so the ACLs are irrelevant and omitted (bucket prerequisites cannot carry ACLs).
skipped: Anonymous access is not supported
HeadObject on an empty object reports ContentLength 0
PutObject without metadata replaces existing metadata
The source asserts the returned Metadata map is exactly empty; the only metadata ever written is meta1, so absence of the x-amz-meta-meta1 response header expresses the same assertion.
GetObject on a missing key returns NoSuchKey
Empty-string user metadata value round-trips
User metadata set on PutObject is returned on GetObject
Overwriting an object can replace its metadata value with the empty string
Cache-Control set on PutObject is returned by HeadObject
PutObject response ETag is the quoted MD5 of the body
Expires set on PutObject is returned by HeadObject
The source sets Expires to now+6000s and compares the round-tripped timestamp; vectors cannot compute relative times, so a fixed far-future instant is used. The expected header value is the RFC 7231 http-date serialization of that instant, which is how SDKs encode timestamp-in-header parameters.
PutObject with a binary body reads back identically
Object write, read, overwrite, re-read and delete round-trip
PutObject to a nonexistent bucket returns NoSuchBucket
The source uses the literal bucket name 'whatchutalkinboutwillis'; a reserved never-created name is used instead to avoid collisions with real buckets.
GetObjectAttributes on a simple object returns size, unquoted ETag and storage class
ETag is the unquoted MD5 of the 3-byte body 'foo'. Attributes not applicable (DeleteMarker, VersionId, ObjectParts) must be absent.
Authenticated PutObject succeeds with HTTP 200
Control characters in an object key are valid and round-trip
AWS accepts C1 control characters (e.g. U+008A) as valid object-key bytes; the key round-trips through PutObject/GetObject (verified against AWS S3). An "unparseable URI" 400 only arises from a raw request line containing a literal control byte, which an SDK operation cannot produce.
Ranged GetObject over an 8 MiB object returns 206 with the correct middle slice
Ranged GetObject on an empty object returns 416 InvalidRange
Ranged GetObject entirely beyond the object size returns 416 InvalidRange
Ranged GetObject returns 206 with the requested slice and Content-Range
Ranged GetObject with a suffix range returns the trailing bytes
Ranged GetObject with an open-ended range skips leading bytes
DeleteObjects bulk-deletes 20 objects and leaves the prefix empty
Reduced from the source test's batch-operations flow: concurrent copy/tagging phases are omitted; the bulk delete is applied to the batch-uploaded objects.
Put a simple object, read it back and find it in a listing
Deleted object returns NoSuchKey on GET and disappears from listing
Cache-Control header round-trips for several directive combinations
Content-Encoding gzip header round-trips on a gzip-compressed object
Content-Disposition header round-trips for inline, attachment and filename forms
Single-part PutObject ETag equals content MD5 and metadata is preserved
PutObject with 10 user metadata entries, all returned by HeadObject
Put/get round-trip preserves body bytes and the ETag
10 MiB object round-trips with size and MD5 intact
ETag determinism: identical bodies share an ETag, different bodies do not
Five body pairs (identical, differing digit, case, trailing space, both empty). Inequality is asserted with a negative-lookahead regex against the captured first ETag.
Ranged GETs return exactly the requested bytes of a patterned object
All 256 byte values survive a put/get round-trip
PutObject ETag is a quoted hex string across object sizes 0 B to 5 MiB
DeleteObject on 'dir/' succeeds even when nested keys exist under it
Original tolerated DirectoryNotEmpty/NoSuchKey from POSIX-backed gateways; S3 semantics are idempotent success.
Deleting 'name' does not remove the distinct object 'name/'
Deleting directory marker 'dir/' leaves nested 'dir/file' intact
DeleteObject on a missing key succeeds (idempotent)
DeleteObject returns HTTP 204
Original accepted 200 or 204; S3 responds 204 No Content.
DeleteObject on a non-versioned bucket returns no DeleteMarker
Original accepted DeleteMarker absent or explicitly false; AWS omits the field on non-versioned buckets, asserted here as absent.
DeleteObject removes the object; subsequent HeadObject returns 404
Deleting the same key twice succeeds both times
DeleteObject works with dashes, underscores, dots, spaces and parens in keys
DeleteObjects with a single missing key reports it deleted and leaves others
DeleteObjects deletes a 100-key batch in one request
DeleteObjects mixing existing and missing keys reports all deleted and empties the bucket
Original asserted set equality of Deleted keys; unordered set equality beyond length plus one membership probe is not expressible; the emptied bucket is verified by listing.
DeleteObjects on a missing bucket returns NoSuchBucket
DeleteObjects on missing keys reports them deleted (idempotent)
Original asserted set equality of Deleted keys; unordered set equality beyond length plus one membership probe is not expressible.
DeleteObjects in Quiet mode omits the Deleted list but deletes the keys
Original accepted an empty Deleted list as well; AWS omits it entirely in quiet mode.
DeleteObjects returns HTTP 200
DeleteObjects response lists every deleted key
Original asserted set equality of Deleted keys; unordered set equality beyond length plus one membership probe is not expressible.
DeleteObjects removes the named keys and leaves the rest
Original asserted set equality of Deleted keys; unordered set equality beyond length plus one membership probe is not expressible; the surviving keys are verified by listing.
DeleteObjects handles keys with spaces, slashes and @
Original asserted set equality of Deleted keys; unordered set equality beyond length plus one membership probe is not expressible.
Zero-byte object: upload, HEAD, GET and copy all preserve zero length
Empty user-metadata map and empty metadata values are accepted, empty values preserved
Single-character object key is accepted
ETag is identical across PutObject, HeadObject, GetObject and ListObjectsV2
Zero-byte object PUT returns an ETag
Identical content uploaded to two keys yields identical ETags
Overwriting an object with different content changes its ETag
The source asserts only inequality of the two ETags; expressed as a negative-lookahead regex against the captured original ETag.
ETag is a quoted hex string of at least 32 characters across object sizes
Binary, control-character, Unicode and embedded-NUL bodies all yield quoted ETags
GetObject after DeleteObject returns 404 NoSuchKey
GetObject on an existing object returns HTTP 200
Expires header set at upload is returned by GetObject
The source used utcnow() plus one hour; a fixed future date is equivalent since Expires is stored metadata, not enforced behavior.
WebsiteRedirectLocation set at upload is returned by GetObject
The source tolerated implementations that omit WebsiteRedirectLocation; converted to the AWS round-trip assertion that the value is echoed back.
GetObject on an empty directory-marker key (trailing slash) returns zero-length body
GetObject on a key under a non-existing parent path returns NoSuchKey
GetObject on a missing directory-marker key (trailing slash) returns NoSuchKey
GetObject with ResponseExpires sets an Expires header in the response
GetObjectAttributes with an empty attributes list returns InvalidArgument
GetObjectAttributes returns ETag, ObjectSize, StorageClass and LastModified for an existing object
GetObjectAttributes with an invalid attribute name alongside a valid one returns InvalidArgument
GetObjectAttributes on a key nested under an existing file object returns NoSuchKey
GetObjectAttributes with a single invalid attribute name returns InvalidArgument
GetObjectAttributes on a non-existing bucket returns NoSuchBucket
GetObjectAttributes on a non-existing object returns NoSuchKey
GetObject ContentLength matches the actual body size
GetObject without trailing slash does not match a directory-style key with slash
GetObject returns a 1 MiB object intact
GetObject response includes a LastModified timestamp
GetObject returns the Cache-Control value set at upload
GetObject returns the ContentType set at upload
GetObject returns user metadata set at upload
GetObject on a zero-length object returns empty body and ContentLength 0
Ranged GetObject over a known byte pattern returns exact bytes for several ranges
Range extending past the object end is trimmed to object size
Single-byte ranges bytes=0-0 and bytes=99-99 return exactly one byte
HeadObject with Range on a directory-style key ('dir/') returns 206 partial content
HeadObject on 'key' returns 404 when only 'key/' exists
HeadObject on a key nested under an existing file key returns 404
HeadObject on 'key/' returns 404 when only 'key' exists
HeadObject returns ContentType, ContentEncoding, ContentLanguage and ContentDisposition
HeadObject returns a LastModified timestamp
HeadObject on a missing key returns 404
HeadObject returns length, content type, ETag, metadata and LastModified
HeadObject returns user-defined metadata
HeadObject Range extending beyond object size is trimmed to the object
HeadObject with malformed Range headers returns the full object
HeadObject with unsatisfiable Range returns 416
HeadObject with valid Range headers returns correct Content-Range and Content-Length
Standard response headers set at PutObject are returned by HeadObject
ContentEncoding 'identity' is sent but not asserted: the original test only required 4 of 5 headers to be preserved. The original also sent an Expires timestamp without asserting it; it is omitted here.
Mixed-case metadata keys are handled case-insensitively and returned lowercased
ASCII metadata values with spaces, quotes and punctuation are preserved
The original also sent a non-ASCII (unicode) metadata value but only required 4 of 6 values to survive; the unicode value is omitted here because non-ASCII metadata handling is undefined in S3.
ETag is quoted hex and consistent between PutObject and HeadObject; LastModified is returned
PutObject accepts user metadata just under the 2KB limit
PutObject rejects user metadata over the 2KB limit
AWS S3 returns MetadataTooLarge; the original test also accepted RequestHeaderSectionTooLarge.
100 user metadata headers round-trip through PutObject and HeadObject
ASCII special-character user metadata values round-trip unchanged
S3 user metadata must be US-ASCII; non-ASCII values are not portably supported (SDKs reject them or AWS RFC2047-encodes them). This vector covers ASCII special characters only.
User metadata named after system headers does not override system values
PutObject accepts a 200-character user metadata key name
Original test also tolerated an InvalidArgument rejection; AWS S3 accepts long metadata key names within the aggregate 2KB metadata limit.
Object keys are case-sensitive
Puts four case variations of the same name and lists to confirm four distinct objects (keys in UTF-8 byte order).
Keys with consecutive slashes are distinct objects
Puts keys differing only in the number of consecutive slashes, then lists to confirm all are distinct (AWS behavior; keys in UTF-8 byte order). Source test also tolerated vendor-specific rejection.
PutObject directory-style key with non-empty body is readable
Keys with dot segments are stored literally
Puts keys containing '.' and '..' segments and lists to confirm all four exist as literal keys (AWS behavior; keys in UTF-8 byte order). Source test also tolerated vendor-specific rejection.
PutObject with empty key segments (a//b//c) round-trips
AWS stores the key literally. Source test also tolerated vendor-specific rejection (XMinioInvalidObjectName etc.).
PutObject and GetObject with a leading-slash key
AWS treats a leading slash as part of the literal key. Source test also tolerated vendor-specific rejection (XMinioInvalidResourceName/InvalidObjectName).
PutObject creates file key alongside existing directory-style key
Creates 'foo/' then 'foo' and verifies both exist as separate objects (AWS behavior). Source test also tolerated vendor-specific rejection (ExistingObjectIsDirectory/InvalidRequest) on filesystem-backed implementations.
PutObject creates directory-style key alongside existing file key
Creates 'foo' then 'foo/' and verifies both exist as separate objects (AWS behavior). Source test also tolerated vendor-specific rejection (ObjectParentIsFile/InvalidRequest) on filesystem-backed implementations.
PutObject creates nested key under an existing file key
Creates 'foo' then 'foo/bar'; keys are independent on AWS. Source test also tolerated vendor-specific rejection (ObjectParentIsFile/InvalidRequest) on filesystem-backed implementations.
PutObject overwrite changes content and ETag
Source test asserted the two PutObject ETags differ; expressed here as each ETag equaling the MD5 of its distinct body.
PutObject accepts keys with normal, trailing, doubled and leading slashes
AWS accepts all of these keys literally. Source test also tolerated vendor-specific rejection (XMinioInvalidObjectName etc.) of the double/leading-slash keys.
Zero-byte object: GET returns empty body and HEAD reports ContentLength 0
Small object sizes 1 B to 1 KiB report correct ContentLength
Power-of-two object sizes 1 KiB to 1 MiB report correct ContentLength
The source loop reuses the single key 'power-20' for every size (a loop-variable artifact), so each PUT overwrites the previous object and HEAD is checked immediately after each PUT; that request sequence is preserved here.
PutObject preserves ContentLanguage, visible via HeadObject
PutObject preserves WebsiteRedirectLocation, visible via HeadObject
Five sequential overwrites of the same key leave the last-written body
The source test performs five blocking sequential PutObject calls (not concurrent) writing version-0..version-4, then reads back and accepts any of the five. Under strong read-after-write consistency the deterministic result is version-4 (last write wins).
PutObject replacing an object updates its Content-Type
PutObject stores a 1 KB metadata value
PutObject stores Content-Disposition retrievable via HeadObject
PutObject preserves explicit Content-Type across various media types
PutObject with StorageClass STANDARD stores the object
The original test only verifies the object was created (ContentLength) since HeadObject may omit StorageClass for STANDARD.
Zero-length PutObject creates a retrievable empty object
Explicit Content-Type on PutObject is preserved and returned by HeadObject
Eight common Content-Type values set explicitly on PutObject must be returned unchanged by HeadObject.
PutObject preserves Content-Type together with Content-Encoding
Batch DeleteObjects removes a nested prefix completely with no orphans
Recursive delete of a nested prefix via a single DeleteObjects batch. Object count reduced from the source's 1000 to a representative sample.
GetObject returns exact bytes for checksum verification; overwrite replaces content
Object ETag reflects content and changes on overwrite
GetObject in a non-existent bucket returns 404 NoSuchBucket
HeadObject with negative PartNumber returns 400
1024-byte object key is accepted; 1025-byte key is rejected
S3 keys are limited to 1024 bytes. The source test accepted KeyTooLongError or InvalidRequest; AWS returns KeyTooLongError.
Multipart object created with object-lock retention resists delete until bypassed
warning: teardown s3tests-object-lock-0001-b1-eb3890fd: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: VUTI8XTQXXKLSDSE, HostID: qc5alRpIeg4iop0uKVqpO4Am/5pyKGDdDOUJOJ4ix8vlWjrE2YkM2DFt1nLfhx8HIk6BNeAHWXQ7g9KFsXduzL0=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0001-b1-eb3890fd: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: 2WJTB4VQ5TTPD5R6, HostID: VWmSmpQZ/oar8hdmsLpD1COp6vxt13mCV2VAJ4vznKzSfemVqNTUnr03a2mNB/0pNntfZY95Cyheh574jlI0c5Q=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
Deleting a version under GOVERNANCE retention fails without bypass and succeeds with it
warning: teardown s3tests-object-lock-0002-b1-06183df6: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: 7FBDC9S73EH49ZHA, HostID: A6ZZ3u6NfNCWLnXJmucnFI5ngWF0OwAUzKuOKPfMd6GHilpV9xUuRiTxUw1yBBVvq+pUFe8+bOzTVA5xHj+JFlU=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0002-b1-06183df6: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: 3SSS5OQ3ROXHMS55, HostID: +Fp5Xybd+YbtjNM9BFjfMdx6r9yMv/+yldIcIReYACnZzafn0XSkSFfZZzqjbiUcqiE/VKCgIQwC9jnWwMwocCY=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GOVERNANCE retention still blocks version delete after creating and removing a delete marker
warning: teardown s3tests-object-lock-0003-b1-1523ae45: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: SRKGTSGLPT3LXWDJ, HostID: 2LduFqZqv8buyYGZ8Z+50IAs8gsZmxLU/HErDQ32BQb/KnJ1p9Bv6y2oY6T/r8jAoHs7Oy6PpgrQYyrdWZ1lZSA=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0003-b1-1523ae45: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: 4DJGGD3UOZ0B7HDE, HostID: S+TUT330031KSFeR5kMsU29O4uNT06t04hj9InVw/W2ZPwtCzHAiXJlcVDUv8H7oYFFEscuHl8B93zpJ7rK23oA=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GetObjectLegalHold reflects legal hold status after setting ON then OFF
GetObjectLockConfiguration returns the stored default retention configuration
GetObjectLockConfiguration on a bucket without object lock returns ObjectLockConfigurationNotFoundError
GetObjectRetention on a bucket without object lock returns InvalidRequest
HeadObject exposes x-amz-object-lock-retain-until-date as an ISO 8601 timestamp
warning: teardown s3tests-object-lock-0008-b1-fdd60fc7: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: HM9I09KDX6YPUQSH, HostID: 30Mk08JfngLKFo8bvSQiYSrCDBMrpHHTzWNuN2HGMQF3I70z2aiJHNmyDFmAJyhaHW89/CKT+oA5d/mtzVxFoLk=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0008-b1-fdd60fc7: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: QL78UD5S1FPK0IQ4, HostID: 6q7JzB8+C2otey82B+3/OSHQGxRXD/Q8EFYOTjDFi3Kon/ZnnO43Z/Own5RgF4s/jsQImiAgfeTP25kszoxP4KY=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
DeleteObjects reports AccessDenied for a retained version and deletes the unretained one
warning: teardown s3tests-object-lock-0009-b1-2861dda3: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: JJX0MW0ZWDEDCSZN, HostID: aYnBthTtwPcRVmJMXMXeZUhf8GuqacIag0Uxc85MxkITtK2kM14SHZ3GpMh/f0kdSdsE+a7kYewulqqsBXbUHDg=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0009-b1-2861dda3: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: N0M4EBR36U4AYLB3, HostID: k5D4v2TFquaf9pMgHoXXt4iOftjQKGAYeJYnITBIfVw3wPPU4wKqb3gjSWwseJR6x92tnOrD4wljxNcbRau4Xkk=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObjectLegalHold accepts Status ON and OFF
PutObjectLegalHold on a bucket without object lock returns InvalidRequest
PutObjectLegalHold with an invalid status returns MalformedXML
PutObjectLockConfiguration accepts GOVERNANCE/Days and COMPLIANCE/Years defaults and implies versioning enabled
PutObjectLockConfiguration fails on unversioned or suspended bucket but succeeds once versioning is enabled
PutObjectLockConfiguration on a bucket without object lock returns InvalidBucketState
PutObjectLockConfiguration with Days of 0 returns InvalidRetentionPeriod
PutObjectLockConfiguration with an invalid or lowercase retention mode returns MalformedXML
PutObjectLockConfiguration with ObjectLockEnabled of Disabled returns MalformedXML
PutObjectLockConfiguration with negative Years returns InvalidRetentionPeriod
PutObjectLockConfiguration with both Days and Years returns MalformedXML
PutObjectRetention stores GOVERNANCE retention readable via GetObjectRetention
warning: teardown s3tests-object-lock-0021-b1-44e6c665: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: LHPTX3NWNZYWVO69, HostID: 8dHDyLVadDpfjxs0xKGi5yvUBz5ngVv583xXOYbzru2I1Yk0n/aZ4SBV86GPnOydDmEho+RyanpX1mLwLl5zV+Y=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0021-b1-44e6c665: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: UCPQMAC83V9PEGZZ, HostID: InTGOyq8C5W6fZnzCSJRIikhZUtmcLWoV42jymil2UysXlqqsEBQGUj2NjoMGxhKz7pqvpSbCLgNRg9hm1DR/VI=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObjectRetention can extend an existing GOVERNANCE retention period
warning: teardown s3tests-object-lock-0022-b1-e8d108d4: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: VWR9JI2R2UXLNKCR, HostID: h6OqHOMrx/9IkauBkj2RHJX9Zev6++uQqqVPZeAXMmuRkPJQ22BsMAKLtgzxbmURrA3QboCZvXwu1kD+OYI1cQ4=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0022-b1-e8d108d4: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: J8RMUCPX4LUH3S6B, HostID: 0VYnMVwWGqXVIiYk46vg+9aTD4OmLWh1WzkvohJioSa/JptW3Z6jQ+mDc6ktjnz5DbnqapsSVh83rBQ1MDLyYFg=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObjectRetention on a bucket without object lock returns InvalidRequest
PutObjectRetention with lowercase or invalid mode returns MalformedXML
Explicit PutObjectRetention overrides the bucket default retention
warning: teardown s3tests-object-lock-0025-b1-fc6f86fb: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: L5Q592FD37MKH7FG, HostID: M7wuCzY+ldnbNAmPKgosnQM2Zc2Sf2tHkl6WydLvd9xdAD2VpaQ3O9IZ+IhF39+ocf496q13v3fhEevwxFQ6Oq8=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0025-b1-fc6f86fb: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: KXDBZCX0S1VSI07Z, HostID: NiDJdVmYpfeY1EaNbRKIWpeWWP4OQplDC/XGisksKsVe1Qct3nSn0Gb6lZGgXjuhw93IfHN2KMU1rj8mHuL7FTg=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
Shortening a GOVERNANCE retention period without bypass returns AccessDenied
warning: teardown s3tests-object-lock-0026-b1-cdb22320: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: GDOQ51CFVESZR0IB, HostID: +1UPqaW4dgC33Avo2NbRHK4rynNReWPHzvpclwNDj3+gfM3CUccUg1loRuXG/tQSHuA7Tpu9dxGoVYSh16spPEI=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0026-b1-cdb22320: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: UO51TNADMZT9AF9P, HostID: dwyiMhADfd+RDO8MSQI1+68HpnS1NgkD1mXhHiHFvOKR7MkHJLTVriHOuZmQ9DIFl7vWpfS6G1HCCxvZnaqFWNM=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
Shortening a GOVERNANCE retention period succeeds with BypassGovernanceRetention
warning: teardown s3tests-object-lock-0027-b1-d37a7bcc: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: GDXIOEQ64AJWNDX6, HostID: bylkd/hjoTB+CwXu3AF8JQPt0Y3vyJ99IXLUPZ0RCzi9o8yAh6kLMV3uTiRQfIi7+4v4REKIqcP0KzslQvGGYYg=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0027-b1-d37a7bcc: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: J80J3VCGCUNR4OOW, HostID: 53NDfhrd4JdWLAKlc7EdisSC5grbOlYI41U5ELtpVAcjRcq/9UCTO1ivbpcEyFl95rUfQgidSTSR22ljffUv/a4=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObjectRetention targeting a specific version stores retention readable for that version
warning: teardown s3tests-object-lock-0028-b1-c7eb6769: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: XJ2B0EZJWM8RK6C2, HostID: /RHqCvf/m5al1CGehREDn10D9pOpqkASQrwO0Eh9kW2clNKLyflDAUkSrTsPuDNV65GWAMAbSXkzbPc/gq9Khn8=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0028-b1-c7eb6769: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: 54TS916KVXAWITGA, HostID: qveiC4q/U9bl4eNGMWgqV16pzSjgwXos503eiWrZHXmtQy6rMVJlvnXVM8fOOkTYi02jwxz3SiQH7Gd9TBGvBkE=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
Suspending versioning on an object-lock bucket returns InvalidBucketState
PutObjectRetention changing GOVERNANCE to COMPLIANCE without bypass returns AccessDenied
The source test uses a retain-until date 10 seconds in the future; a fixed future date is used here since GOVERNANCE retention remains bypassable for cleanup.
warning: teardown s3tests-object-lock-0030-b1-c9efad15: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: SE6JIQFPQNCAG4PV, HostID: IOgWwMTAmLzylYHu8XJYntooevjtPD2zAMWVirhcMd6sln52EWZqcEdygnq9QEdhGwpTH0M1SQrIwCODSyOrgXw=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0030-b1-c9efad15: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: SOX71LV0CCPPNGC4, HostID: 1itDvDhhu7xN+Mdrqpnrf4sJRZcWIYNw866yKAn3m3dopIWuT2TUpMy/XL5A80ZiscjESkkPgeiqnacBjzshyiA=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
DeleteObject on a multipart-uploaded version created with legal hold ON returns AccessDenied
DeleteObject on a version with legal hold OFF succeeds with 204
DeleteObject on a version with legal hold ON returns AccessDenied
GetObjectLegalHold on a bucket without object lock returns InvalidRequest
HeadObject reports object lock mode, retain-until date and legal hold status
warning: teardown s3tests-object-lock-0035-b1-bf64ef3b: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: FCX7Z3GZM1623QJ7, HostID: FL1PEpjpstQyT80orS+E8QCRJO4r0DcKe+ZMpScYaiifqJLvfB81iPbFJEfFtIXlc905h8wbHpxOS0GbhvXMNvo=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0035-b1-bf64ef3b: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: W8DIE5HRB22YQ8LG, HostID: Yn3cfheb+9cKL2FKxWZeOccKTSpRgafv5gsFG8uGxkpBX9+iruuCqTAVK/gqkZt4PPz1hbUyyChIvOBCgCPD/VU=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObject with object lock parameters; HeadObject reports them back
warning: teardown s3tests-object-lock-0036-b1-7f7a3965: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: 7GK13YD6XIZ55Z3J, HostID: aRXETtWcgxF+R0y023mV8SqfU4P9uM1XjDF2tkKdqndRODHqh45ivqe5iUqsjpQs4BqUrwtt6UFWgFrGBbYjf/E=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0036-b1-7f7a3965: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: XSZMUPLXI35KJ74T, HostID: 5Y/ebMpX/9d6a6uuRyHz9PYGAPQGDWGWM1lUUvLA9swAhVbmjN1Sd9V4o63G4ZS80B081zqy3pKx+uY+q8wFtIY=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GetObjectLegalHold on a non-existent bucket returns NoSuchBucket
Source test also tolerated InvalidRequest/NoSuchKey (MinIO); the primary expected AWS code NoSuchBucket is asserted.
GetObjectLegalHold on a missing key in a lock-enabled bucket returns NoSuchKey
GetObjectLegalHold on an object with no legal hold set returns NoSuchObjectLockConfiguration
Source test also tolerated InvalidRequest; the primary expected AWS code NoSuchObjectLockConfiguration is asserted.
PutObjectLegalHold with no legal-hold body returns MalformedXML
Source test also tolerated InvalidArgument/InvalidRequest; the primary expected AWS code MalformedXML is asserted.
PutObjectLegalHold on a non-existent bucket returns NoSuchBucket
PutObjectLegalHold on a missing key in a lock-enabled bucket returns NoSuchKey
Legal hold set to ON is readable via GetObjectLegalHold
GetObjectLockConfiguration on a non-existent bucket returns NoSuchBucket
Source test also tolerated ObjectLockConfigurationNotFound(Error) (MinIO); the primary expected AWS code NoSuchBucket is asserted.
Object lock configuration with COMPLIANCE default retention round-trips
Default retention specifying both Days and Years returns MalformedXML
Source test also tolerated InvalidArgument/InvalidRequest; the primary expected AWS code MalformedXML is asserted. Bucket is created without object lock, matching the source test.
PutObjectLockConfiguration with no configuration body returns MalformedXML
Source test also tolerated InvalidArgument/InvalidRequest; the primary expected AWS code MalformedXML is asserted.
Negative default retention Days or Years returns InvalidArgument
Source test also tolerated InvalidRequest; the primary expected AWS code InvalidArgument is asserted. Bucket is created without object lock, matching the source test.
PutObjectLockConfiguration on a non-existent bucket returns NoSuchBucket
Source test also tolerated InvalidBucketState (MinIO); the primary expected AWS code NoSuchBucket is asserted.
Minimal object lock configuration round-trips on a lock-enabled bucket
GetObjectRetention on missing bucket returns NoSuchBucket
Source test also accepted NoSuchKey/InvalidRequest; AWS returns NoSuchBucket.
GetObjectRetention on missing key returns NoSuchKey
GetObjectRetention returns the mode and retain-until date that were set
warning: teardown s3tests-object-lock-0053-b1-b87b2653: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: ZWCKV6YMINQRO3YW, HostID: MEx0SvsskNymFcnFuB+dGt+pPcJTJHqyBp7ltkno2irW97JvMWeoawA2VWBxXFjOQbibRAi2FEivG1e98dDZD3M=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0053-b1-b87b2653: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: XPV5SPNFZO0KPNAM, HostID: YVUCaVGkOGevsdcy/RsHbFsBs6weJmwNtPmu/40CV2Sj5lKrlfbo5dMH7eRnXH98jD7xEKxSsmB0NAQsg2BgPb4=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GetObjectRetention with no retention set returns NoSuchObjectLockConfiguration
Source test also accepted InvalidRequest; AWS returns NoSuchObjectLockConfiguration.
PutObjectRetention with past RetainUntilDate returns InvalidArgument
The source test never creates the key, matching its request exactly. It also accepted InvalidRequest and MalformedXML; AWS returns InvalidArgument (the retain until date must be in the future).
PutObjectRetention on missing bucket returns NoSuchBucket
PutObjectRetention on missing key returns NoSuchKey
COMPLIANCE retention cannot be overwritten with GOVERNANCE
Source test also accepted vendor codes ObjectLocked/InvalidRequest; AWS returns AccessDenied. Leaves a COMPLIANCE-locked object: runner cleanup is blocked until the retention date passes.
warning: teardown s3tests-object-lock-0058-b1-07411a75: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: 2QYTJCH1NRJTJOWQ, HostID: 29z2fjkxiayCN+6arpThW0OwSKHA2ZAjyG1U16kU2F0iffG1PS7EBi9eB5A724x/vRjhy5qJaojYiZ6BPcceMHM=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0058-b1-07411a75: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: TT88R86TLW6Y286G, HostID: thogQruM79OZOCo8r8EWl5tsKYIf9Jx3AfY/mp/kMJLGJzKoxOc2K4OO+wnDSIKILlAvjMJwYERr3E1ekvOyx/A=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
COMPLIANCE retention can be extended to a later date
Leaves a COMPLIANCE-locked object: runner cleanup is blocked until the retention date passes.
warning: teardown s3tests-object-lock-0059-b1-16197c9a: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: MJ0PIOEZCNEYI9BL, HostID: q6IysW1PeiXU7Sz2iAnX6BzUVui7ROCNLaqiq27CcSsMr5iI1vOu9h0Xci40zrUDscto9saEtp7Xeb2lFKu1dm4=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0059-b1-16197c9a: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: L2S1RIWKVLCGC0PJ, HostID: mnJHMmInjI4qX2JvjBANzxODBV7uqp+vypZ3QG1Sxet4a7fvIPa+8t5xE0a/7hsX7MMIW3Hpy28VMbCIeldv9lA=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GOVERNANCE retention can be extended to a later date
warning: teardown s3tests-object-lock-0060-b1-0918ec93: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: DNT8BDG8SMDWFP1O, HostID: cukwupz9groZplLk5mcoXpJHAI/Z0WRSxh9QVu5fB3Abfqkb3GNkJwpaPIJOl+ZliqLV+XPyhR+YGdBfGJexO7w=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0060-b1-0918ec93: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: E7R0X1SWK8DMXEG8, HostID: yokCgx4kLRKFOIneHA02QVSDCz+DQk+40Tj7srLgrY1WKsPc9fRHiKj/MOHYJPXv6bjut4MI1/SAyuzswI2l+Bk=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GOVERNANCE retention overwritten with BypassGovernanceRetention
Source test skipped when bypass permission was unavailable; the runner's main identity is assumed to hold s3:BypassGovernanceRetention. Leaves a COMPLIANCE-locked object: runner cleanup is blocked until the retention date passes.
warning: teardown s3tests-object-lock-0061-b1-84ddcd54: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: S42GEMT3RTOCSQFP, HostID: KbNiUGHfQBbKvng8y3J/pMJ2DqlDo3zSCeZ87tC4VkNOSFGcPbqam2QMBCUhtPH3leNU8oI0rPuCD4pZVU7lu1U=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0061-b1-84ddcd54: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: X11KJR2G9I7P9I9Y, HostID: stFQONH3D1GNHpdHy152qe8GP9xsHdQ97cLAUYkySNl28xkGvjsXI231uBoFkCXgzb0GOHFnm/JHfgKLB199960=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GOVERNANCE to COMPLIANCE change without bypass is denied
Source test also accepted vendor codes ObjectLocked/InvalidRequest; AWS returns AccessDenied.
warning: teardown s3tests-object-lock-0062-b1-bbf534a7: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: AFS5EEVBG04G54UK, HostID: VF38cw+lIdEAgJwr4jWXKxq/trxBf3mVxIUMxdsmoOYexschAixPht2Sa7pKVH8P4YDz6Ua/Nk9L22/El4nZg6E=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0062-b1-bbf534a7: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: Q9OJBDDA6BM4RQ2F, HostID: pmjyI1RA1oO9neNhIUolRHK8ijp0/oH1fy+7vUTKO9AwjTYsjGtGqBghwgtQYsTyCcv8I1pS1Q9VlZPWZGMDlL4=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObject with ObjectLockLegalHoldStatus ON sets a legal hold, visible via HeadObject
The original test used a plain bucket and skipped when object lock was unavailable; here an object-lock-enabled bucket prerequisite is used so the behavior is actually exercised. Runner cleanup must remove the legal hold before deleting the object.
PutObject with GOVERNANCE ObjectLockMode and a future RetainUntilDate sets retention, visible via HeadObject
The original test used a plain bucket, a retain-until date of now+30 days, and skipped when object lock was unavailable; here an object-lock-enabled bucket prerequisite and a fixed far-future date are used. Runner cleanup must delete with governance bypass.
warning: teardown s3tests-object-lock-0064-b1-f7741ed0: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: PNPSRHU91P3SJYQQ, HostID: XFVxgyB0I94oGkXFjszJjqc4PMDcNoHgCV18brXYwwZIU4dEKe1TZEUA98LcXNUrwt354emBVgG2VCJI6mpeP6Y=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0064-b1-f7741ed0: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: 5JD1P4SPQS91T2Q2, HostID: WrdCHyKehjyppZRL82BPOdbZuTl/SNKS3leJilzaKa6SbLtjp9/hvMe3p6kFfDsK0qiF+OMSeLOYhB564ghkLik=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
CreateBucket with ObjectLockEnabledForBucket enables object lock
PutObject with GOVERNANCE retention, read back via GetObjectRetention
warning: teardown s3tests-object-lock-0066-b1-f05f1018: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: UPPLFSSP71DJAV35, HostID: c14mtGI56yyisvOXoPX1ZAoGw+wIu5llMGXvjQuxBvJZqUGxfr1bIso0NZtCUtLhl2OZUBqoc60AKbDwbgW4mug=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0066-b1-f05f1018: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: M5YU7U5PR0TYFS78, HostID: iiAOr/DZ7LvYUP1LfokXShG85/2zBdAdw9PE3OV98P0ZEtUVZRjH4sjW20znD9GYG6vodJBrEi+PXxaA0MFR3EA=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
PutObject with COMPLIANCE retention, read back via GetObjectRetention
warning: teardown s3tests-object-lock-0067-b1-5e709ef6: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: AMG8SE6S72B6UBIX, HostID: XR//M64bolJCGfhZqBZhKtx2dOi2QGUPu7AHZfiqigLwAzmpr3f7+/8aE+PsP58mzFWBTZbLFpICkfObA1Qfbmk=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0067-b1-5e709ef6: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: HN2PCWBH8P3A7150, HostID: utjeMg94ADBlhjq67nF69lhnnhnKOSnOyPpXW7m/NZ744tnO0+7vI7N3q1nk5qBlw2GzSC6lJ+bgcV3tUwJGZPY=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
Extend a GOVERNANCE retention period with PutObjectRetention
warning: teardown s3tests-object-lock-0068-b1-dd1ec65c: DeleteObjects: operation error S3: DeleteObjects, https response error StatusCode: 403, RequestID: 10BS963PZ3PR6EBL, HostID: P/nIb0rsIaTpszxAS9BsXZIMTe9eg4m+OhcWC6+e0zVQNzOypcIsd0F1aSgDXEui/0nueUcYMQ0FmcWfB/nmV5k=, api error AccessDenied: Access Denied because object protected by object lock.
warning: teardown s3tests-object-lock-0068-b1-dd1ec65c: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: E3X8K8D6BIUJ1565, HostID: z+vkkGuuXbpjK3fGhrrVfSQUUdTaFhqUuAwhDkv3rqADvYrhRiNKTp6KpmBBWBSEUOd5tocT5wSLc/6+yDI8qPg=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
GetBucketPolicyStatus reports IsPublic true after an authenticated-read canned ACL
skipped: ACL is not supported
GetBucketPolicyStatus on a fresh private bucket reports IsPublic false
skipped: Bucket policies are not supported
GetBucketPolicyStatus stays IsPublic false after attaching a policy restricted by source IP
skipped: Bucket policies are not supported
GetBucketPolicyStatus reports IsPublic true after a public-read canned ACL
skipped: ACL is not supported
GetBucketPolicyStatus flips to IsPublic true after attaching a public bucket policy
skipped: Bucket policies are not supported
Put, get and delete a bucket policy; get after delete returns NoSuchBucketPolicy
skipped: Bucket policies are not supported
Allow policy restricted to a specific principal reports non-public policy status
skipped: Bucket policies are not supported
Deny bucket policy blocks GetPublicAccessBlock
skipped: Bucket policies are not supported
HeadObject under a prefix-conditioned ListBucket policy: 404 inside the prefix, 403 outside
skipped: Bucket policies are not supported
IgnorePublicAcls makes public-read ACLs behave as private
skipped: ACL is not supported
Delete public access block, then get returns NoSuchPublicAccessBlockConfiguration
skipped: Bucket policies are not supported
PutPublicAccessBlock round-trips the configuration
skipped: Bucket policies are not supported
GetPublicAccessBlock after deleting the configuration returns NoSuchPublicAccessBlockConfiguration
skipped: Bucket policies are not supported
BlockPublicAcls rejects PutObject with public canned ACLs but allows private
skipped: ACL is not supported
BlockPublicPolicy rejects putting a public bucket policy
skipped: Bucket policies are not supported
BlockPublicAcls rejects PutBucketAcl with public and authenticated-read canned ACLs
skipped: ACL is not supported
Bucket policy allowing s3:ListBucket to everyone grants list access to another account (ListObjects)
skipped: Bucket policies are not supported
Deny s3:ListBucket bucket policy overrides authenticated-read bucket ACL (ListObjects)
skipped: ACL is not supported
Bucket policy combining Allow effect with NotPrincipal is rejected
The source accepts either InvalidArgument or MalformedPolicy as the error code (AWS returns MalformedPolicy); only the 400 status is asserted here.
skipped: Bucket policies are not supported
Wildcard-resource policy fetched via GetBucketPolicy can be applied to another bucket (ListObjects)
skipped: Bucket policies are not supported
GetObjectAcl allowed only when s3:ExistingObjectTag condition matches; other operations stay denied
skipped: ACL is not supported
GetObject allowed only when s3:ExistingObjectTag condition matches the object's tag
skipped: Bucket policies are not supported
CreateMultipartUpload requires s3:PutObject on the object ARN, not the bucket ARN
skipped: Bucket policies are not supported
Deny statement on s3:x-amz-acl public* blocks PutObject with a public canned ACL but not a plain put
skipped: ACL is not supported
PutObject conditioned on s3:x-amz-copy-source restricts which sources may be copied
skipped: Bucket policies are not supported
PutObject conditioned on s3:x-amz-metadata-directive allows COPY and denies copies without it
skipped: Bucket policies are not supported
Policy requiring aws:kms denies PutObject requesting AES256 encryption
skipped: Bucket policies are not supported
PutObject allowed only when s3:RequestObjectTag condition matches the request tagging
The source test intends the second put to carry the x-amz-tagging header (its event hook is registered on the wrong client); expressed here via the Tagging parameter.
skipped: Bucket policies are not supported
StringNotEquals deny on s3:x-amz-server-side-encryption rejects wrong SSE algorithm and accepts AES256
skipped: Bucket policies are not supported
Policy requiring AES256 denies PutObject requesting aws:kms encryption
The KMS key id does not need to exist: the request is denied by the bucket policy before any KMS use.
skipped: Bucket policies are not supported
Deny unencrypted PutObject via Null condition on s3:x-amz-server-side-encryption
skipped: Bucket policies are not supported
StringLikeIfExists condition on aws:Referer allows matching referers and denies non-matching ones
skipped: Bucket policies are not supported
Bucket policy allowing s3:ListBucket to everyone grants list access to another account (ListObjectsV2)
skipped: Bucket policies are not supported
Deny s3:ListBucket bucket policy overrides authenticated-read bucket ACL (ListObjectsV2)
skipped: ACL is not supported
Wildcard-resource policy fetched via GetBucketPolicy can be applied to another bucket (ListObjectsV2)
skipped: Bucket policies are not supported
RestrictPublicBuckets public access block revokes anonymous access granted by a public bucket policy
skipped: Anonymous access is not supported
Public access block configuration round-trips full, partial and all-false settings and can be deleted
skipped: ACL is not supported
PutBucketPolicy accepts Action and Resource as arrays
skipped: Bucket policies are not supported
PutBucketPolicy accepts a policy with multiple Allow and Deny statements
skipped: Bucket policies are not supported
PutBucketPolicy accepts an AWS service Principal
The source test skipped when MinIO rejected the Service principal with MalformedPolicy; AWS accepts service principals such as logging.s3.amazonaws.com.
skipped: Bucket policies are not supported
PutBucketPolicy accepts wildcard Resource patterns including a prefix wildcard
skipped: Bucket policies are not supported
PutBucketPolicy accepts the s3:* wildcard Action
skipped: Bucket policies are not supported
PutBucketPolicy replaces an existing bucket policy
skipped: Bucket policies are not supported
DeleteBucketPolicy removes the policy; subsequent GetBucketPolicy returns NoSuchBucketPolicy
skipped: Bucket policies are not supported
GetBucketPolicy on a bucket without a policy returns NoSuchBucketPolicy
GetBucketPolicy on a non-existent bucket returns NoSuchBucket
The source test also tolerated NoSuchBucketPolicy (MinIO); AWS returns NoSuchBucket when the bucket itself does not exist.
PutBucketPolicy with syntactically invalid JSON returns MalformedPolicy
The source test also tolerated InvalidArgument and InvalidJSON; AWS returns MalformedPolicy for a policy document that is not valid JSON.
PutBucketPolicy on a non-existent bucket returns NoSuchBucket
PutBucketPolicy with a Deny statement succeeds and is retrievable
skipped: Bucket policies are not supported
PutBucketPolicy accepts an IpAddress condition block and GetBucketPolicy returns it
skipped: Bucket policies are not supported
PutBucketPolicy accepts a StringLike condition block and GetBucketPolicy returns it
skipped: Bucket policies are not supported
PutBucketPolicy stores a policy that GetBucketPolicy returns
skipped: Bucket policies are not supported
DeleteBucketPolicy removes the policy so GetBucketPolicy returns NoSuchBucketPolicy
skipped: Bucket policies are not supported
Presigned GetObject URL succeeds before its expiry
The source additionally sends an OPTIONS request to the presigned URL expecting 400; that part is not expressible (the presigned URL is runner-minted per step) and is omitted.
skipped: ACL is not supported
Presigned PUT with ACL parameter then presigned GET reads the body back
skipped: ACL is not supported
Presigned GET URL returns the object body
Presigned PUT URL uploads the object
Presigned GET URL honors ResponseContentDisposition override
Presigned GET URL for a missing key returns 404
Presigned DELETE URL removes the object
Copy an SSE-S3 encrypted object to an SSE-S3 encrypted destination
Covers the sse-s3 -> sse-s3, STANDARD -> STANDARD, 8 MiB parametrization of test_copy_enc; KMS and non-STANDARD storage-class combinations need server-side provisioning and are not expressible.
skipped: Server Side Encryption is not supported
Copy an SSE-C encrypted object to an SSE-C destination with a different key
Covers the sse-c -> sse-c, STANDARD -> STANDARD, 8 MiB parametrization of test_copy_enc; KMS and non-STANDARD storage-class combinations need server-side provisioning and are not expressible.
skipped: Server Side Encryption is not supported
UploadPartCopy from an SSE-S3 source into an SSE-S3 multipart upload
Covers the sse-s3 -> sse-s3, STANDARD -> STANDARD, 8 MiB parametrization of test_copy_part_enc; KMS and non-STANDARD storage-class combinations need server-side provisioning and are not expressible.
skipped: Server Side Encryption is not supported
UploadPartCopy from an SSE-C source into an SSE-C multipart upload enforces SSE-C headers
Covers the sse-c -> sse-c, STANDARD -> STANDARD, 8 MiB parametrization of test_copy_part_enc; KMS and non-STANDARD storage-class combinations need server-side provisioning and are not expressible.
skipped: Server Side Encryption is not supported
SSE-C write and read-back of a 13-byte object
skipped: Server Side Encryption is not supported
DeleteBucketEncryption is idempotent and removes the SSE-S3 configuration
skipped: Server Side Encryption is not supported
GetBucketEncryption before and after configuring SSE-S3 default encryption
skipped: Server Side Encryption is not supported
GetObjectAttributes on an SSE-C object requires the customer key headers
The source also asserts the attributes ETag equals the PutObject ETag; that exact equality is not expressible here (the source strips the quotes before comparing and the SSE-C ETag value is not derivable), so the ETag is asserted as present.
skipped: Server Side Encryption is not supported
PutBucketEncryption with SSE-S3 default encryption succeeds
skipped: Server Side Encryption is not supported
PutObject with an invalid server-side encryption algorithm returns 400 InvalidArgument
x-amz-server-side-encryption: 'aes:kms' (aes, not aws) is not a valid algorithm. The source injects it as a raw header before signing; expressed as a raw-HTTP PUT so no SDK enum validation intervenes. Empty object body.
skipped: Server Side Encryption is not supported
PutObject with both SSE-KMS and SSE-C headers returns 400 InvalidArgument
The source test injects the conflicting raw headers before signing, using a dummy KMS key id ('fool-me-once') and the ceph s3-tests public dummy SSE-C fixture. Empty object body.
skipped: Server Side Encryption is not supported
PutObject with both SSE-S3 and SSE-C headers returns 400 InvalidArgument
The source test injects the conflicting raw headers before signing; the SSE-C key is the ceph s3-tests public dummy fixture. Empty object body.
skipped: Server Side Encryption is not supported
PutObject with SSE-S3 AES256 plus a KMS key id returns 400 InvalidArgument
x-amz-server-side-encryption: AES256 conflicts with x-amz-server-side-encryption-aws-kms-key-id; the key id is a dummy value ('fool-me-once'). Empty object body.
skipped: Server Side Encryption is not supported
PutObject with SSE-C key and key-MD5 but no algorithm header returns 400
Raw-HTTP step: the request deliberately omits the customer-algorithm header.
skipped: Server Side Encryption is not supported
Bucket policy denying non-AES256 SSE-C algorithms: AES192 PutObject gets 403, AES256 succeeds
skipped: Bucket policies are not supported
Bucket policy denying unencrypted uploads: plain PutObject gets 403, SSE-C PutObject succeeds
skipped: Bucket policies are not supported
PutObject with SSE-C key and a non-matching key MD5 returns 400
skipped: Server Side Encryption is not supported
SSE-C object: HEAD without the key returns 400, HEAD with the key succeeds
skipped: Server Side Encryption is not supported
Completed SSE-C multipart object: readable with the upload key, GET with a different key returns 400
skipped: Server Side Encryption is not supported
UploadPart with a different SSE-C key than the multipart initiation returns 400
skipped: Server Side Encryption is not supported
UploadPart with the multipart SSE-C key but a non-matching key MD5 returns 400
skipped: Server Side Encryption is not supported
SSE-C multipart upload (6 aligned 5MiB parts): complete, list size, full read-back and boundary-sampling ranged reads
The source test range-reads the whole object at step sizes 1000000, 10000000 and partlen-1/partlen/partlen+1; this vector samples the boundary-relevant chunks of each pass (first chunk, part-boundary-crossing chunk, tail chunk).
skipped: Server Side Encryption is not supported
PutObject with SSE-C algorithm header but no key is rejected
Raw-HTTP step: the request deliberately sends only the customer-algorithm header with no key material.
skipped: Server Side Encryption is not supported
PutObject with SSE-C algorithm and key but no key-MD5 header is rejected
The source test injects the headers at the wire level to bypass the SDK's automatic key-MD5 computation, so this is a raw-HTTP step.
skipped: Server Side Encryption is not supported
SSE-C object read with a different key returns 400
skipped: Server Side Encryption is not supported
SSE-C object cannot be read without supplying the key
skipped: Server Side Encryption is not supported
SSE-C multipart upload with parts not aligned to the 4k encryption block size: complete, list size, full and ranged read-back
Parts are 5MiB+1 bytes (not a multiple of the 4k encryption block size). The source test range-reads the whole object at several step sizes; this vector samples the boundary-relevant chunks of each pass.
skipped: Server Side Encryption is not supported
PutObject with a KMS key id but without declaring aws:kms encryption returns 400
skipped: Server Side Encryption is not supported
GetObject with SSE-KMS request headers returns 400
Raw-HTTP step: the GetObject API has no server-side-encryption request members, so the source test injects the x-amz-server-side-encryption headers at the wire level.
skipped: Server Side Encryption is not supported
Bucket default SSE-S3 encryption: 1-byte upload and read-back report x-amz-server-side-encryption: AES256
skipped: Server Side Encryption is not supported
Bucket default SSE-S3 encryption: HEAD reports AES256; HEAD carrying an x-amz-server-side-encryption request header returns 400
skipped: Server Side Encryption is not supported
Bucket default SSE-S3 encryption: multipart upload (6 aligned 5MiB parts) completes, lists at full size, and reads back AES256-encrypted with metadata and ranged reads
The source test range-reads the whole object at step sizes 1000000 and 10000000; this vector samples the boundary-relevant chunks of each pass (first chunk, part-boundary-crossing chunk, tail chunk).
skipped: Server Side Encryption is not supported
Explicit SSE-S3 (ServerSideEncryption=AES256): 1-byte upload and read-back report x-amz-server-side-encryption: AES256
skipped: Server Side Encryption is not supported
SSE-S3 (AES256) round-trip on PutObject, CopyObject and multipart upload
SSE-C portion of the source test is omitted: its customer key is 34 bytes, which AWS rejects, so the test self-skips that branch.
skipped: Server Side Encryption is not supported
Bucket default encryption (AES256) is applied to objects uploaded without SSE parameters
skipped: Server Side Encryption is not supported
GetObject with a mismatched SSE-C key is denied
SSE-C keys are the well-known test bytes '0'*32 (upload) and '1'*32 (mismatched read). The source accepted BadRequest, InvalidRequest or 403; converted to the AWS behavior of 403 AccessDenied.
skipped: Server Side Encryption is not supported
PutObject with SSE-S3 (AES256) reports the encryption in PutObject and HeadObject responses
skipped: Server Side Encryption is not supported
CreateMultipartUpload with SSE-S3 echoes ServerSideEncryption AES256
skipped: Server Side Encryption is not supported
SSE-C object is readable only with the customer key
skipped: Server Side Encryption is not supported
Bucket default encryption applies SSE-S3 to uploads that specify none
skipped: Server Side Encryption is not supported
Invalid SSE algorithm and undersized SSE-C key are rejected
skipped: Server Side Encryption is not supported
SSE-S3 bucket encryption configuration round-trips
skipped: Server Side Encryption is not supported
CopyObject can add SSE-S3 encryption to an unencrypted object
skipped: Server Side Encryption is not supported
Multipart upload initiated with SSE-S3 produces an encrypted object
skipped: Server Side Encryption is not supported
GetObjectTagging allowed only when s3:ExistingObjectTag condition matches the object's tag
skipped: Bucket policies are not supported
PutObjectTagging allowed only while s3:ExistingObjectTag condition matches, and replacing the matching tag revokes access
skipped: Bucket policies are not supported
Bucket policy allowing s3:DeleteObjectTagging to everyone lets another account delete object tags
skipped: Bucket policies are not supported
HeadObject reports the number of tags in the x-amz-tagging-count header
Bucket policy allowing s3:GetObjectTagging to everyone lets another account read object tags
skipped: Bucket policies are not supported
DeleteObjectTagging returns 204 and removes all tags
PutObjectTagging with 129-char tag keys fails with InvalidTag and stores no tags
PutObjectTagging with 11 tags fails with BadRequest and stores no tags
PutObjectTagging with 257-char tag values fails with InvalidTag and stores no tags
PutObjectTagging accepts 10 tags with maximum-size 128-char keys and 256-char values
The original test used random strings and only asserted each returned pair was among the input pairs; this vector uses deterministic strings and asserts one representative pair round-trips and the count is 10.
PutObjectTagging accepts the maximum of 10 tags and returns them all
A second PutObjectTagging replaces the previous tag set entirely
PutObject with an x-amz-tagging header (including a valueless tag) stores tags readable via GetObjectTagging
Bucket policy allowing s3:PutObjectTagging to everyone lets another account write object tags
skipped: Bucket policies are not supported
Bucket tagging lifecycle: NoSuchTagSet before set, tag readable after put, NoSuchTagSet again after delete
Tagging supplied on CreateMultipartUpload is readable after complete and removable via DeleteObjectTagging
Object tagging: set at upload, update, special characters, 10-tag maximum, and delete
Tags supplied at CreateMultipartUpload are present on the completed object
DeleteObjectTagging on a missing key returns NoSuchKey
DeleteObjectTagging removes all tags
AWS behavior after deletion: 200 with an empty TagSet. The source test also tolerated a NoSuchTagSet/NoSuchTagSetError error from some implementations; the AWS path is asserted.
DeleteObjectTagging succeeds on a tagged object
The source test accepted HTTP 200 or 204; alternative statuses are not expressible, so generic success (2xx) is asserted (AWS returns 204).
GetObjectTagging for a key nested under an existing file-like key returns NoSuchKey
GetObjectTagging on a missing key returns NoSuchKey
GetObjectTagging returns exactly the three tags that were set
GetObjectTagging on an untagged object returns an empty TagSet
AWS behavior: 200 with an empty TagSet. The source test also tolerated a NoSuchTagSet/NoSuchTagSetError error from some implementations; that alternative is not expressible and the AWS path is asserted.
Tags can be set, updated and deleted repeatedly on one object
After deletion the source test tolerated NoSuchTagSet/NoSuchTagSetError from some implementations; the AWS behavior (200 with empty TagSet) is asserted.
Tag values may contain slashes, at-signs and spaces
Duplicate tag keys in one TagSet are rejected
The source test accepted any of InvalidTag/DuplicateTagKeys/InvalidArgument; alternative codes are not expressible, so only the 400 status is asserted (AWS returns InvalidTag).
Tag key over 128 chars or tag value over 256 chars is rejected
The source test accepted any of InvalidTagKey/InvalidTag/InvalidArgument (key) and InvalidTagValue/InvalidTag/InvalidArgument (value); alternative codes are not expressible, so only the 400 status is asserted (AWS returns InvalidTag).
PutObjectTagging on a missing key returns NoSuchKey
PutObjectTagging replaces the entire existing tag set
PutObjectTagging sets tags retrievable via GetObjectTagging
More than 10 tags on an object is rejected
The source test accepted any of BadRequest/InvalidTag/InvalidArgument; alternative codes are not expressible, so only the 400 status is asserted (AWS returns BadRequest).
DeleteBucketTagging removes all bucket tags
GetBucketTagging on a bucket with no tags returns NoSuchTagSet
GetBucketTagging on a non-existent bucket returns NoSuchBucket
PutBucketTagging with duplicate tag keys returns InvalidTag
PutBucketTagging rejects a 200-char tag key and a 300-char tag value with InvalidTag
Tag keys are limited to 128 characters and tag values to 256 characters.
PutBucketTagging on a non-existent bucket returns NoSuchBucket
PutBucketTagging with two valid tags succeeds and GetBucketTagging returns exactly those tags
PutBucketTagging with a valid tag succeeds
The original test accepts HTTP 200 or 204; that disjunction is expressed as the step simply succeeding (2xx).
PutBucketTagging with 51 tags is rejected with a 400 error
Buckets allow at most 50 tags. The original test accepts several error codes (InvalidTag, BadRequest, InvalidArgument, MalformedXML), so only the 400 status is asserted.
PutBucketTagging replaces the entire existing tag set
PutObject with Tagging sets tags readable via GetObjectTagging
Two GetObjectTagging steps assert membership of each tag independently, mirroring the original's unordered dict comparison.
HEAD after delete in an unversioned bucket returns 404 with no delete-marker header
Delete under suspended versioning still creates a delete marker
HEAD after delete in a versioned bucket reports x-amz-delete-marker true
Get and put the ACL of a specific object version
The owner's canonical ID and display name are captured from a first ACL read and asserted on later reads, since the runner's primary identity attributes are not directly referenceable.
skipped: ACL is not supported
PutObjectAcl without VersionId applies to the current object version
The owner's canonical ID and display name are captured from a first ACL read and asserted on later reads, since the runner's primary identity attributes are not directly referenceable.
skipped: ACL is not supported
PutObject returns a VersionId only on a versioning-enabled bucket
Toggle bucket versioning between Suspended and Enabled and read back the status
CompleteMultipartUpload returns a VersionId only on a versioning-enabled bucket
The same 30 MiB dataset is uploaded in six 5 MiB parts to an enabled, an unversioned and a suspended bucket; only the enabled bucket must return a VersionId.
Copy specific object versions by VersionId within and across buckets
DeleteObjects removes specific versions by VersionId and is idempotent
DeleteObjects removes versions and a delete marker by VersionId, and is idempotent
DeleteObjects on a missing key creates a delete marker and reports its version id
warning: teardown s3tests-versioning-0012-b1-4b8fd840: DeleteBucket: operation error S3: DeleteBucket, https response error StatusCode: 500, RequestID: 0Q2YC6E50N8QZ5ZV, HostID: RBkECjpQPkbJ3cwmc3dHQHu07Djn5v31Dbov8DWBqfxcn8Rl/VCz8HAdk+d/jCDo/hKAwEqbXgiLq1eytwEhl1s=, api error InternalError: We encountered an internal error, please try again.
Three multipart overwrites of one key create distinct versions readable by VersionId, then remove all versions
The source builds each 18 MiB object from a random 5-char string repeated to 5 MiB (uploaded as parts 1-3) plus a 3 MiB "123" pattern last part; fixed 5-char patterns are used here so full-body MD5s are deterministic.
Create five object versions and delete them newest-first, verifying remaining versions after each delete
One removal-order round of the source test (removal order: version 5, then version 4, then version 3, then version 2, then version 1). After each delete the remaining versions are listed newest-first and each remaining version body is read back.
Create five object versions and delete them oldest-first, verifying remaining versions after each delete
One removal-order round of the source test (removal order: version 1, then version 2, then version 3, then version 4, then version 5). After each delete the remaining versions are listed newest-first and each remaining version body is read back.
Create five object versions and delete the second-oldest remaining version each time, verifying remaining versions
One removal-order round of the source test (removal order: version 2, then version 3, then version 4, then version 5, then version 1). After each delete the remaining versions are listed newest-first and each remaining version body is read back.
Create five object versions and delete them in mixed order, verifying remaining versions after each delete
One removal-order round of the source test (removal order: version 4, then version 3, then version 1, then version 2, then version 5). After each delete the remaining versions are listed newest-first and each remaining version body is read back.
Delete the newest version by id, then add a delete marker; listing shows remaining versions and the marker
Create ten object versions and remove them one by one in mixed order, verifying remaining versions after each delete
Removal order: version 1, then version 3, then version 5, then version 7, then version 9, then version 2, then version 8, then version 6, then version 4, then version 10.
Create and remove ten versions of a key with special name ":"
Removal order: version 1, then version 3, then version 5, then version 7, then version 9, then version 2, then version 8, then version 6, then version 4, then version 10. One key of the source test per vector.
Create and remove ten versions of a key with special name " "
Removal order: version 1, then version 3, then version 5, then version 7, then version 9, then version 2, then version 8, then version 6, then version 4, then version 10. One key of the source test per vector.
ListObjectVersions orders versions by key, newest first within each key
Overwrite a pre-versioning object after enabling versioning, then delete each version by id
Overwriting a pre-versioning object under suspended versioning replaces the null version
Deleting the null version of a pre-versioning object removes it entirely
Suspend and re-enable versioning with interleaved deletes and overwrites, then remove every version by id
Five versions are created while enabled; under suspension the key is deleted and overwritten repeatedly (null versions replace each other); three more versions are created after re-enabling. All eight surviving real versions are then removed one by one, re-listing and re-reading remaining versions after each delete.
Copy a null-version object under suspended versioning within and across buckets
Repeated unversioned deletes on a versioned key stack delete markers
Deleting a specific version of a nested key echoes that VersionId
Original skipped when versioning was unsupported; here the versioned bucket is a prerequisite, so unsupported versioning reports blocked.
Objects uploaded with checksum algorithms appear in the version listing
Version created while versioning is suspended is listed with VersionId null
ListObjectVersions returns every version of every key, grouped by key
The ordered key expectation encodes the original's per-key version counts (bar=3, baz=5, foo=4) plus AWS key ordering.
All versions of a single key are listed newest-first with IsLatest on the first
MaxKeys truncates the version listing and markers resume it
ListObjectVersions on a missing bucket returns NoSuchBucket
The missing bucket name is derived from a runner-provisioned bucket name to avoid colliding with foreign buckets in a shared namespace.
Pre-versioning object is listed as the null version beneath its delete marker
Delete marker appears in DeleteMarkers with IsLatest alongside older versions
ListObjectVersions on an unversioned bucket returns null version IDs
Objects created with versioning never enabled appear as versions with a null VersionId ('null' or empty per the original test), IsLatest true, and correct Key/Size. ETag and StorageClass must be present.
Versioning toggles through unset, Enabled, Suspended and back to Enabled
For the initial unconfigured read the original test also tolerates an empty-string Status; in the parsed API model an unconfigured bucket has no Status member.
GetBucketVersioning on a non-existing bucket returns NoSuchBucket
GetBucketVersioning on a versioning-enabled bucket returns Status Enabled
PutBucketVersioning with an invalid Status value is rejected
Original test accepts MalformedXML, InvalidArgument or IllegalVersioningConfigurationException; all are HTTP 400, so only the status is asserted.
Enabling bucket versioning succeeds and is reflected by GetBucketVersioning
Suspending versioning after enabling it succeeds and is reflected by GetBucketVersioning
DeleteBucket on a versioned bucket holding object versions fails
Original test accepts BucketNotEmpty or VersionedBucketNotEmpty; both are HTTP 409, so only the status is asserted.
Five rapid overwrites of one key create five distinct, individually retrievable versions
The original asserted five unique version ids appear in ListObjectVersions in any order; AWS lists versions newest-first, so the listing asserts the exact order v4..v0. Each version body is read back by version id.
CopyObject from a versioned source key containing '?' and '&' echoes CopySourceVersionId
The source key 'my-obj?test&data' must be URL-encoded in the x-amz-copy-source header (?->%3F, &->%26) while the trailing ?versionId= suffix stays literal (the original relied on boto3 to do this). Original tolerated a missing CopySourceVersionId (MinIO); AWS returns it.
GetObjectAttributes on a delete marker fails
Original accepted NoSuchKey/MethodNotAllowed/404/405 for the delete-marker version and NoSuchKey/404 without a version id; AWS S3 returns 405 MethodNotAllowed for the delete-marker version and 404 NoSuchKey when the latest version is a delete marker.
GetObjectAttributes returns per-version attributes and defaults to the latest version
DeleteObject with VersionId permanently removes that version only
Deleting a specific version echoes its VersionId without a DeleteMarker flag (original also accepted an explicit false); that version is then gone while the other version remains latest. Original accepted NoSuchVersion/NoSuchKey/404 for the deleted version; AWS returns 404 NoSuchVersion.
DeleteObject on a non-existing key in a versioned bucket returns 204
AWS S3 creates a delete marker, but the original only asserted the 204 status (MinIO returns no VersionId/DeleteMarker for this case).
GetObject with a malformed VersionId returns InvalidArgument
Original accepted NoSuchVersion or InvalidArgument; AWS S3 returns 400 InvalidArgument for a version id that is not a valid version id format.
GetObject with VersionId=null retrieves the pre-versioning object
An object stored before versioning was enabled is retrievable as VersionId=null while a plain GET returns the newer version. Original tolerated a missing VersionId on the null-version GET (MinIO); AWS returns 'null'.
GetObject by VersionId and without it both return the stored version
HeadObject with a malformed VersionId returns 400
HEAD responses carry no error body, so only the status is asserted. Original accepted NoSuchVersion/404/400/NoSuchKey/InvalidArgument; AWS S3 returns 400 (InvalidArgument) for a malformed version id.
HeadObject with and without VersionId returns the version's metadata
HeadObject without VersionId returns the latest version
Object created before versioning is enabled remains readable as versionId=null
PutObject before versioning returns no VersionId; after enabling versioning a new PUT gets a real VersionId, GetObject with VersionId=null returns the original bytes, and a plain GetObject returns the new version. Original tolerated a missing VersionId on the null-version GET (MinIO); AWS returns 'null'.
Overwrites after enabling versioning create new versions above the null version
A null-version object exists, versioning is enabled, then two more PUTs. The latest GET returns the newest body and VersionId, and the intermediate version stays retrievable by its id (which also proves the two new version ids differ, as the original asserted).
PutObject to a versioning-enabled bucket returns a non-empty VersionId
CopyObject with a versionId CopySource copies the specified (older) version
CopyObject onto an existing key in a versioned bucket creates a new version
DeleteObject with VersionId=null permanently removes the pre-versioning null version
An object written before versioning is enabled has version id "null". Deleting that null version specifically removes it; a versioned GET of the null version then fails. The source test accepted NoSuchVersion/NoSuchKey/404; AWS returns NoSuchVersion.
Deleting a delete marker restores visibility of the previous version
DeleteObject with versioning suspended returns 204 and existing versions remain readable
DeleteObjects with explicit version IDs permanently removes the delete marker and the object version
DeleteObjects on a versioned bucket creates delete markers hiding all keys
GetObject addressing a delete-marker version returns MethodNotAllowed
GetObject without version ID on a key whose latest version is a delete marker returns NoSuchKey
HeadObject on a delete-marker version returns 405; HeadObject without version returns 404
Aborted multipart upload on a versioned bucket creates no version
Multipart upload over existing versions creates a distinct third, latest version
CompleteMultipartUpload on a versioned bucket returns a VersionId
Multipart upload metadata and content type are preserved on the created version
UploadPartCopy from a specific source version copies that version's bytes
Copy-to-self with REPLACE metadata creates a new version each time
On a versioned bucket, each CopyObject onto the same key with MetadataDirective REPLACE creates a distinct version that retains its own metadata.
A captured version is immutable under subsequent overwrites
After capturing version IDs, overwriting the keys with new versions does not change the bytes returned when reading the captured versions by VersionId.
Historical versions are retrievable by VersionId
On a versioned bucket, each PutObject creates a distinct version; GetObject with a VersionId returns that version's bytes, and GetObject without one returns the latest.
Enable then suspend bucket versioning across the object version lifecycle
New bucket reports no versioning; enabling produces distinct versions retrievable by VersionId; suspending stops new-version creation and GetObject returns the latest write.
Three PUTs of one key on a versioned bucket yield three versions
New bucket reports no versioning status
Repeated puts create distinct retrievable versions, listed newest-first
Deleting a specific version leaves the other versions intact
Deleting current version creates a delete marker; removing it restores the object
Suspending versioning makes a new put get the null version id
Each version retains its own user metadata
Copy the current object and copy a specific version by id
Two multipart uploads to one key create two independent versions
Delete marker hides object, version-id read bypasses it, removing the marker restores it
Three put/delete cycles yield three versions and three delete markers
Deleting a non-current version preserves the remaining versions
Deleting an already-deleted object creates a second, distinct delete marker
CreateBucket with an empty Authorization header returns AccessDenied
CreateBucket without an Authorization header returns AccessDenied
CreateBucket with an empty Content-Length header returns 400
CreateBucket with a negative Content-Length header returns 400
CreateBucket with an empty Expect header succeeds
skipped: quirk vector skipped by default (run it with NoSkip)
PutBucketAcl with an invalid canned ACL value in x-amz-acl returns 400
skipped: ACL is not supported
PutObject with an empty Authorization header returns 403
PutObject without an Authorization header returns 403
PutObject with an empty Content-Length header returns 400
PutObject with a negative Content-Length header returns 400
PutObject with an empty Content-Type succeeds
PutObject with an ordinary Content-Type header succeeds
PutObject with an empty Expect header succeeds
PutObject without an Expect header succeeds
PutObject with a well-formed but non-matching Content-MD5 returns BadDigest
PutObject with an empty Content-MD5 header returns InvalidDigest
PutObject with a Content-MD5 that decodes to the wrong length returns InvalidDigest
CreateBucket with malformed SigV2-style Authorization header 'AWS HAHAHA' returns InvalidArgument
The request is sent byte-literal (unsigned) with a two-token 'AWS HAHAHA' Authorization header; the server must reject the malformed header with 400 InvalidArgument before evaluating anything else, so the target bucket is never created.
CreateBucket with an empty User-Agent header succeeds
Source test used a SigV2 client, but User-Agent is not part of either signature scheme, so the behavior under test (server accepts an empty User-Agent) is signing-independent; the runner signs SigV4.
skipped: quirk vector skipped by default (run it with NoSkip)
PutObject with malformed SigV2-style Authorization header 'AWS HAHAHA' returns InvalidArgument
The request is sent byte-literal (unsigned) with a two-token 'AWS HAHAHA' Authorization header; the server must reject the malformed header with 400 InvalidArgument.
PutObject with an empty User-Agent header succeeds
Source test used a SigV2 client, but User-Agent is not part of either signature scheme, so the behavior under test (server accepts an empty User-Agent) is signing-independent; the runner signs SigV4. The source performs two puts to the same key with the empty User-Agent, first with an empty body, then with body 'bar'.