diff --git a/tests/rest_scripts/delete_objects.sh b/tests/rest_scripts/delete_objects.sh
new file mode 100755
index 0000000..5355038
--- /dev/null
+++ b/tests/rest_scripts/delete_objects.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+
+# Copyright 2024 Versity Software
+# This file is licensed under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+source ./tests/rest_scripts/rest.sh
+
+# Fields
+
+# shellcheck disable=SC2153,SC2154
+payload="$PAYLOAD"
+# shellcheck disable=SC2153,SC2154
+bucket_name="$BUCKET_NAME"
+has_content_md5="${HAS_CONTENT_MD5:="true"}"
+
+current_date_time=$(date -u +"%Y%m%dT%H%M%SZ")
+payload_hash="$(echo -n "$payload" | sha256sum | awk '{print $1}')"
+if [ "$has_content_md5" == "true" ]; then
+ content_md5=$(echo -n "$payload" | openssl dgst -binary -md5 | openssl base64)
+fi
+
+canonical_request="POST
+/$bucket_name
+delete=
+"
+if [ "$has_content_md5" == "true" ]; then
+ canonical_request+="content-md5:$content_md5
+"
+fi
+canonical_request+="host:$host
+x-amz-content-sha256:$payload_hash
+x-amz-date:$current_date_time
+
+"
+if [ "$has_content_md5" == "true" ]; then
+ canonical_request+="content-md5;"
+fi
+canonical_request+="host;x-amz-content-sha256;x-amz-date
+$payload_hash"
+
+create_canonical_hash_sts_and_signature
+
+curl_command+=(curl -ks -w "\"%{http_code}\"" -X POST "$AWS_ENDPOINT_URL/$bucket_name?delete")
+signed_headers=""
+if [ "$has_content_md5" == "true" ]; then
+ signed_headers+="content-md5;"
+fi
+signed_headers+="host;x-amz-content-sha256;x-amz-date"
+curl_command+=(-H "\"Authorization: AWS4-HMAC-SHA256 Credential=$aws_access_key_id/$year_month_day/$aws_region/s3/aws4_request,SignedHeaders=$signed_headers,Signature=$signature\"")
+curl_command+=(-H "\"Content-Type: application/xml\"")
+if [ "$has_content_md5" == "true" ]; then
+ curl_command+=(-H "\"content-md5: $content_md5\"")
+fi
+curl_command+=(-H "\"x-amz-content-sha256: $payload_hash\""
+-H "\"x-amz-date: $current_date_time\"")
+curl_command+=(-o "$OUTPUT_FILE")
+curl_command+=(-d "\"${payload//\"/\\\"}\"")
+# shellcheck disable=SC2154
+eval "${curl_command[*]}" 2>&1
diff --git a/tests/test_rest.sh b/tests/test_rest.sh
index c66f1d2..7a3c576 100755
--- a/tests/test_rest.sh
+++ b/tests/test_rest.sh
@@ -33,6 +33,7 @@ source ./tests/logger.sh
source ./tests/setup.sh
source ./tests/util/util_acl.sh
source ./tests/util/util_attributes.sh
+source ./tests/util/util_delete_object.sh
source ./tests/util/util_head_object.sh
source ./tests/util/util_legal_hold.sh
source ./tests/util/util_list_buckets.sh
@@ -545,3 +546,53 @@ export RUN_USERS=true
run get_etag_attribute_rest "$BUCKET_ONE_NAME" "$test_file" "$expected_etag"
assert_success
}
+
+@test "REST - POST call on root endpoint" {
+ if [ "$DIRECT" != "true" ]; then
+ skip "https://github.com/versity/versitygw/issues/1036"
+ fi
+ run delete_object_empty_bucket_check_error
+ assert_success
+}
+
+@test "REST - delete objects - no content-md5 header" {
+ if [ "$DIRECT" != "true" ]; then
+ skip "https://github.com/versity/versitygw/issues/1040"
+ fi
+ run setup_bucket "s3api" "$BUCKET_ONE_NAME"
+ assert_success
+
+ run delete_objects_no_content_md5_header "$BUCKET_ONE_NAME"
+ assert_success
+}
+
+@test "REST - delete objects command" {
+ run setup_bucket "s3api" "$BUCKET_ONE_NAME"
+ assert_success
+
+ test_file_one="test_file"
+ test_file_two="test_file_two"
+ run create_test_files "$test_file_one" "$test_file_two"
+ assert_success
+
+ run put_object "s3api" "$TEST_FILE_FOLDER/$test_file_one" "$BUCKET_ONE_NAME" "$test_file_one"
+ assert_success
+
+ run put_object "s3api" "$TEST_FILE_FOLDER/$test_file_two" "$BUCKET_ONE_NAME" "$test_file_two"
+ assert_success
+
+ run verify_object_exists "$BUCKET_ONE_NAME" "$test_file_one"
+ assert_success
+
+ run verify_object_exists "$BUCKET_ONE_NAME" "$test_file_two"
+ assert_success
+
+ run delete_objects_verify_success "$BUCKET_ONE_NAME" "$test_file_one" "$test_file_two"
+ assert_success
+
+ run verify_object_not_found "$BUCKET_ONE_NAME" "$test_file_one"
+ assert_success
+
+ run verify_object_not_found "$BUCKET_ONE_NAME" "$test_file_two"
+ assert_success
+}
\ No newline at end of file
diff --git a/tests/util/util_delete_object.sh b/tests/util/util_delete_object.sh
index 2948eb5..2a9c34c 100644
--- a/tests/util/util_delete_object.sh
+++ b/tests/util/util_delete_object.sh
@@ -15,4 +15,82 @@ block_delete_object_without_permission() {
return 1
fi
return 0
-}
\ No newline at end of file
+}
+
+delete_object_empty_bucket_check_error() {
+ if ! result=$(OUTPUT_FILE="$TEST_FILE_FOLDER/result.txt" COMMAND_LOG="$COMMAND_LOG" BUCKET_NAME="" ./tests/rest_scripts/delete_objects.sh); then
+ log 2 "error deleting objects: $result"
+ return 1
+ fi
+ log 5 "result: $(cat "$TEST_FILE_FOLDER/result.txt")"
+ if ! error=$(xmllint --xpath "Error" "$TEST_FILE_FOLDER/result.txt" 2>&1); then
+ log 2 "error getting XML error data: $error"
+ return 1
+ fi
+ if ! check_xml_element <(echo "$error") "MethodNotAllowed" "Code"; then
+ log 2 "Code mismatch"
+ return 1
+ fi
+ if ! check_xml_element <(echo "$error") "POST" "Method"; then
+ log 2 "Method mismatch"
+ return 1
+ fi
+ if ! check_xml_element <(echo "$error") "SERVICE" "ResourceType"; then
+ log 2 "ResourceType mismatch"
+ return 1
+ fi
+ return 0
+}
+
+delete_objects_no_content_md5_header() {
+ if [ $# -ne 1 ]; then
+ log 2 "delete_objects_no_content_md5_header requires bucket name"
+ return 1
+ fi
+ data="
+
+
+ "
+
+ if ! result=$(OUTPUT_FILE="$TEST_FILE_FOLDER/result.txt" COMMAND_LOG="$COMMAND_LOG" PAYLOAD="$data" BUCKET_NAME="$1" HAS_CONTENT_MD5="false" ./tests/rest_scripts/delete_objects.sh); then
+ log 2 "error deleting objects: $result"
+ return 1
+ fi
+ if [ "$result" != "400" ]; then
+ log 2 "expected response code '400', actual '$result' ($(cat "$TEST_FILE_FOLDER/result.txt")"
+ return 1
+ fi
+ if ! check_xml_element "$TEST_FILE_FOLDER/result.txt" "InvalidRequest" "Error" "Code"; then
+ log 2 "error checking error element"
+ return 1
+ fi
+}
+
+delete_objects_verify_success() {
+ if [ $# -ne 3 ]; then
+ log 2 "'delete_objects_verify_success' requires bucket name, two objects"
+ return 1
+ fi
+ data="
+
+
+"
+
+ if ! result=$(OUTPUT_FILE="$TEST_FILE_FOLDER/result.txt" COMMAND_LOG="$COMMAND_LOG" PAYLOAD="$data" BUCKET_NAME="$1" ./tests/rest_scripts/delete_objects.sh); then
+ log 2 "error deleting objects: $result"
+ return 1
+ fi
+ if [ "$result" != "200" ]; then
+ log 2 "expected '200', was '$result ($(cat "$TEST_FILE_FOLDER/result.txt"))"
+ return 1
+ fi
+ return 0
+}
diff --git a/tests/util/util_head_object.sh b/tests/util/util_head_object.sh
index 82e6dcf..9bbf415 100644
--- a/tests/util/util_head_object.sh
+++ b/tests/util/util_head_object.sh
@@ -87,4 +87,36 @@ get_etag_rest() {
log 5 "head object data: $(cat "$TEST_FILE_FOLDER/head_object.txt")"
etag_value=$(grep "E[Tt]ag:" "$TEST_FILE_FOLDER/head_object.txt" | sed -n 's/E[Tt]ag: "\([^"]*\)"/\1/p' | tr -d '\r')
echo "$etag_value"
-}
\ No newline at end of file
+}
+
+verify_object_not_found() {
+ if [ $# -ne 2 ]; then
+ log 2 "'verify_object_not_found' requires bucket name, object key"
+ return 1
+ fi
+ if ! result=$(OUTPUT_FILE="$TEST_FILE_FOLDER/result.txt" COMMAND_LOG="$COMMAND_LOG" BUCKET_NAME="$1" OBJECT_KEY="$2" ./tests/rest_scripts/head_object.sh); then
+ log 2 "error getting result: $result"
+ return 1
+ fi
+ if [ "$result" != "404" ]; then
+ log 2 "expected '404', was '$result' ($(cat "$TEST_FILE_FOLDER/result.txt"))"
+ return 1
+ fi
+ return 0
+}
+
+verify_object_exists() {
+ if [ $# -ne 2 ]; then
+ log 2 "'verify_object_not_found' requires bucket name, object key"
+ return 1
+ fi
+ if ! result=$(OUTPUT_FILE="$TEST_FILE_FOLDER/result.txt" COMMAND_LOG="$COMMAND_LOG" BUCKET_NAME="$1" OBJECT_KEY="$2" ./tests/rest_scripts/head_object.sh); then
+ log 2 "error getting result: $result"
+ return 1
+ fi
+ if [ "$result" != "200" ]; then
+ log 2 "expected '200', was '$result' ($(cat "$TEST_FILE_FOLDER/result.txt"))"
+ return 1
+ fi
+ return 0
+}