From d038507e0d1cd711691297400cebf8ecb8950e09 Mon Sep 17 00:00:00 2001 From: Luke McCrone Date: Mon, 20 Jul 2026 10:49:47 -0300 Subject: [PATCH] test: website - initial tests --- .github/workflows/system.yml | 2 + tests/drivers/cloudfront.sh | 316 ++++++++++++++++++ tests/drivers/file.sh | 22 +- .../put_bucket_website_rest.sh | 52 +++ tests/env.sh | 6 +- .../command/createBucketCommand.go | 2 +- .../command/putBucketCorsCommand.go | 2 +- .../command/putBucketWebsiteCommand.go | 76 +++++ .../rest_scripts/command/putTaggingCommand.go | 2 +- .../rest_scripts/command/s3RequestBuilder.go | 1 + tests/rest_scripts/generateCommand.go | 9 + tests/setup.sh | 10 +- tests/test_rest_website.sh | 123 +++++++ tests/versity.sh | 6 + 14 files changed, 610 insertions(+), 19 deletions(-) create mode 100644 tests/drivers/cloudfront.sh create mode 100644 tests/drivers/put_bucket_website/put_bucket_website_rest.sh create mode 100644 tests/rest_scripts/command/putBucketWebsiteCommand.go create mode 100755 tests/test_rest_website.sh diff --git a/.github/workflows/system.yml b/.github/workflows/system.yml index 0bfc357f..40fc14c2 100644 --- a/.github/workflows/system.yml +++ b/.github/workflows/system.yml @@ -147,6 +147,8 @@ jobs: TEMPLATE_MATRIX_FILE=$TEMPLATE_MATRIX_FILE QUICK_COMPARE_SIZE=1048576 GENERATE_COMMAND_EXECUTABLE=/home/tester/test-files/generateCommand + WEBSITE_DOMAIN=localhost + WEBSITE=:7071 EOF sudo chown tester:tester /home/tester/test-files/.env sudo chmod 600 /home/tester/test-files/.env diff --git a/tests/drivers/cloudfront.sh b/tests/drivers/cloudfront.sh new file mode 100644 index 00000000..f1e88684 --- /dev/null +++ b/tests/drivers/cloudfront.sh @@ -0,0 +1,316 @@ +#!/usr/bin/env bats + +# Copyright 2026 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. + +get_cloudfront_config() { + if ! check_param_count_v2 "bucket name, root object, website endpoint" 3 $#; then + return 1 + fi + local bucket="$1" root_object="$2" website_endpoint="$3" + local response file_name + + if ! response=$(get_file_name 2>&1); then + log 2 "error getting file name: $response" + return 1 + fi + file_name="$response" + + cat > "$TEST_FILE_FOLDER/$file_name" <&1); then + log 2 "error getting cloudfront config file: $response" + return 1 + fi + config_file="$response" + + if ! response=$(put_cloudfront_user_policy 2>&1); then + log 2 "error putting cloudfront user policy: $response" + return 1 + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront create-distribution \ + --distribution-config "file://${config_file}" --query 'Distribution.[Id,DomainName]' --output text 2>&1); then + log 2 "error creating distribution: $response" + return 1 + fi + read -r distribution_id distribution_domain <<< "$response" + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront wait distribution-deployed --id "$distribution_id" 2>&1); then + log 2 "error waiting for distribution to deploy: $response" + return 1 + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront tag-resource \ + --resource "arn:aws:cloudfront::${DIRECT_AWS_USER_ID}:distribution/${distribution_id}" \ + --tags 'Items=[{Key=Versitygw-Test,Value='"${DIRECT_CLOUDFRONT_TAG}"'}]' 2>&1); then + log 2 "error tagging new distribution: $response" + return 1 + fi + + printf '%s\n' "https://${distribution_domain}" + return 0 +} + +put_cloudfront_user_policy() { + log 6 "put_cloudfront_user_policy" + local response file_name + + if ! response=$(get_file_name 2>&1); then + log 2 "error getting file name: $response" + return 1 + fi + file_name="$response" + + cat > "$TEST_FILE_FOLDER/$file_name" <&1); then + log 2 "error putting user policy: $response" + return 1 + fi + return 0 +} + +delete_tester_created_distributions() { + log 6 "get_user_created_distribution_ids" + if ! check_param_count_v2 "s3 username" 1 $#; then + return 1 + fi + local username="$1" + local response cloudtrail_data distribution_ids + local -a distribution_id_array=() + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudtrail lookup-events \ + --region us-east-1 --lookup-attributes AttributeKey=Username,AttributeValue="${username}" --query 'Events[].CloudTrailEvent' --output text 2>&1); then + log 2 "error getting distribution ids: $response" + return 1 + fi + cloudtrail_data="$response" + + if [ -n "$cloudtrail_data" ]; then + return 0 + fi + + distribution_ids=$(jq -r -s ' + .[] | select(. != null) | try fromjson catch null + | select(. != null and .eventSource == "cloudfront.amazonaws.com") + | select(.eventName == "CreateDistribution" or .eventName == "CreateDistributionWithTags") + | .responseElements.distribution.id + ' <<< "$cloudtrail_data" 2>&1) + log 5 "distribution ids: $distribution_ids" + + mapfile -t distribution_id_array <<< "$distribution_ids" + for id in "${distribution_id_array[@]}"; do + if ! delete_cloudfront_distribution "$id"; then + log 2 "error deleting distribution" + return 1 + fi + done + return 0 +} + +disable_cloudfront_distribution() { + if ! check_param_count_v2 "config JSON, distribution ID, etag" 3 $#; then + return 1 + fi + local config_json="$1" distribution_id="$2" etag="$3" + local response config_with_disable + + if ! response=$(get_file_name 2>&1); then + log 2 "error getting file name: $response" + return 1 + fi + config_with_disable="$TEST_FILE_FOLDER/$response" + + if ! response=$(jq '.DistributionConfig.Enabled = false | .DistributionConfig' <<< "$config_json" > "$config_with_disable" 2>&1); then + log 2 "error locally writing new config with Enabled set to false: $response" + return 1 + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront update-distribution \ + --id "$distribution_id" --if-match "$etag" --distribution-config "file://${config_with_disable}" 2>&1); then + log 2 "error updating distribution config: $response" + return 1 + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront wait distribution-deployed \ + --id "$distribution_id" 2>&1); then + log 2 "error waiting for distribution disability: $response" + return 1 + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront get-distribution-config \ + --id "$distribution_id" --query ETag --output text 2>&1); then + log 2 "error getting delete etag: $response" + return 1 + fi + etag="$response" + echo "$etag" + return 0 +} + +# return 2 for error, 1 for no match, 0 for match +check_for_cloudfront_tag() { + if ! check_param_count_v2 "distribution ID" 1 $#; then + return 2 + fi + local distribution_id="$1" + local distribution_arn response tag_value + + distribution_arn="arn:aws:cloudfront::${DIRECT_AWS_USER_ID}:distribution/${distribution_id}" + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront list-tags-for-resource \ + --resource "$distribution_arn" \ + --query "Tags.Items[?Key=='Versitygw-Test' && Value=='${DIRECT_CLOUDFRONT_TAG}'].Value | [0]" \ + --output text 2>&1); then + log 2 "error checking tag: $response" + return 2 + fi + tag_value="$response" + + if [ "$tag_value" != "$DIRECT_CLOUDFRONT_TAG" ]; then + log 5 "skipping distribution with ID '${distribution_id}' (not tagged '$DIRECT_CLOUDFRONT_TAG')" + return 1 + fi + return 0 +} + +delete_cloudfront_distribution() { + log 6 "delete_cloudfront_distribution: '$1'" + if ! check_param_count_v2 "distribution ID" 1 $#; then + return 1 + fi + local distribution_id="$1" + local response config_json enabled etag tag_check_result=0 config_with_disable deletion_etag + + if [ -z "$distribution_id" ] || [ "$distribution_id" == "null" ]; then + return 0 + fi + if ! response="$(env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront get-distribution-config --id "$distribution_id" 2>&1)"; then + if [[ "$response" == *"The specified distribution does not exist"* ]]; then + return 0 + fi + log 2 "error getting config for distribution with ID '$distribution_id': $response" + return 1 + fi + config_json="$response" + enabled="$(jq -r '.DistributionConfig.Enabled' <<< "$config_json")" + etag="$(jq -r '.ETag' <<< "$config_json")" + + check_for_cloudfront_tag "$distribution_id" || tag_check_result=$? + if [ "$tag_check_result" -eq 2 ]; then + log 2 "error checking for cloudfront tag" + return 1 + elif [ "$tag_check_result" -eq 1 ]; then + return 0 + fi + + if [ "$enabled" == "true" ]; then + if ! response=$(disable_cloudfront_distribution "$config_json" "$distribution_id" "$etag" 2>&1); then + log 2 "error deleting cloudfront distribution with id '$distribution_id': $response" + return 1 + fi + deletion_etag="$response" + else + deletion_etag="$etag" + fi + + if ! response=$(send_command env AWS_IGNORE_CONFIGURED_ENDPOINT_URLS=true aws cloudfront delete-distribution \ + --id "$distribution_id" --if-match "$deletion_etag" 2>&1); then + log 2 "error deleting distribution: $response" + return 1 + fi + + log 5 "Deleted $distribution_id" + return 0 +} diff --git a/tests/drivers/file.sh b/tests/drivers/file.sh index 975d81e0..852e4aff 100644 --- a/tests/drivers/file.sh +++ b/tests/drivers/file.sh @@ -713,17 +713,17 @@ create_test_file_base() { } create_and_split_large_file() { -if ! check_param_count_v2 "file name, size in MB, pieces" 3 $#; then - return 1 -fi -if ! error=$(create_large_file_with_size "$1" "$2" 2>&1); then - log 2 "error creating large file: $error" - return 1 -fi -if ! split_file "$TEST_FILE_FOLDER/$1" "$3"; then - log 2 "error splitting file" - return 1 -fi + if ! check_param_count_v2 "file name, size in MB, pieces" 3 $#; then + return 1 + fi + if ! error=$(create_large_file_with_size "$1" "$2" 2>&1); then + log 2 "error creating large file: $error" + return 1 + fi + if ! split_file "$TEST_FILE_FOLDER/$1" "$3"; then + log 2 "error splitting file" + return 1 + fi } # param: number of files diff --git a/tests/drivers/put_bucket_website/put_bucket_website_rest.sh b/tests/drivers/put_bucket_website/put_bucket_website_rest.sh new file mode 100644 index 00000000..a43c3aa7 --- /dev/null +++ b/tests/drivers/put_bucket_website/put_bucket_website_rest.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Copyright 2026 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/drivers/create_bucket/create_bucket_rest.sh +source ./tests/drivers/string.sh + +create_website_with_random_string() { + if ! check_param_count_v2 "bucket name" 1 $#; then + return 1 + fi + local bucket_name="$1" + local response file_name test_string + + if ! response=$(get_file_name 2>&1); then + log 2 "error getting file name: $response" + return 1 + fi + file_name="$response" + + if ! response=$(generate_random_string 8 10 2>&1); then + log 2 "error generating random string: $response" + return 1 + fi + test_string="$response" + echo "$test_string" > "$TEST_FILE_FOLDER/$file_name" + + if ! response=$(put_object_rest "$TEST_FILE_FOLDER/$file_name" "$bucket_name" "$file_name" 2>&1); then + log 2 "error putting random string file: $response" + return 1 + fi + + if ! response=$(send_rest_go_command "200" "-commandType" "putBucketWebsiteConfiguration" "-bucketName" "$bucket_name" \ + "-websiteConfiguration" "{\"IndexDocument\":{\"Suffix\":\"$file_name\"}}" 2>&1); then + log 2 "error putting website configuration: $response" + return 1 + fi + printf '%s\n' "$test_string" + return 0 +} \ No newline at end of file diff --git a/tests/env.sh b/tests/env.sh index 8c85f756..fc9cd19c 100644 --- a/tests/env.sh +++ b/tests/env.sh @@ -366,15 +366,15 @@ check_user_profile_and_add_if_needed() { fi done if ! response=$(aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID" --profile "$AWS_PROFILE" 2>&1); then - log 2 "error calculating response: $response" + log 2 "error adding aws access key id to profile '$AWS_PROFILE': $response" return 1 fi if ! response=$(aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" --profile "$AWS_PROFILE" 2>&1); then - log 2 "error calculating response: $response" + log 2 "error adding aws secret access key to profile '$AWS_PROFILE': $response" return 1 fi if ! response=$(aws configure set aws_region "$AWS_REGION" --profile "$AWS_PROFILE" 2>&1); then - log 2 "error calculating response: $response" + log 2 "error adding aws region to profile '$AWS_PROFILE': $response" return 1 fi return 0 diff --git a/tests/rest_scripts/command/createBucketCommand.go b/tests/rest_scripts/command/createBucketCommand.go index 182ef964..6761c049 100644 --- a/tests/rest_scripts/command/createBucketCommand.go +++ b/tests/rest_scripts/command/createBucketCommand.go @@ -39,7 +39,7 @@ func NewCreateBucketCommand(s3Command *S3RequestBuilder, locationConstraint stri if err != nil { return nil, fmt.Errorf("error marshalling XML: %w", err) } - s3Command.Config.Payload = "\n" + string(xmlData) + s3Command.Config.Payload = xml.Header + string(xmlData) } return command, nil } diff --git a/tests/rest_scripts/command/putBucketCorsCommand.go b/tests/rest_scripts/command/putBucketCorsCommand.go index ea178081..395576df 100644 --- a/tests/rest_scripts/command/putBucketCorsCommand.go +++ b/tests/rest_scripts/command/putBucketCorsCommand.go @@ -50,7 +50,7 @@ func NewPutBucketCORSCommand(command *S3RequestBuilder, ruleStrings []string) (* if err != nil { return nil, fmt.Errorf("error marshalling XML: %w", err) } - command.Config.Payload = "\n" + string(xmlData) + command.Config.Payload = xml.Header + string(xmlData) return command, nil } diff --git a/tests/rest_scripts/command/putBucketWebsiteCommand.go b/tests/rest_scripts/command/putBucketWebsiteCommand.go new file mode 100644 index 00000000..5c0a60f5 --- /dev/null +++ b/tests/rest_scripts/command/putBucketWebsiteCommand.go @@ -0,0 +1,76 @@ +package command + +import ( + "encoding/json" + "encoding/xml" + "fmt" +) + +type ErrorDocument struct { + Key string `xml:"Key,omitempty" json:"key,omitempty"` +} + +type IndexDocument struct { + Suffix string `xml:"Suffix,omitempty" json:"suffix,omitempty"` +} + +type RedirectAllRequestsTo struct { + HostName string `xml:"HostName,omitempty" json:"hostName,omitempty"` + Protocol string `xml:"Protocol,omitempty" json:"protocol,omitempty"` +} + +type Condition struct { + HttpErrorCodeReturnedEquals string `xml:"HttpErrorCodeReturnedEquals,omitempty" json:"httpErrorCodeReturnedEquals,omitempty"` + KeyPrefixEquals string `xml:"KeyPrefixEquals,omitempty" json:"keyPrefixEquals,omitempty"` +} + +type Redirect struct { + HostName string `xml:"HostName,omitempty" json:"hostName,omitempty"` + HttpRedirectCode string `xml:"HttpRedirectCode,omitempty" json:"httpRedirectCode,omitempty"` + Protocol string `xml:"Protocol,omitempty" json:"protocol,omitempty"` + ReplaceKeyPrefixWith string `xml:"ReplaceKeyPrefixWith,omitempty" json:"replaceKeyPrefixWith,omitempty"` + ReplaceKeyWith string `xml:"ReplaceKeyWith,omitempty" json:"replaceKeyWith,omitempty"` +} + +type RoutingRule struct { + Condition *Condition `xml:"Condition,omitempty" json:"condition,omitempty"` + Redirect *Redirect `xml:"Redirect,omitempty" json:"redirect,omitempty"` +} + +type RoutingRules struct { + RoutingRule []RoutingRule `xml:"RoutingRule,omitempty" json:"routingRule,omitempty"` +} + +type WebsiteConfiguration struct { + XMLName xml.Name `xml:"WebsiteConfiguration,omitempty" json:"-"` + XMLNamespace string `xml:"xmlns,attr" json:"xmlNamespace,omitempty"` + ErrorDocument *ErrorDocument `xml:"ErrorDocument,omitempty" json:"errorDocument,omitempty"` + IndexDocument *IndexDocument `xml:"IndexDocument,omitempty" json:"indexDocument,omitempty"` + RedirectAllRequestsTo *RedirectAllRequestsTo `xml:"RedirectAllRequestsTo,omitempty" json:"redirectAllRequestsTo,omitempty"` + RoutingRules *RoutingRules `xml:"RoutingRules,omitempty" json:"routingRules,omitempty"` +} + +func NewPutBucketWebsiteCommand(command *S3RequestBuilder, websiteString string) (*S3RequestBuilder, error) { + command.Config.Method = "PUT" + command.Config.Query = "website" + xmlData, err := convertWebsiteJsonToXml(websiteString) + if err != nil { + return nil, fmt.Errorf("error assembling website configuration: %w", err) + } + command.Config.Payload = xml.Header + string(xmlData) + return command, nil +} + +func convertWebsiteJsonToXml(websiteString string) ([]byte, error) { + websiteConfiguration := WebsiteConfiguration{ + XMLNamespace: "https://s3.amazonaws.com/doc/2006-03-01/", + } + if err := json.Unmarshal([]byte(websiteString), &websiteConfiguration); err != nil { + return nil, fmt.Errorf("error unmarshaling website string: %w", err) + } + xmlData, err := xml.Marshal(websiteConfiguration) + if err != nil { + return nil, fmt.Errorf("error marshalling XML: %w", err) + } + return xmlData, nil +} diff --git a/tests/rest_scripts/command/putTaggingCommand.go b/tests/rest_scripts/command/putTaggingCommand.go index 4a1652a6..91997215 100644 --- a/tests/rest_scripts/command/putTaggingCommand.go +++ b/tests/rest_scripts/command/putTaggingCommand.go @@ -40,6 +40,6 @@ func (p *PutTaggingCommand) createTaggingPayload(fields *TaggingFields) error { if err != nil { return fmt.Errorf("error marshalling XML: %w", err) } - p.Config.Payload = "\n" + string(xmlData) + p.Config.Payload = xml.Header + string(xmlData) return nil } diff --git a/tests/rest_scripts/command/s3RequestBuilder.go b/tests/rest_scripts/command/s3RequestBuilder.go index 3ac8023b..8acbe524 100644 --- a/tests/rest_scripts/command/s3RequestBuilder.go +++ b/tests/rest_scripts/command/s3RequestBuilder.go @@ -86,6 +86,7 @@ type S3RequestConfigData struct { OutputFile string HeaderFile string AddressFormat string + WebsiteConfiguration string } type S3RequestBuilder struct { diff --git a/tests/rest_scripts/generateCommand.go b/tests/rest_scripts/generateCommand.go index 82065d15..ca27de68 100644 --- a/tests/rest_scripts/generateCommand.go +++ b/tests/rest_scripts/generateCommand.go @@ -16,6 +16,7 @@ const ( DeleteObjects = "deleteObjects" PutBucketCors = "putBucketCORS" PutBucketTagging = "putBucketTagging" + PutBucketWebsite = "putBucketWebsiteConfiguration" PutObject = "putObject" PutObjectTagging = "putObjectTagging" ) @@ -84,6 +85,8 @@ var writeXMLPayloadToFile *string var addressFormat *string +var websiteConfiguration *string + func (r *restParams) String() string { return fmt.Sprintf("%v", *r) } @@ -161,6 +164,7 @@ func main() { OutputFile: *outputFile, HeaderFile: *headerFile, AddressFormat: *addressFormat, + WebsiteConfiguration: *websiteConfiguration, }, } @@ -198,6 +202,10 @@ func getS3CommandType(baseCommand *command.S3RequestBuilder) (command.S3CommandC if s3Command, err = command.NewPutBucketTaggingCommand(baseCommand, &fields); err != nil { return nil, fmt.Errorf("error setting up PutBucketTagging command: %w", err) } + case PutBucketWebsite: + if s3Command, err = command.NewPutBucketWebsiteCommand(baseCommand, *websiteConfiguration); err != nil { + return nil, fmt.Errorf("error setting up PutBucketWebsiteConfiguration commmand: %w", err) + } case PutObject: if s3Command, err = command.NewPutObjectCommand(baseCommand); err != nil { return nil, fmt.Errorf("error setting up PutObject command: %w", err) @@ -286,6 +294,7 @@ func checkFlags() error { flag.Var(&objectsToDelete, "objectsToDelete", "Objects to delete in DeleteObjects command (can add multiple)") deleteObjectsQuietMode = flag.Bool("deleteObjectsQuietMode", false, "Quiet mode for DeleteObjects command") addressFormat = flag.String("addressFormat", command.AddressFormatPath, "S3 command address format ('path' or 'virtual')") + websiteConfiguration = flag.String("websiteConfiguration", "", "JSON configuration for S3 bucket website") // Parse the flags flag.Parse() diff --git a/tests/setup.sh b/tests/setup.sh index e8a98c2f..83de9696 100644 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -111,6 +111,7 @@ setup() { fi export AWS_PROFILE + log 4 "********** END SETUP **********" return 0 } @@ -143,17 +144,22 @@ bucket_and_user_cleanup() { if [ "$SKIP_USERS_TESTS" != "true" ]; then if [ -n "$USERNAME_ONE" ]; then if user_exists "$USERNAME_ONE" && ! delete_user "$USERNAME_ONE"; then - log 3 "error deleting user $USERNAME_ONE" + log 3 "error deleting user '$USERNAME_ONE'" fi fi if [ -n "$USERNAME_TWO" ]; then if user_exists "$USERNAME_TWO" && ! delete_user "$USERNAME_TWO"; then - log 3 "error deleting user $USERNAME_TWO" + log 3 "error deleting user '$USERNAME_TWO'" fi fi fi if [ "$AUTOGENERATE_USERS" == "true" ] && ! delete_autogenerated_users; then log 3 "error deleting autocreated users" fi + if [ "$DIRECT" == "true" ] && [ -n "$distribution_created" ] && [ "$distribution_created" == "true" ]; then + if ! delete_tester_created_distributions "$USER_ID_USER_1"; then + log 3 "error deleting test-created S3 distributions" + fi + fi return 0 } diff --git a/tests/test_rest_website.sh b/tests/test_rest_website.sh new file mode 100755 index 00000000..3b82effb --- /dev/null +++ b/tests/test_rest_website.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bats + +# Copyright 2026 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. + +load ./bats-support/load +load ./bats-assert/load + +source ./tests/commands/put_public_access_block.sh +source ./tests/drivers/create_bucket/create_bucket_rest.sh +source ./tests/drivers/put_bucket_website/put_bucket_website_rest.sh +source ./tests/drivers/cloudfront.sh +source ./tests/drivers/string.sh +source ./tests/setup.sh + +@test "PutBucketWebsite - empty payload" { + local bucket_name + + run setup_bucket_v3 "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run send_rest_go_command_expect_error "400" "MissingRequestBodyError" "Request Body is empty" "-bucketName" "$bucket_name" "-method" "PUT" "-query" "website" + assert_success +} + +@test "PutBucketWebsite - XML message with empty WebsiteConfiguration struct" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/2260" + fi + local bucket_name + + run setup_bucket_v3 "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run send_rest_go_command_expect_error "400" "InvalidArgument" "A value for IndexDocument Suffix must be provided if RedirectAllRequestsTo is empty" \ + "-commandType" "putBucketWebsiteConfiguration" "-bucketName" "$bucket_name" "-websiteConfiguration" "{}" + assert_success +} + +@test "PutBucketWebsite - HTTP call with HTTPS enabled does not return empty response" { + if [ "$DIRECT" != "true" ]; then + skip "https://github.com/versity/versitygw/issues/2261" + fi + distribution_created=false + + local bucket_name policy_file distribution_domain http_domain + + run setup_bucket_v3 "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run create_website_with_random_string "$bucket_name" + assert_success + random_string="$output" + + if [ "$DIRECT" == "true" ]; then + run put_public_access_block "$bucket_name" "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false" + assert_success + fi + + run setup_policy_with_single_statement_v2 "2012-10-17" "Allow" "*" "s3:GetObject" "arn:aws:s3:::$bucket_name/*" + assert_success + policy_file="$output" + + run put_bucket_policy "rest" "$bucket_name" "$TEST_FILE_FOLDER"/"$policy_file" + assert_success + + if [ "$DIRECT" == "true" ]; then + run create_cloudfront_distribution "$bucket_name" "index.html" "${bucket_name}.s3-website.us-east-1.amazonaws.com" + assert_success + # shellcheck disable=SC2034 + distribution_created=true + distribution_domain="$output" + http_domain="${distribution_domain/https:\/\//http:\/\/}" + else + http_domain="${AWS_ENDPOINT_URL/https:\/\//http:\/\/}" + fi + + run curl -ks "${http_domain}" + assert_success + assert_output -p "301 Moved Permanently" +} + +@test "PutBucketWebsite - IndexDocument suffix" { + local bucket_name policy_file random_string + + run setup_bucket_v3 "$BUCKET_ONE_NAME" + assert_success + bucket_name="$output" + + run create_website_with_random_string "$bucket_name" + assert_success + random_string="$output" + + if [ "$DIRECT" == "true" ]; then + run put_public_access_block "$bucket_name" "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false" + assert_success + fi + + run setup_policy_with_single_statement_v2 "2012-10-17" "Allow" "*" "s3:GetObject" "arn:aws:s3:::$bucket_name/*" + assert_success + policy_file="$output" + + run put_bucket_policy "rest" "$bucket_name" "$TEST_FILE_FOLDER"/"$policy_file" + assert_success + + run curl -ks "https://${bucket_name}.${WEBSITE_DOMAIN}${WEBSITE}" + assert_success + assert_output "$random_string" +} diff --git a/tests/versity.sh b/tests/versity.sh index 87f512a4..bca9df91 100644 --- a/tests/versity.sh +++ b/tests/versity.sh @@ -115,6 +115,12 @@ run_versity_app_posix() { if [ -n "$PORT" ]; then base_command+=(--port ":$PORT") fi + if [ -n "$WEBSITE" ]; then + base_command+=(--website "$WEBSITE") + fi + if [ -n "$WEBSITE_DOMAIN" ]; then + base_command+=(--website-domain "$WEBSITE_DOMAIN") + fi base_command+=(posix) if [ -n "$VERSIONING_DIR" ]; then base_command+=(--versioning-dir "$VERSIONING_DIR")