diff --git a/src/update_handler.cpp b/src/update_handler.cpp index b2cbca0..48c519d 100644 --- a/src/update_handler.cpp +++ b/src/update_handler.cpp @@ -120,10 +120,16 @@ void UpdateHandler::handle_update_object(const drogon::HttpRequestPtr& req, std: return; } - // Prepare updated entry (keep hash and labeltags, update metadata) + // Prepare updated entry dbEntry updated_entry = entry; updated_entry.metadata = new_metadata; - // Ensure labeltags and hash are preserved in metadata + + // Update labeltags if provided in the new metadata + if (new_metadata.contains("labeltags") && new_metadata["labeltags"].is_array()) { + updated_entry.labeltags = new_metadata["labeltags"].get>(); + } + + // Ensure labeltags and hash are reflected in metadata updated_entry.metadata["labeltags"] = updated_entry.labeltags; updated_entry.metadata["hash"] = updated_entry.hash; diff --git a/testing/test.sh b/testing/test.sh index 44161bf..27a9531 100755 --- a/testing/test.sh +++ b/testing/test.sh @@ -42,49 +42,52 @@ cat << EOF EOF -# Test 0: Verify the script is running -title "0: Verify the server is running" + +function test0() { + # Test 0: Verify the script is running + title "0: Verify the server is running" + + # test jq is installed + if ! command -v jq &> /dev/null; then + echo "jq could not be found" + echo "sudo apt-get install jq" + exit 1 + fi + + # read sos_config.json + CONFIG_PATH="${SCRIPT_DIR}/sos_config.json" + if [ ! -f "${CONFIG_PATH}" ]; then + echo "config file not found at ${CONFIG_PATH}" + exit 1 + fi + CONFIG=$(cat "${CONFIG_PATH}") + + # extract the first write token from the config + WRITE_TOKEN=$(echo "$CONFIG" | jq -r '.write_tokens[0]') + + BASE_TAG="autotest" + + if ! command -v wget &> /dev/null; then + echo "wget could not be found" + exit 1 + fi + + # test if server is running + if ! wget -qO- "${HOSTURL}/status" | jq -r '.result' | grep -q 'success'; then + wget -O - "${HOSTURL}/status" + die "server is not running on ${HOSTURL}" + fi + + echo "Simple Object Storage server is running at ${HOSTURL}" +} -# test jq is installed -if ! command -v jq &> /dev/null; then - echo "jq could not be found" - echo "sudo apt-get install jq" - exit 1 -fi +function test1() { + # test every action in the README.md file, leaving the system in the same state it was found + # and print the output of each action -# read sos_config.json -CONFIG_PATH="${SCRIPT_DIR}/sos_config.json" -if [ ! -f "${CONFIG_PATH}" ]; then - echo "config file not found at ${CONFIG_PATH}" - exit 1 -fi -CONFIG=$(cat "${CONFIG_PATH}") - -# extract the first write token from the config -WRITE_TOKEN=$(echo "$CONFIG" | jq -r '.write_tokens[0]') - -BASE_TAG="autotest" - -if ! command -v wget &> /dev/null; then - echo "wget could not be found" - exit 1 -fi - -# test if server is running -if ! wget -qO- "${HOSTURL}/status" | jq -r '.result' | grep -q 'success'; then - wget -O - "${HOSTURL}/status" - - die "server is not running on ${HOSTURL}" -fi - -echo "Simple Object Storage server is running at ${HOSTURL}" - - # test every action in the README.md file, leaving the system in the same state it was found - # and print the output of each action - -# Construct metadata JSON -METADATA_JSON=$(cat </dev/null 2>&1; then - die "Invalid JSON response from upload: ${UPLOAD_RESPONSE}" -fi + if ! echo "${UPLOAD_RESPONSE}" | jq -e . >/dev/null 2>&1; then + die "Invalid JSON response from upload: ${UPLOAD_RESPONSE}" + fi -OBJECT_HASH=$(echo "${UPLOAD_RESPONSE}" | jq -r '.hash') -echo "Received hash: ${OBJECT_HASH}" + OBJECT_HASH=$(echo "${UPLOAD_RESPONSE}" | jq -r '.hash') + echo "Received hash: ${OBJECT_HASH}" -# Verify the object exists -echo "Verifying object exists..." -EXISTS_RESPONSE=$(curl -s "${HOSTURL}/exists/${BASE_TAG}:test2") -echo "Exists response: ${EXISTS_RESPONSE}" + # Verify the object exists + echo "Verifying object exists..." + EXISTS_RESPONSE=$(curl -s "${HOSTURL}/exists/${BASE_TAG}:test2") + echo "Exists response: ${EXISTS_RESPONSE}" -# Get metadata and verify extra fields are preserved -echo "Retrieving metadata for ${BASE_TAG}:test2" -METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${BASE_TAG}:test2") -CURL_EXIT_CODE=$? -echo "Curl exit code: ${CURL_EXIT_CODE}" -echo "Full metadata response: ${METADATA_RESPONSE}" + # Get metadata and verify extra fields are preserved + echo "Retrieving metadata for ${BASE_TAG}:test2" + METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${BASE_TAG}:test2") + CURL_EXIT_CODE=$? + echo "Curl exit code: ${CURL_EXIT_CODE}" + echo "Full metadata response: ${METADATA_RESPONSE}" -if [ ${CURL_EXIT_CODE} -ne 0 ]; then - die "Failed to retrieve metadata: curl returned ${CURL_EXIT_CODE}" -fi + if [ ${CURL_EXIT_CODE} -ne 0 ]; then + die "Failed to retrieve metadata: curl returned ${CURL_EXIT_CODE}" + fi -if ! echo "${METADATA_RESPONSE}" | jq -e . >/dev/null 2>&1; then - die "Invalid JSON response: ${METADATA_RESPONSE}" -fi + if ! echo "${METADATA_RESPONSE}" | jq -e . >/dev/null 2>&1; then + die "Invalid JSON response: ${METADATA_RESPONSE}" + fi -if ! echo "${METADATA_RESPONSE}" | jq -r '.metadata.extra_field1' | grep -q 'value1'; then - die "extra_field1 not preserved in metadata" -fi -if ! echo "${METADATA_RESPONSE}" | jq -r '.metadata.extra_field2' | grep -q 'value2'; then - die "extra_field2 not preserved in metadata" -fi + if ! echo "${METADATA_RESPONSE}" | jq -r '.metadata.extra_field1' | grep -q 'value1'; then + die "extra_field1 not preserved in metadata" + fi + if ! echo "${METADATA_RESPONSE}" | jq -r '.metadata.extra_field2' | grep -q 'value2'; then + die "extra_field2 not preserved in metadata" + fi +} -#------------------------------------------------------------------------------------------------ -title "6: Test tag versioning behavior" +function test6() { + #------------------------------------------------------------------------------------------------ + title "6: Test tag versioning behavior" -# Clean up -curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${OBJECT_HASH}" > /dev/null + # Clean up + curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${OBJECT_HASH}" > /dev/null -# Upload first version with tag 'latest' -FIRST_METADATA_JSON=$(cat < /dev/null -curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${SECOND_HASH}" > /dev/null - -# Use a known invalid token -INVALID_TOKEN="invalid_token" - -# Make 5 requests with an invalid token -for i in {1..5}; do - echo "Attempt $i with invalid token" - RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${INVALID_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") - echo "Response: ${RESPONSE}" -done - -# Now try a request with a valid token - should be rate limited -echo "Attempting request with valid token (should be rate limited)" -RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${WRITE_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") -if ! echo "${RESPONSE}" | jq -r '.error' | grep -q "Too many authentication attempts"; then - die "Expected rate limit error, got: ${RESPONSE}" -fi - -echo "Sleeping for 3 seconds to allow rate limit to reset" -echo "(Normally 5 mins, but we set to 2s for this test!)" - -sleep 3 -# Now try a request with a valid token - should be rate limited -echo "Attempting request with valid token (should NOT be rate limited)" -RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${WRITE_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") - -#------------------------------------------------------------------------------------------------ -title "8: Test update endpoint" - -# First upload a test file -UPLOAD_RESPONSE=$(curl -s -X PUT \ - -H "Authorization: Bearer ${WRITE_TOKEN}" \ - -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" \ - -F "metadata={\"labeltags\":[\"test:update\"]}" \ - "${HOSTURL}/upload") - -HASH=$(echo "${UPLOAD_RESPONSE}" | jq -r '.hash') -if [ -z "${HASH}" ] || [ "${HASH}" = "null" ]; then - die "Failed to upload test file for update test" -fi - -# Test 8.1: Update metadata using JSON -UPDATED_METADATA='{"labeltags":["test:updated", "version:1.0"], "new_field":"test_value"}' -UPDATE_RESPONSE=$(curl -s -X PUT \ - -H "Authorization: Bearer ${WRITE_TOKEN}" \ - -H "Content-Type: application/json" \ - -d "{\"hash\":\"${HASH}\", \"metadata\":${UPDATED_METADATA}}" \ - "${HOSTURL}/update") - -echo "Update response: ${UPDATE_RESPONSE}" -if [ "$(echo "${UPDATE_RESPONSE}" | jq -r '.result')" != "success" ]; then - die "Failed to update metadata via JSON" -fi - -# Verify the update -UPDATED_METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${HASH}") -if ! echo "${UPDATED_METADATA_RESPONSE}" | jq -e '.metadata.new_field == "test_value"' | grep -q true; then - die "Metadata was not updated correctly via JSON" -fi - -# Test 8.2: Update metadata using form data -# Update with form data using raw JSON string -UPDATE_FORM_RESPONSE=$(curl -s -X PUT \ - -H "Authorization: Bearer ${WRITE_TOKEN}" \ - -F "hash=${HASH}" \ - -F 'metadata={"labeltags":["test:form_updated"], "form_field":"form_value"}' \ - "${HOSTURL}/update") - -echo "Form update response: ${UPDATE_FORM_RESPONSE}" -if [ "$(echo "${UPDATE_FORM_RESPONSE}" | jq -r '.result')" != "success" ]; then - die "Failed to update metadata via form data" -fi - -# Verify the form update -UPDATED_FORM_METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${HASH}") -if ! echo "${UPDATED_FORM_METADATA_RESPONSE}" | jq -e '.metadata.form_field == "form_value"' | grep -q true; then - die "Metadata was not updated correctly via form data" -fi - -# Test 8.3: Test error cases -# Missing hash -MISSING_HASH_RESPONSE=$(curl -s -X PUT \ - -H "Authorization: Bearer ${WRITE_TOKEN}" \ - -d '{"metadata":{}}' \ - "${HOSTURL}/update") -if [ "$(echo "${MISSING_HASH_RESPONSE}" | jq -r '.error')" != "Missing 'hash' or 'metadata' field in request body" ]; then - die "Expected error for missing hash, got: ${MISSING_HASH_RESPONSE}" -fi - -# Missing metadata -MISSING_METADATA_RESPONSE=$(curl -s -X PUT \ - -H "Authorization: Bearer ${WRITE_TOKEN}" \ - -d "{\"hash\":\"${HASH}\"}" \ - "${HOSTURL}/update") -if [ "$(echo "${MISSING_METADATA_RESPONSE}" | jq -r '.error')" != "Missing 'hash' or 'metadata' field in request body" ]; then - die "Expected error for missing metadata, got: ${MISSING_METADATA_RESPONSE}" -fi - -# Clean up -curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${HASH}" > /dev/null -if echo "${RESPONSE}" | jq -r '.error' | grep -q "Too many authentication attempts"; then - die "Expected no rate limit error, got: ${RESPONSE}" -fi - -curl -s "${HOSTURL}/meta/${HASH}" | jq - -# delete the object (only if it exists) -TODELHASH=$(curl -s "${HOSTURL}/hash/test:form_updated" | jq -r '.hash') -if [ "${TODELHASH}" != "null" ] && [ -n "${TODELHASH}" ]; then - echo "deleting test:form_updated ${TODELHASH}" - if ! curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${TODELHASH}" | jq -r '.result' | grep -q 'success'; then - die "failed to delete ${TODELHASH}" + # Verify first version's metadata still has v1 tag + FIRST_METADATA=$(curl -s "${HOSTURL}/meta/${FIRST_HASH}") + echo "First version metadata response: ${FIRST_METADATA}" + if ! echo "${FIRST_METADATA}" | jq -r '.metadata.labeltags[]' | grep -q "${BASE_TAG}:v1"; then + die "First version does not have v1 tag" fi -else - echo "test:form_updated not found, skipping deletion" - echo "HASH=${HASH}, TODELHASH=${TODELHASH}" -fi + + # Verify first version's metadata no longer has the latest tag + if echo "${FIRST_METADATA}" | jq -r '.metadata.labeltags[]' | grep -q "${BASE_TAG}:latest"; then + die "First version still has latest tag" + fi + + # Verify second version has the correct tags: v2 and latest + SECOND_METADATA=$(curl -s "${HOSTURL}/meta/${SECOND_HASH}") + echo "Second version metadata response: ${SECOND_METADATA}" + if ! echo "${SECOND_METADATA}" | jq -r '.metadata.labeltags[]' | grep -q "${BASE_TAG}:latest"; then + die "Second version does not have latest tag" + fi + if ! echo "${SECOND_METADATA}" | jq -r '.metadata.labeltags[]' | grep -q "${BASE_TAG}:v2"; then + die "Second version does not have v2 tag" + fi +} + +function test7() { + #------------------------------------------------------------------------------------------------ + title "7: Test rate limiting behavior" + + # Clean up + curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${FIRST_HASH}" > /dev/null + curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${SECOND_HASH}" > /dev/null + + # Use a known invalid token + INVALID_TOKEN="invalid_token" + + # Make 5 requests with an invalid token + for i in {1..5}; do + echo "Attempt $i with invalid token" + RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${INVALID_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") + echo "Response: ${RESPONSE}" + done + + # Now try a request with a valid token - should be rate limited + echo "Attempting request with valid token (should be rate limited)" + RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${WRITE_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") + if ! echo "${RESPONSE}" | jq -r '.error' | grep -q "Too many authentication attempts"; then + die "Expected rate limit error, got: ${RESPONSE}" + fi + + echo "Sleeping for 3 seconds to allow rate limit to reset" + echo "(Normally 5 mins, but we set to 2s for this test!)" + + sleep 3 + # Now try a request with a valid token - should be rate limited + echo "Attempting request with valid token (should NOT be rate limited)" + RESPONSE=$(curl -s -X PUT -H "Authorization: Bearer ${WRITE_TOKEN}" -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" -F "metadata={\"labeltags\":[\"test:latest\"]}" "${HOSTURL}/upload") +} + +function test8() { + #------------------------------------------------------------------------------------------------ + title "8: Test update endpoint" + + # First upload a test file + UPLOAD_RESPONSE=$(curl -s -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" \ + -F "metadata={\"labeltags\":[\"test:update\"]}" \ + "${HOSTURL}/upload") + + HASH=$(echo "${UPLOAD_RESPONSE}" | jq -r '.hash') + if [ -z "${HASH}" ] || [ "${HASH}" = "null" ]; then + die "Failed to upload test file for update test" + fi + + # Test 8.1: Update metadata using JSON + UPDATED_METADATA='{"labeltags":["test:updated", "version:1.0"], "new_field":"test_value"}' + UPDATE_RESPONSE=$(curl -s -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"hash\":\"${HASH}\", \"metadata\":${UPDATED_METADATA}}" \ + "${HOSTURL}/update") + + echo "Update response: ${UPDATE_RESPONSE}" + if [ "$(echo "${UPDATE_RESPONSE}" | jq -r '.result')" != "success" ]; then + die "Failed to update metadata via JSON" + fi + + # Verify the update + UPDATED_METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${HASH}") + if ! echo "${UPDATED_METADATA_RESPONSE}" | jq -e '.metadata.new_field == "test_value"' | grep -q true; then + die "Metadata was not updated correctly via JSON" + fi + + # Test 8.2: Update metadata using form data + # Update with form data using raw JSON string + UPDATE_FORM_RESPONSE=$(curl -s -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -F "hash=${HASH}" \ + -F 'metadata={"labeltags":["test:form_updated"], "form_field":"form_value"}' \ + "${HOSTURL}/update") + + echo "Form update response: ${UPDATE_FORM_RESPONSE}" + if [ "$(echo "${UPDATE_FORM_RESPONSE}" | jq -r '.result')" != "success" ]; then + die "Failed to update metadata via form data" + fi + + # Verify the form update + UPDATED_FORM_METADATA_RESPONSE=$(curl -s "${HOSTURL}/meta/${HASH}") + if ! echo "${UPDATED_FORM_METADATA_RESPONSE}" | jq -e '.metadata.form_field == "form_value"' | grep -q true; then + die "Metadata was not updated correctly via form data" + fi + + # Test 8.3: Test error cases + # Missing hash + MISSING_HASH_RESPONSE=$(curl -s -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -d '{"metadata":{}}' \ + "${HOSTURL}/update") + if [ "$(echo "${MISSING_HASH_RESPONSE}" | jq -r '.error')" != "Missing 'hash' or 'metadata' field in request body" ]; then + die "Expected error for missing hash, got: ${MISSING_HASH_RESPONSE}" + fi + + # Missing metadata + MISSING_METADATA_RESPONSE=$(curl -s -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -d "{\"hash\":\"${HASH}\"}" \ + "${HOSTURL}/update") + if [ "$(echo "${MISSING_METADATA_RESPONSE}" | jq -r '.error')" != "Missing 'hash' or 'metadata' field in request body" ]; then + die "Expected error for missing metadata, got: ${MISSING_METADATA_RESPONSE}" + fi + + # Verify that labeltags were actually updated + echo "Checking if test:updated tag exists..." + UPDATED_HASH=$(curl -s "${HOSTURL}/hash/test:updated" | jq -r '.hash') + if [ "${UPDATED_HASH}" = "${HASH}" ]; then + echo "✓ test:updated tag correctly points to hash ${HASH}" + else + echo "✗ test:updated tag not found or points to wrong hash: ${UPDATED_HASH}" + exit 1 + fi + + echo "Checking if test:form_updated tag exists..." + FORM_UPDATED_HASH=$(curl -s "${HOSTURL}/hash/test:form_updated" | jq -r '.hash') + if [ "${FORM_UPDATED_HASH}" = "${HASH}" ]; then + echo "✓ test:form_updated tag correctly points to hash ${HASH}" + else + echo "✗ test:form_updated tag not found or points to wrong hash: ${FORM_UPDATED_HASH}" + exit 1 + fi + + # Clean up using the correct current labeltag + echo "Cleaning up test object with hash: ${HASH}" + RESPONSE=$(curl -s -H "Authorization: Bearer ${WRITE_TOKEN}" "${HOSTURL}/deleteobject?hash=${HASH}") + echo "Delete response: ${RESPONSE}" + + if echo "${RESPONSE}" | jq -r '.result' | grep -q 'success'; then + echo "Successfully deleted test object" + else + echo "Failed to delete test object: ${RESPONSE}" + exit 1 + fi +} #------------------------------------------------------------------------------------------------ -function test_1GB_file_upload() { +function test9() { + title "9: Testing 1GB File upload" source test_1GB_file_upload.sh } -title "Testing 1GB File upload" -test_1GB_file_upload - #------------------------------------------------------------------------------------------------ + +test0 + +test8 +exit 0 + +test1 +test2 +test3 +test3b +test4 +test5 +test6 +test7 +test8 +test9 + title "ALL TESTS PASSED"