Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/main/java/com/datadobi/s3test/ChecksumTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,50 @@ public class ChecksumTests extends S3TestBase {
public ChecksumTests() throws IOException {
}

/**
* Puts an object with CRC32 checksum and verifies the server stores and returns it.
* Expected: Put succeeds; HEAD with ChecksumMode.ENABLED returns same content-length, ETag, and CRC32 checksum.
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
public void testCRC32() {
putWithChecksum(ChecksumAlgorithm.CRC32);
}

/**
* Puts an object with CRC32C checksum and verifies the server stores and returns it.
* Expected: Put succeeds; HEAD with ChecksumMode.ENABLED returns same content-length, ETag, and CRC32C checksum.
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
public void testCRC32_C() {
putWithChecksum(ChecksumAlgorithm.CRC32_C);
}

/**
* Puts an object with SHA1 checksum and verifies the server stores and returns it.
* Expected: Put succeeds; HEAD with ChecksumMode.ENABLED returns same content-length, ETag, and SHA1 checksum.
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
public void testSHA1() {
putWithChecksum(ChecksumAlgorithm.SHA1);
}

/**
* Puts an object with SHA256 checksum and verifies the server stores and returns it.
* Expected: Put succeeds; HEAD with ChecksumMode.ENABLED returns same content-length, ETag, and SHA256 checksum.
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
public void testSHA256() {
putWithChecksum(ChecksumAlgorithm.SHA256);
}

/**
* Puts an object with CRC64_NVME checksum and verifies the server stores and returns it.
* Expected: Put succeeds; HEAD returns same CRC64_NVME checksum. Skipped by default (requires C runtime).
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
@Ignore("Requires C runtime")
Expand Down Expand Up @@ -99,6 +119,10 @@ private void putWithChecksum(ChecksumAlgorithm checksumAlgorithm) {
assertEquals(String.format("Checksum mismatch (expected: %s, received: %s)", putChecksum, headChecksum), putChecksum, headChecksum);
}

/**
* Puts an object using indefinite-length (chunked) request body with CRC32 checksum.
* Expected: Put completes successfully without requiring a predeclared Content-Length.
*/
@Test
@SkipForQuirks({Quirk.CHECKSUMS_NOT_SUPPORTED})
public void indefiniteLengthWithChecksum() {
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/com/datadobi/s3test/ConditionalRequestTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public class ConditionalRequestTests extends S3TestBase {
public ConditionalRequestTests() throws IOException {
}

/**
* Puts an object, then attempts overwrite with If-None-Match: * (create-only semantics).
* Expected: First put succeeds; second put either fails with 412 Precondition Failed (object exists)
* or 501 if unsupported; if it wrongly succeeds, GET returns new content and test fails.
*/
@Test
@SkipForQuirks({PUT_OBJECT_IF_NONE_MATCH_STAR_NOT_SUPPORTED})
public void thatConditionalPutIfNoneMatchStarWorks() throws InterruptedException {
Expand All @@ -59,6 +64,10 @@ public void thatConditionalPutIfNoneMatchStarWorks() throws InterruptedException
}
}

/**
* Puts an object, then attempts overwrite with If-None-Match: "<etag>" (create-only if etag differs).
* Expected: Overwrite fails with 412 (or 501 if unsupported); object content remains unchanged.
*/
@Test
@SkipForQuirks({PUT_OBJECT_IF_NONE_MATCH_ETAG_NOT_SUPPORTED})
public void thatConditionalPutIfNoneMatchEtagWorks() throws IOException, InterruptedException {
Expand Down Expand Up @@ -110,6 +119,10 @@ public void thatConditionalPutIfNoneMatchEtagWorks() throws IOException, Interru
}
}

/**
* Puts an object, then overwrites with If-Match: "<etag>" (update-only if etag matches).
* Expected: Overwrite succeeds; GET returns new content and new ETag; or 412/501 if precondition fails.
*/
@Test
@SkipForQuirks({PUT_OBJECT_IF_MATCH_ETAG_NOT_SUPPORTED})
public void thatConditionalPutIfMatchEtagWorks() throws IOException, InterruptedException {
Expand Down Expand Up @@ -153,6 +166,16 @@ public void thatConditionalPutIfMatchEtagWorks() throws IOException, Interrupted
"baz"
);

GetObjectResponse getResponse;
String content;
try (var response = bucket.getObject("object")) {
getResponse = response.response();
content = new String(response.readAllBytes(), StandardCharsets.UTF_8);
}
if (!target.hasQuirk(PUT_OBJECT_IF_MATCH_ETAG_NOT_SUPPORTED)) {
assertEquals("bar", content);
assertEquals(overwritePutResponse.eTag(), getResponse.eTag());
}
fail("PutObject using 'If-Match: \"<etag>\"' should fail if object etag does not match current etag");
} catch (S3Exception e) {
// Should have gotten 412 Precondition Failed
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/datadobi/s3test/DeleteObjectTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ public class DeleteObjectTests extends S3TestBase {
public DeleteObjectTests() throws IOException {
}

/**
* Puts an object then deletes it with DeleteObject.
* Expected: Delete succeeds; object is no longer present (no exception).
*/
@Test
public void testDeleteObject() {
bucket.putObject("foo", "Hello, World!");
bucket.deleteObject("foo");
}

/**
* Puts and then deletes an object whose key contains ".." (e.g. "f..o").
* Expected: HEAD confirms object exists; DeleteObject succeeds without error.
*/
@Test
public void testDeleteObjectContainingDotDot() {
var fullContent = "Hello, World!";
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/datadobi/s3test/DeleteObjectsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class DeleteObjectsTests extends S3TestBase {
public DeleteObjectsTests() throws IOException {
}

/**
* Deletes multiple objects by key in a single DeleteObjects request.
* Expected: Both objects are removed; response hasDeleted() is true and deleted list contains "a" and "b".
*/
@Test
public void testDeleteObjectsByKey() {
bucket.putObject("a", "Hello");
Expand All @@ -45,6 +49,10 @@ public void testDeleteObjectsByKey() {
);
}

/**
* Deletes one object using DeleteObjects with a matching ETag condition.
* Expected: Object is deleted; response hasDeleted() is true and deleted list contains "a".
*/
@Test
public void testDeleteObjectsWithMatchingETag() {
bucket.putObject("a", "Hello");
Expand All @@ -59,6 +67,10 @@ public void testDeleteObjectsWithMatchingETag() {
);
}

/**
* Attempts to delete an object with a non-matching ETag (conditional delete).
* Expected: Object is not deleted; response hasDeleted() is false and deleted list is empty.
*/
@Test
public void testDeleteObjectsWithDifferentETag() {
bucket.putObject("a", "Hello");
Expand All @@ -75,6 +87,10 @@ public void testDeleteObjectsWithDifferentETag() {
);
}

/**
* Deletes objects whose keys contain ".." (e.g. "a..b", "c..d") via DeleteObjects.
* Expected: Both objects are deleted; response lists "a..b" and "c..d" in deleted.
*/
@Test
public void testDeleteObjectsContainingDotDot() {
bucket.putObject("a..b", "Hello");
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/com/datadobi/s3test/GetObjectTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public class GetObjectTests extends S3TestBase {
public GetObjectTests() throws IOException {
}

/**
* Puts an object and retrieves it with GET.
* Expected: GetObject returns the full body; content matches original string "Hello, World!".
*/
@Test
public void testGetObject() throws IOException {
var fullContent = "Hello, World!";
Expand All @@ -47,6 +51,10 @@ public void testGetObject() throws IOException {
}
}

/**
* Puts an empty object and retrieves it with GET.
* Expected: GetObject returns 0 bytes; response contentLength is 0.
*/
@Test
public void testGetEmptyObject() throws IOException {
var emptyContent = "";
Expand All @@ -59,6 +67,10 @@ public void testGetEmptyObject() throws IOException {
}
}

/**
* Calls GetObject for a key that does not exist.
* Expected: S3Exception with HTTP status 404 (Not Found).
*/
@Test
public void testGetKeyThatDoesNotExist() throws IOException {
try (var objectInputStream = bucket.getObject("foo")) {
Expand All @@ -68,6 +80,10 @@ public void testGetKeyThatDoesNotExist() throws IOException {
}
}

/**
* Gets a byte range (bytes 7 to end) of an object using Range header.
* Expected: Response body is "World!"; Content-Range and Content-Length match the range.
*/
@Test
public void testGetPartial() throws IOException {
var fullContent = "Hello, World!";
Expand All @@ -89,6 +105,10 @@ public void testGetPartial() throws IOException {
}
}

/**
* Requests a byte range that starts beyond the object size (e.g. bytes 200-).
* Expected: S3Exception with HTTP status 416 (Range Not Satisfiable).
*/
@Test
public void testGetPartialUnsatisfiable() {
var fullContent = "Hello, World!";
Expand All @@ -102,6 +122,10 @@ public void testGetPartialUnsatisfiable() {
}
}

/**
* Gets a byte range with both start and end (bytes 0-4).
* Expected: Response body is "Hello"; Content-Range and Content-Length are correct.
*/
@Test
public void testGetPartialWithEndPosition() throws IOException {
var fullContent = "Hello, World!";
Expand All @@ -123,6 +147,10 @@ public void testGetPartialWithEndPosition() throws IOException {
}
}

/**
* Gets a middle byte range (bytes 7-11) of "Hello, World!".
* Expected: Response body is "World"; Content-Range and Content-Length match.
*/
@Test
public void testGetPartialMiddleRange() throws IOException {
var fullContent = "Hello, World!";
Expand All @@ -144,6 +172,10 @@ public void testGetPartialMiddleRange() throws IOException {
}
}

/**
* Gets the last N bytes using suffix-length range (e.g. bytes=-6 for last 6 bytes).
* Expected: Response body is "World!" (last 6 bytes of "Hello, World!").
*/
@Test
public void testGetPartialLastBytes() throws IOException {
var fullContent = "Hello, World!";
Expand All @@ -158,6 +190,10 @@ public void testGetPartialLastBytes() throws IOException {
}
}

/**
* Requests a range with end position beyond object size (e.g. bytes 10-100).
* Expected: Server returns from start to end of object only; body is "ld!" (bytes 10-12).
*/
@Test
public void testGetPartialBeyondEnd() throws IOException {
var fullContent = "Hello, World!";
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/datadobi/s3test/ListBucketsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public class ListBucketsTests extends S3TestBase {
public ListBucketsTests() throws IOException {
}

/**
* Calls ListBuckets and checks the response Date header.
* Expected: Response includes a "Date" header; parsed value is within ~30 seconds of request time (valid RFC 1123).
*/
@Test
public void listBucketsResponsesShouldReturnValidDateHeader() {
var timeOfRequest = Instant.now();
Expand Down
Loading