diff --git a/.github/workflows/multi tenancy_Integration.yml b/.github/workflows/multi tenancy_Integration.yml index e0c04f9a..060a5409 100644 --- a/.github/workflows/multi tenancy_Integration.yml +++ b/.github/workflows/multi tenancy_Integration.yml @@ -1,7 +1,7 @@ name: Multi Tenancy Integration Test 🚀 on: - + workflow_dispatch: inputs: cf_space: @@ -24,7 +24,7 @@ jobs: matrix: tokenFlow: [namedUser, technicalUser] tenant: [TENANT1, TENANT2] - testClass: + testClass: - IntegrationTest_SingleFacet - IntegrationTest_MultipleFacet - IntegrationTest_Chapters_MultipleFacet @@ -166,15 +166,58 @@ jobs: echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT echo "✅ Multi-tenant client details fetched successfully!" + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Prepare credentials file 📝 env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} - CMIS_URL: ${{ secrets.CMIS_URL }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} @@ -203,6 +246,7 @@ jobs: password="$CF_PASSWORD" noSDMRoleUsername="$NOSDMROLEUSERNAME" noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + CMIS_URL="${{ env.CMIS_URL_FROM_CF }}" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" defaultRepositoryID="$DEFAULTREPOSITORYID" @@ -305,10 +349,12 @@ jobs: sdm/target/failsafe-reports/ retention-days: 7 - versioned-repo-setup: + # Single-job setup: switch CF app to versioned repo BEFORE matrix tests run. + # Avoids race condition where parallel matrix entries try to restage the same app simultaneously. + versioned-setup: + environment: dev runs-on: ubuntu-latest needs: integration-test - if: "!cancelled()" steps: - name: Cache CF CLI 📦 id: cache-cf-cli @@ -325,36 +371,68 @@ jobs: sudo apt-get update sudo apt-get install cf8-cli + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - - name: Switch to Versioned Repository 🔄 + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} run: | - echo "🔄 Switching REPOSITORY_ID to versioned repository..." - cf set-env bookshop-mt-srv REPOSITORY_ID "${{ secrets.VERSIONEDREPOSITORYID }}" - echo "🔄 Restaging application..." - cf restage bookshop-mt-srv > /dev/null 2>&1 - echo "✅ Switched to versioned repository!" + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi - versioned-integration-test: + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev runs-on: ubuntu-latest - needs: versioned-repo-setup - if: "!cancelled() && needs.versioned-repo-setup.result == 'success'" + needs: versioned-setup strategy: fail-fast: false matrix: @@ -400,21 +478,37 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -470,35 +564,85 @@ jobs: echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT - - name: Run versioned integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.tenant }} - ${{ matrix.testClass }}) + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - appUrlMT="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - authUrlMT1="${{ secrets.AUTHURLMT1 }}" - authUrlMT2="${{ secrets.AUTHURLMT2 }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - clientIDMT="${{ env.CLIENT_ID_MT }}" - clientSecretMT="${{ env.CLIENT_SECRET_MT }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - defaultRepositoryIDMT="${{ secrets.DEFAULTREPOSITORYIDMT }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - - name: Switch to Virus Scan Repository 🔄 + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} run: | - echo "🔄 Switching REPOSITORY_ID to virus scan repository..." - cf set-env bookshop-mt-srv REPOSITORY_ID "${{ secrets.VIRUSSCANREPOSITORYID }}" - echo "🔄 Restaging application..." - cf restage bookshop-mt-srv > /dev/null 2>&1 - echo "✅ Switched to virus scan repository!" + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi - virusscan-integration-test: + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev runs-on: ubuntu-latest - needs: virusscan-repo-setup - if: "!cancelled() && needs.virusscan-repo-setup.result == 'success'" + needs: virusscan-setup strategy: fail-fast: false matrix: @@ -585,9 +784,6 @@ jobs: - IntegrationTest_SingleFacet_Virus - IntegrationTest_MultipleFacet_Virus - IntegrationTest_Chapters_MultipleFacet_Virus - env: - FILE_URL: ${{ 'http://www.eicar.org/download/eicar.com.txt' }} - DOWNLOAD_PATH: ${{ 'sdm/eicar.com.txt' }} steps: - name: Checkout repository 📁 uses: actions/checkout@v6 @@ -624,21 +820,37 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -694,48 +906,97 @@ jobs: echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Download virus test file 📥 run: | - curl -fSL "$FILE_URL" -o "$DOWNLOAD_PATH" + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" sleep 5 - if [ -f "$DOWNLOAD_PATH" ]; then - FILE_NAME=$(basename "$DOWNLOAD_PATH") - FILE_SIZE=$(stat -c '%s' "$DOWNLOAD_PATH") - echo "File exists — Name: $FILE_NAME, Size: $FILE_SIZE bytes" + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" else - echo "❌ File NOT found at path: $DOWNLOAD_PATH" + echo "❌ File NOT found at path: sdm/eicar.com.txt" exit 1 fi - - name: Run virus scan integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.tenant }} - ${{ matrix.testClass }}) + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - appUrlMT="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - authUrlMT1="${{ secrets.AUTHURLMT1 }}" - authUrlMT2="${{ secrets.AUTHURLMT2 }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - clientIDMT="${{ env.CLIENT_ID_MT }}" - clientSecretMT="${{ env.CLIENT_SECRET_MT }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - defaultRepositoryIDMT="${{ secrets.DEFAULTREPOSITORYIDMT }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | echo "🔄 Reverting REPOSITORY_ID to default repository..." - cf set-env bookshop-mt-srv REPOSITORY_ID "${{ secrets.DEFAULTREPOSITORYIDMT }}" + cf set-env bookshop-mt-srv REPOSITORY_ID "$DEFAULTREPOSITORYIDMT" echo "🔄 Restaging application..." cf restage bookshop-mt-srv > /dev/null 2>&1 echo "✅ Reverted to default repository!" - repospecific-integration-test: + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev runs-on: ubuntu-latest - needs: virusscan-repo-cleanup - if: "!cancelled()" + needs: [virusscan-test, virusscan-cleanup] + if: false strategy: fail-fast: false max-parallel: 1 @@ -859,21 +1172,37 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -929,35 +1258,85 @@ jobs: echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT - - name: Run repo-specific integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.tenant }} - ${{ matrix.testClass }}) + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - appUrlMT="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - authUrlMT1="${{ secrets.AUTHURLMT1 }}" - authUrlMT2="${{ secrets.AUTHURLMT2 }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - clientIDMT="${{ env.CLIENT_ID_MT }}" - clientSecretMT="${{ env.CLIENT_SECRET_MT }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - defaultRepositoryIDMT="${{ secrets.DEFAULTREPOSITORYIDMT }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" cat > "$PROPERTIES_FILE" </dev/null | head -1) if [ -z "$BTP_BIN" ]; then echo "❌ btp binary not found after install script." @@ -1046,21 +1447,37 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -1116,47 +1533,104 @@ jobs: echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT - - name: Run subscription integration tests 🎯 (${{ matrix.tenant }}) + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run subscription integration test 🎯 (${{ matrix.tenant }}) + id: run_tests env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + CONSUMERSUBACCOUNTIDMT1: ${{ secrets.CONSUMERSUBACCOUNTIDMT1 }} + CONSUMERSUBDOMAINMT1: ${{ secrets.CONSUMERSUBDOMAINMT1 }} + CONSUMERSUBACCOUNTIDMT2: ${{ secrets.CONSUMERSUBACCOUNTIDMT2 }} + CONSUMERSUBDOMAINMT2: ${{ secrets.CONSUMERSUBDOMAINMT2 }} + CONSUMERSUBDOMAINMT: ${{ secrets.CONSUMERSUBDOMAINMT }} + BTP_CLI_URL: ${{ secrets.BTP_CLI_URL }} + BTP_GLOBAL_ACCOUNT_SUBDOMAIN: ${{ secrets.BTP_GLOBAL_ACCOUNT_SUBDOMAIN }} run: | set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - appUrlMT="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - authUrlMT1="${{ secrets.AUTHURLMT1 }}" - authUrlMT2="${{ secrets.AUTHURLMT2 }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - clientIDMT="${{ env.CLIENT_ID_MT }}" - clientSecretMT="${{ env.CLIENT_SECRET_MT }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - defaultRepositoryIDMT="${{ secrets.DEFAULTREPOSITORYIDMT }}" - consumerSubaccountIdMT1="${{ secrets.CONSUMERSUBACCOUNTIDMT1 }}" - consumerSubdomainMT1="${{ secrets.CONSUMERSUBDOMAINMT1 }}" - consumerSubaccountIdMT2="${{ secrets.CONSUMERSUBACCOUNTIDMT2 }}" - consumerSubdomainMT2="${{ secrets.CONSUMERSUBDOMAINMT2 }}" - consumerSubdomainMT="${{ secrets.CONSUMERSUBDOMAINMT }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-${{ steps.determine_space.outputs.space }}-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + consumerSubaccountIdMT1="$CONSUMERSUBACCOUNTIDMT1" + consumerSubdomainMT1="$CONSUMERSUBDOMAINMT1" + consumerSubaccountIdMT2="$CONSUMERSUBACCOUNTIDMT2" + consumerSubdomainMT2="$CONSUMERSUBDOMAINMT2" + consumerSubdomainMT="$CONSUMERSUBDOMAINMT" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" SAAS_APP_NAME="bookshop-mt-sdmgoogleworkspacedev-${{ steps.determine_space.outputs.space }}" - ROLE_COLLECTION_NAME="ak-test" + ROLE_COLLECTION_NAME="test-cases-role" APP_ROLE_FILTER="bookshop-mt-sdmgoogleworkspacedev-${{ steps.determine_space.outputs.space }}" - BTP_CLI_URL="${{ secrets.BTP_CLI_URL }}" - BTP_GLOBAL_ACCOUNT_SUBDOMAIN="${{ secrets.BTP_GLOBAL_ACCOUNT_SUBDOMAIN }}" + cat > "$PROPERTIES_FILE" <> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 env: @@ -184,12 +173,12 @@ jobs: echo "::add-mask::$CF_PASSWORD" echo "::add-mask::$CF_ORG" echo "::add-mask::$CF_SPACE" - echo "🔄 Logging in to Cloud Foundry..." + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" cf login -a "$CF_API" \ -u "$CF_USER" \ -p "$CF_PASSWORD" \ -o "$CF_ORG" \ - -s "$CF_SPACE" > /dev/null + -s $CF_SPACE > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -259,14 +248,59 @@ jobs: echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT echo "✅ Multi-tenant client details fetched successfully!" + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Prepare credentials file 📝 env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} - CF_SPACE: ${{ secrets.CF_SPACE }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} @@ -274,6 +308,8 @@ jobs: CF_PASSWORD: ${{ secrets.CF_PASSWORD }} NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | echo "🚀 Preparing credentials for ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}..." set +x @@ -292,6 +328,11 @@ jobs: password="$CF_PASSWORD" noSDMRoleUsername="$NOSDMROLEUSERNAME" noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + CMIS_URL="${{ env.CMIS_URL_FROM_CF }}" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" echo "::add-mask::$clientSecret" echo "::add-mask::$clientID" @@ -301,6 +342,9 @@ jobs: echo "::add-mask::$password" echo "::add-mask::$noSDMRoleUsername" echo "::add-mask::$noSDMRoleUserPassword" + echo "::add-mask::$CMIS_URL" + echo "::add-mask::$cmisClientID" + echo "::add-mask::$cmisClientSecret" if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi if [ -z "$appUrlMT" ]; then echo "❌ Error: appUrlMT is not set"; exit 1; fi @@ -315,6 +359,9 @@ jobs: if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi + if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi + if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi + if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + run: | + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi + + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev + runs-on: ubuntu-latest + needs: versioned-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_VersionedRepository + - IntegrationTest_MultipleFacet_VersionedRepository + - IntegrationTest_Chapters_MultipleFacet_VersionedRepository + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + run: | + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi + + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev + runs-on: ubuntu-latest + needs: virusscan-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_Virus + - IntegrationTest_MultipleFacet_Virus + - IntegrationTest_Chapters_MultipleFacet_Virus + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" + sleep 5 + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: sdm/eicar.com.txt" + exit 1 + fi + + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + echo "🔄 Reverting REPOSITORY_ID to default repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$DEFAULTREPOSITORYIDMT" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Reverted to default repository!" + + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev + runs-on: ubuntu-latest + needs: [virusscan-test, virusscan-cleanup] + if: false + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_RepoSpecific + - IntegrationTest_MultipleFacet_RepoSpecific + - IntegrationTest_Chapters_MultipleFacet_RepoSpecific + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + - name: Install BTP CLI 🔧 + run: | + echo "🔄 Installing SAP BTP CLI..." + curl -fsSL https://cli.btp.cloud.sap/btpcli-install.sh | bash + BTP_BIN=$(find "$HOME/bin" /usr/local/bin -maxdepth 1 -name 'btp' -type f 2>/dev/null | head -1) + if [ -z "$BTP_BIN" ]; then + echo "❌ btp binary not found after install script." + exit 1 + fi + if [ "$BTP_BIN" != "/usr/local/bin/btp" ]; then + sudo install -m 755 "$BTP_BIN" /usr/local/bin/btp + fi + btp --version + echo "✅ BTP CLI installed successfully!" + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run subscription integration test 🎯 (${{ matrix.tenant }}) + id: run_tests + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + CONSUMERSUBACCOUNTIDMT1: ${{ secrets.CONSUMERSUBACCOUNTIDMT1 }} + CONSUMERSUBDOMAINMT1: ${{ secrets.CONSUMERSUBDOMAINMT1 }} + CONSUMERSUBACCOUNTIDMT2: ${{ secrets.CONSUMERSUBACCOUNTIDMT2 }} + CONSUMERSUBDOMAINMT2: ${{ secrets.CONSUMERSUBDOMAINMT2 }} + CONSUMERSUBDOMAINMT: ${{ secrets.CONSUMERSUBDOMAINMT }} + BTP_CLI_URL: ${{ secrets.BTP_CLI_URL }} + BTP_GLOBAL_ACCOUNT_SUBDOMAIN: ${{ secrets.BTP_GLOBAL_ACCOUNT_SUBDOMAIN }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + consumerSubaccountIdMT1="$CONSUMERSUBACCOUNTIDMT1" + consumerSubdomainMT1="$CONSUMERSUBDOMAINMT1" + consumerSubaccountIdMT2="$CONSUMERSUBACCOUNTIDMT2" + consumerSubdomainMT2="$CONSUMERSUBDOMAINMT2" + consumerSubdomainMT="$CONSUMERSUBDOMAINMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + SAAS_APP_NAME="bookshop-mt-sdmgoogleworkspacedev-$CF_SPACE" + ROLE_COLLECTION_NAME="test-cases-role" + APP_ROLE_FILTER="bookshop-mt-sdmgoogleworkspacedev-$CF_SPACE" + + cat > "$PROPERTIES_FILE" <> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 env: @@ -235,12 +224,12 @@ jobs: echo "::add-mask::$CF_PASSWORD" echo "::add-mask::$CF_ORG" echo "::add-mask::$CF_SPACE" - echo "🔄 Logging in to Cloud Foundry..." + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" cf login -a "$CF_API" \ -u "$CF_USER" \ -p "$CF_PASSWORD" \ -o "$CF_ORG" \ - -s "$CF_SPACE" > /dev/null + -s $CF_SPACE > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -310,14 +299,59 @@ jobs: echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT echo "✅ Multi-tenant client details fetched successfully!" + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Prepare credentials file 📝 env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} - CF_SPACE: ${{ secrets.CF_SPACE }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} @@ -325,6 +359,8 @@ jobs: CF_PASSWORD: ${{ secrets.CF_PASSWORD }} NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} run: | echo "🚀 Preparing credentials for ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}..." set +x @@ -343,6 +379,11 @@ jobs: password="$CF_PASSWORD" noSDMRoleUsername="$NOSDMROLEUSERNAME" noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + CMIS_URL="${{ env.CMIS_URL_FROM_CF }}" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" echo "::add-mask::$clientSecret" echo "::add-mask::$clientID" @@ -352,6 +393,9 @@ jobs: echo "::add-mask::$password" echo "::add-mask::$noSDMRoleUsername" echo "::add-mask::$noSDMRoleUserPassword" + echo "::add-mask::$CMIS_URL" + echo "::add-mask::$cmisClientID" + echo "::add-mask::$cmisClientSecret" if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi if [ -z "$appUrlMT" ]; then echo "❌ Error: appUrlMT is not set"; exit 1; fi @@ -366,6 +410,9 @@ jobs: if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi + if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi + if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi + if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + run: | + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi + + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev + runs-on: ubuntu-latest + needs: versioned-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_VersionedRepository + - IntegrationTest_MultipleFacet_VersionedRepository + - IntegrationTest_Chapters_MultipleFacet_VersionedRepository + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + run: | + APP_GUID=$(cf app bookshop-mt-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi + + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev + runs-on: ubuntu-latest + needs: virusscan-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_Virus + - IntegrationTest_MultipleFacet_Virus + - IntegrationTest_Chapters_MultipleFacet_Virus + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" + sleep 5 + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: sdm/eicar.com.txt" + exit 1 + fi + + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + echo "🔄 Reverting REPOSITORY_ID to default repository..." + cf set-env bookshop-mt-srv REPOSITORY_ID "$DEFAULTREPOSITORYIDMT" + echo "🔄 Restaging application..." + cf restage bookshop-mt-srv > /dev/null 2>&1 + echo "✅ Reverted to default repository!" + + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev + runs-on: ubuntu-latest + needs: [virusscan-test, virusscan-cleanup] + if: false + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tokenFlow: [namedUser, technicalUser] + tenant: [TENANT1, TENANT2] + testClass: + - IntegrationTest_SingleFacet_RepoSpecific + - IntegrationTest_MultipleFacet_RepoSpecific + - IntegrationTest_Chapters_MultipleFacet_RepoSpecific + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }} - ${{ matrix.tenant }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + - name: Install BTP CLI 🔧 + run: | + echo "🔄 Installing SAP BTP CLI..." + curl -fsSL https://cli.btp.cloud.sap/btpcli-install.sh | bash + BTP_BIN=$(find "$HOME/bin" /usr/local/bin -maxdepth 1 -name 'btp' -type f 2>/dev/null | head -1) + if [ -z "$BTP_BIN" ]; then + echo "❌ btp binary not found after install script." + exit 1 + fi + if [ "$BTP_BIN" != "/usr/local/bin/btp" ]; then + sudo install -m 755 "$BTP_BIN" /usr/local/bin/btp + fi + btp --version + echo "✅ BTP CLI installed successfully!" + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape Client Details for multi tenant 🔍 + id: fetch_credentials_mt + run: | + service_instance_guid=$(cf service bookshop-mt-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret_mt=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret_mt" ] || [ "$clientSecret_mt" == "null" ]; then + echo "❌ Error: clientSecret_mt is not set or is null"; exit 1; + fi + escapedClientSecret_mt=$(echo "$clientSecret_mt" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret_mt" + clientID_mt=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID_mt" ] || [ "$clientID_mt" == "null" ]; then + echo "❌ Error: clientID_mt is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID_mt" + echo "CLIENT_SECRET_MT=$escapedClientSecret_mt" >> $GITHUB_OUTPUT + echo "CLIENT_ID_MT=$clientID_mt" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app bookshop-mt-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for bookshop-mt-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run subscription integration test 🎯 (${{ matrix.tenant }}) + id: run_tests + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CLIENT_SECRET_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_SECRET_MT }} + CLIENT_ID_MT: ${{ steps.fetch_credentials_mt.outputs.CLIENT_ID_MT }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + AUTHURLMT1: ${{ secrets.AUTHURLMT1 }} + AUTHURLMT2: ${{ secrets.AUTHURLMT2 }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + DEFAULTREPOSITORYIDMT: ${{ secrets.DEFAULTREPOSITORYIDMT }} + CONSUMERSUBACCOUNTIDMT1: ${{ secrets.CONSUMERSUBACCOUNTIDMT1 }} + CONSUMERSUBDOMAINMT1: ${{ secrets.CONSUMERSUBDOMAINMT1 }} + CONSUMERSUBACCOUNTIDMT2: ${{ secrets.CONSUMERSUBACCOUNTIDMT2 }} + CONSUMERSUBDOMAINMT2: ${{ secrets.CONSUMERSUBDOMAINMT2 }} + CONSUMERSUBDOMAINMT: ${{ secrets.CONSUMERSUBDOMAINMT }} + BTP_CLI_URL: ${{ secrets.BTP_CLI_URL }} + BTP_GLOBAL_ACCOUNT_SUBDOMAIN: ${{ secrets.BTP_GLOBAL_ACCOUNT_SUBDOMAIN }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + appUrlMT="$CF_ORG-$CF_SPACE-bookshop-mt-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + authUrlMT1="$AUTHURLMT1" + authUrlMT2="$AUTHURLMT2" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + clientIDMT="$CLIENT_ID_MT" + clientSecretMT="$CLIENT_SECRET_MT" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + defaultRepositoryIDMT="$DEFAULTREPOSITORYIDMT" + consumerSubaccountIdMT1="$CONSUMERSUBACCOUNTIDMT1" + consumerSubdomainMT1="$CONSUMERSUBDOMAINMT1" + consumerSubaccountIdMT2="$CONSUMERSUBACCOUNTIDMT2" + consumerSubdomainMT2="$CONSUMERSUBDOMAINMT2" + consumerSubdomainMT="$CONSUMERSUBDOMAINMT" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + SAAS_APP_NAME="bookshop-mt-sdmgoogleworkspacedev-$CF_SPACE" + ROLE_COLLECTION_NAME="test-cases-role" + APP_ROLE_FILTER="bookshop-mt-sdmgoogleworkspacedev-$CF_SPACE" + + cat > "$PROPERTIES_FILE" < /dev/null + -s $CF_SPACE > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials run: | - echo "Fetching client details for single tenant..." + echo "🔄 Fetching client details for single tenant..." service_instance_guid=$(cf service demoappjava-public-uaa --guid) if [ -z "$service_instance_guid" ]; then echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; @@ -209,17 +214,73 @@ jobs: echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT echo "✅ Client details fetched successfully!" + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "$FILE_URL" -o "$DOWNLOAD_PATH" + sleep 5 + if [ -f "$DOWNLOAD_PATH" ]; then + FILE_NAME=$(basename "$DOWNLOAD_PATH") + FILE_SIZE=$(stat -c '%s' "$DOWNLOAD_PATH") + echo "File exists — Name: $FILE_NAME, Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: $DOWNLOAD_PATH" + exit 1 + fi + - name: Run integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} - CF_SPACE: ${{ secrets.CF_SPACE }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} CF_USER: ${{ secrets.CF_USER }} CF_PASSWORD: ${{ secrets.CF_PASSWORD }} NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | set +x echo "🚀 Starting integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." @@ -233,12 +294,24 @@ jobs: password="$CF_PASSWORD" noSDMRoleUsername="$NOSDMROLEUSERNAME" noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" echo "::add-mask::$clientSecret" echo "::add-mask::$clientID" echo "::add-mask::$username" echo "::add-mask::$password" echo "::add-mask::$noSDMRoleUsername" echo "::add-mask::$noSDMRoleUserPassword" + echo "::add-mask::$versionedRepositoryID" + echo "::add-mask::$virusScanRepositoryID" + echo "::add-mask::$defaultRepositoryID" + echo "::add-mask::$CMIS_URL" + echo "::add-mask::$cmisClientID" + echo "::add-mask::$cmisClientSecret" if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi if [ -z "$authUrl" ]; then echo "❌ Error: authUrl is not set"; exit 1; fi if [ -z "$clientID" ]; then echo "❌ Error: clientID is not set"; exit 1; fi @@ -247,6 +320,12 @@ jobs: if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi + if [ -z "$versionedRepositoryID" ]; then echo "❌ Error: versionedRepositoryID is not set"; exit 1; fi + if [ -z "$virusScanRepositoryID" ]; then echo "❌ Error: virusScanRepositoryID is not set"; exit 1; fi + if [ -z "$defaultRepositoryID" ]; then echo "❌ Error: defaultRepositoryID is not set"; exit 1; fi + if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi + if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi + if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + run: | + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi + + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev + runs-on: ubuntu-latest + needs: versioned-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_VersionedRepository + - IntegrationTest_MultipleFacet_VersionedRepository + - IntegrationTest_Chapters_MultipleFacet_VersionedRepository + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + run: | + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi + + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev + runs-on: ubuntu-latest + needs: virusscan-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_Virus + - IntegrationTest_MultipleFacet_Virus + - IntegrationTest_Chapters_MultipleFacet_Virus + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" + sleep 5 + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: sdm/eicar.com.txt" + exit 1 + fi + + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + echo "🔄 Reverting REPOSITORY_ID to default repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$DEFAULTREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Reverted to default repository!" + + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev + runs-on: ubuntu-latest + needs: [virusscan-test, virusscan-cleanup] + if: false + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_RepoSpecific + - IntegrationTest_MultipleFacet_RepoSpecific + - IntegrationTest_Chapters_MultipleFacet_RepoSpecific + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null + -s $CF_SPACE > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -262,23 +267,78 @@ jobs: echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT echo "✅ Client details fetched successfully!" - - name: Run integration tests (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "$FILE_URL" -o "$DOWNLOAD_PATH" + sleep 5 + if [ -f "$DOWNLOAD_PATH" ]; then + FILE_NAME=$(basename "$DOWNLOAD_PATH") + FILE_SIZE=$(stat -c '%s' "$DOWNLOAD_PATH") + echo "File exists — Name: $FILE_NAME, Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: $DOWNLOAD_PATH" + exit 1 + fi + + - name: Run integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} - CF_SPACE: ${{ secrets.CF_SPACE }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} CF_USER: ${{ secrets.CF_USER }} CF_PASSWORD: ${{ secrets.CF_PASSWORD }} NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | set +x - echo "Starting integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." - set -e # Enable error checking + echo "🚀 Starting integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." + set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - # Gather secrets and other values appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" authUrl="$CAPAUTH_URL" clientID="$CLIENT_ID" @@ -287,22 +347,38 @@ jobs: password="$CF_PASSWORD" noSDMRoleUsername="$NOSDMROLEUSERNAME" noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" - echo "::add-mask::$clientID" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" echo "::add-mask::$clientSecret" + echo "::add-mask::$clientID" echo "::add-mask::$username" echo "::add-mask::$password" echo "::add-mask::$noSDMRoleUsername" echo "::add-mask::$noSDMRoleUserPassword" - # Ensure all required variables are set - if [ -z "$appUrl" ]; then echo "Error: appUrl is not set"; exit 1; fi - if [ -z "$authUrl" ]; then echo "Error: authUrl is not set"; exit 1; fi - if [ -z "$clientID" ]; then echo "Error: clientID is not set"; exit 1; fi - if [ -z "$clientSecret" ]; then echo "Error: clientSecret is not set"; exit 1; fi - if [ -z "$username" ]; then echo "Error: username is not set"; exit 1; fi - if [ -z "$password" ]; then echo "Error: password is not set"; exit 1; fi - if [ -z "$noSDMRoleUsername" ]; then echo "Error: noSDMRoleUsername is not set"; exit 1; fi - if [ -z "$noSDMRoleUserPassword" ]; then echo "Error: noSDMRoleUserPassword is not set"; exit 1; fi - # Update properties file with real values + echo "::add-mask::$versionedRepositoryID" + echo "::add-mask::$virusScanRepositoryID" + echo "::add-mask::$defaultRepositoryID" + echo "::add-mask::$CMIS_URL" + echo "::add-mask::$cmisClientID" + echo "::add-mask::$cmisClientSecret" + if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi + if [ -z "$authUrl" ]; then echo "❌ Error: authUrl is not set"; exit 1; fi + if [ -z "$clientID" ]; then echo "❌ Error: clientID is not set"; exit 1; fi + if [ -z "$clientSecret" ]; then echo "❌ Error: clientSecret is not set"; exit 1; fi + if [ -z "$username" ]; then echo "❌ Error: username is not set"; exit 1; fi + if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi + if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi + if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi + if [ -z "$versionedRepositoryID" ]; then echo "❌ Error: versionedRepositoryID is not set"; exit 1; fi + if [ -z "$virusScanRepositoryID" ]; then echo "❌ Error: virusScanRepositoryID is not set"; exit 1; fi + if [ -z "$defaultRepositoryID" ]; then echo "❌ Error: defaultRepositoryID is not set"; exit 1; fi + if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi + if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi + if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + run: | + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi + + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev + runs-on: ubuntu-latest + needs: versioned-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_VersionedRepository + - IntegrationTest_MultipleFacet_VersionedRepository + - IntegrationTest_Chapters_MultipleFacet_VersionedRepository + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + run: | + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi + + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev + runs-on: ubuntu-latest + needs: virusscan-setup + strategy: + fail-fast: false + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_Virus + - IntegrationTest_MultipleFacet_Virus + - IntegrationTest_Chapters_MultipleFacet_Virus + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Download virus test file 📥 + run: | + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" + sleep 5 + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" + else + echo "❌ File NOT found at path: sdm/eicar.com.txt" + exit 1 + fi + + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + echo "🔄 Reverting REPOSITORY_ID to default repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$DEFAULTREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Reverted to default repository!" + + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev + runs-on: ubuntu-latest + needs: [virusscan-test, virusscan-cleanup] + if: false + strategy: + fail-fast: false + max-parallel: 1 + matrix: + tokenFlow: [namedUser, technicalUser] + testClass: + - IntegrationTest_SingleFacet_RepoSpecific + - IntegrationTest_MultipleFacet_RepoSpecific + - IntegrationTest_Chapters_MultipleFacet_RepoSpecific + steps: + - name: Checkout repository 📁 + uses: actions/checkout@v6 + + - name: Set up Java 17 ☕ + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'maven' + + - name: Cache CF CLI 📦 + id: cache-cf-cli + uses: actions/cache@v4 + with: + path: /usr/bin/cf8 + key: cf-cli-v8-${{ runner.os }} + + - name: Install Cloud Foundry CLI 🔧 + if: steps.cache-cf-cli.outputs.cache-hit != 'true' + run: | + wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - + echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list + sudo apt-get update + sudo apt-get install cf8-cli + + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + + + - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} + run: | + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: $CF_SPACE" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s $CF_SPACE > /dev/null + + - name: Fetch and Escape Client Details for single tenant 🔍 + id: fetch_credentials + run: | + service_instance_guid=$(cf service demoappjava-public-uaa --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + binding_guid=$(echo "$bindings_response" | jq -r '.resources[0].guid') + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve binding GUID"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + clientSecret=$(echo "$binding_details" | jq -r '.credentials.clientsecret') + if [ -z "$clientSecret" ] || [ "$clientSecret" == "null" ]; then + echo "❌ Error: clientSecret is not set or is null"; exit 1; + fi + escapedClientSecret=$(echo "$clientSecret" | sed 's/\$/\\$/g') + echo "::add-mask::$escapedClientSecret" + clientID=$(echo "$binding_details" | jq -r '.credentials.clientid') + if [ -z "$clientID" ] || [ "$clientID" == "null" ]; then + echo "❌ Error: clientID is not set or is null"; exit 1; + fi + echo "::add-mask::$clientID" + echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT + echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) + env: + CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} + CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} + run: | + set -e + PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" + appUrl="$CF_ORG-$CF_SPACE-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" + cmisClientID="$CMIS_CLIENT_ID" + cmisClientSecret="$CMIS_CLIENT_SECRET" + cat > "$PROPERTIES_FILE" < /dev/null - echo "✅ Logged in successfully!" - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials @@ -124,6 +123,44 @@ jobs: echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT echo "✅ Client details fetched successfully!" + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Download virus test file 📥 run: | curl -fSL "$FILE_URL" -o "$DOWNLOAD_PATH" @@ -136,13 +173,14 @@ jobs: echo "❌ File NOT found at path: $DOWNLOAD_PATH" exit 1 fi + - name: Run integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} - CMIS_URL: ${{ secrets.CMIS_URL }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} CF_ORG: ${{ secrets.CF_ORG }} CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} CF_USER: ${{ secrets.CF_USER }} @@ -168,6 +206,7 @@ jobs: versionedRepositoryID="$VERSIONEDREPOSITORYID" virusScanRepositoryID="$VIRUSSCANREPOSITORYID" defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" echo "::add-mask::$clientSecret" @@ -213,7 +252,6 @@ jobs: cmisClientSecret=$cmisClientSecret EOL echo "🎯 Running Maven integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." - # mvn clean verify -P integration-tests -DtokenFlow=${{ matrix.tokenFlow }} -DtenancyModel=single -DskipUnitTests -Dfailsafe.includes="**/${{ matrix.testClass }}.java" MAX_RETRIES=3 ATTEMPT=0 EXIT_CODE=1 @@ -235,10 +273,12 @@ jobs: done exit $EXIT_CODE - versioned-repo-setup: + # Single-job setup: switch CF app to versioned repo BEFORE matrix tests run. + # Avoids race condition where parallel matrix entries try to restage the same app simultaneously. + versioned-setup: + environment: dev runs-on: ubuntu-latest needs: integration-test - if: "!cancelled()" steps: - name: Cache CF CLI 📦 id: cache-cf-cli @@ -250,42 +290,73 @@ jobs: - name: Install Cloud Foundry CLI 🔧 if: steps.cache-cf-cli.outputs.cache-hit != 'true' run: | - echo "🔄 Installing Cloud Foundry CLI..." wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list sudo apt-get update sudo apt-get install cf8-cli + - name: Install jq 📦 + run: | + if ! command -v jq &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - - name: Switch to Versioned Repository 🔄 + - name: Switch to versioned repository 🔄 + env: + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} run: | - echo "🔄 Switching REPOSITORY_ID to versioned repository..." - cf set-env demoappjava-srv REPOSITORY_ID "${{ secrets.VERSIONEDREPOSITORYID }}" - echo "🔄 Restaging application..." - cf restage demoappjava-srv > /dev/null 2>&1 - echo "✅ Switched to versioned repository!" + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VERSIONEDREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to versioned repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VERSIONEDREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to versioned repository!" + else + echo "✅ Repository already set to versioned, skipping restage" + fi - versioned-integration-test: + # Versioned tests run in parallel against the already-switched repo + # Skipped if integration-test or versioned-setup fails + versioned-test: + environment: dev runs-on: ubuntu-latest - needs: versioned-repo-setup - if: "!cancelled() && needs.versioned-repo-setup.result == 'success'" + needs: versioned-setup strategy: fail-fast: false matrix: @@ -317,7 +388,6 @@ jobs: - name: Install Cloud Foundry CLI 🔧 if: steps.cache-cf-cli.outputs.cache-hit != 'true' run: | - echo "🔄 Installing Cloud Foundry CLI..." wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list sudo apt-get update @@ -331,26 +401,41 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials run: | - echo "🔄 Fetching client details for single tenant..." service_instance_guid=$(cf service demoappjava-public-uaa --guid) if [ -z "$service_instance_guid" ]; then echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; @@ -374,57 +459,77 @@ jobs: echo "::add-mask::$clientID" echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT - echo "✅ Client details fetched successfully!" - - name: Run versioned integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run versioned integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | - echo "🚀 Starting versioned integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" - echo "::add-mask::$clientSecret" - echo "::add-mask::$clientID" - echo "::add-mask::$username" - echo "::add-mask::$password" - echo "::add-mask::$noSDMRoleUsername" - echo "::add-mask::$noSDMRoleUserPassword" - echo "::add-mask::$versionedRepositoryID" - echo "::add-mask::$virusScanRepositoryID" - echo "::add-mask::$defaultRepositoryID" - echo "::add-mask::$CMIS_URL" - echo "::add-mask::$cmisClientID" - echo "::add-mask::$cmisClientSecret" - if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi - if [ -z "$authUrl" ]; then echo "❌ Error: authUrl is not set"; exit 1; fi - if [ -z "$clientID" ]; then echo "❌ Error: clientID is not set"; exit 1; fi - if [ -z "$clientSecret" ]; then echo "❌ Error: clientSecret is not set"; exit 1; fi - if [ -z "$username" ]; then echo "❌ Error: username is not set"; exit 1; fi - if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi - if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi - if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi - if [ -z "$versionedRepositoryID" ]; then echo "❌ Error: versionedRepositoryID is not set"; exit 1; fi - if [ -z "$virusScanRepositoryID" ]; then echo "❌ Error: virusScanRepositoryID is not set"; exit 1; fi - if [ -z "$defaultRepositoryID" ]; then echo "❌ Error: defaultRepositoryID is not set"; exit 1; fi - if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi - if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi - if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - - name: Switch to Virus Scan Repository 🔄 + - name: Switch to virus scan repository 🔄 + env: + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} run: | - echo "🔄 Switching REPOSITORY_ID to virus scan repository..." - cf set-env demoappjava-srv REPOSITORY_ID "${{ secrets.VIRUSSCANREPOSITORYID }}" - echo "🔄 Restaging application..." - cf restage demoappjava-srv > /dev/null 2>&1 - echo "✅ Switched to virus scan repository!" + APP_GUID=$(cf app demoappjava-srv --guid) + CURRENT=$(cf curl "/v3/apps/${APP_GUID}/environment_variables" | jq -r '.var.REPOSITORY_ID // empty') + if [ "$CURRENT" != "$VIRUSSCANREPOSITORYID" ]; then + echo "🔄 Switching REPOSITORY_ID to virus scan repository..." + cf set-env demoappjava-srv REPOSITORY_ID "$VIRUSSCANREPOSITORYID" + echo "🔄 Restaging application..." + cf restage demoappjava-srv > /dev/null 2>&1 + echo "✅ Switched to virus scan repository!" + else + echo "✅ Repository already set to virus scan, skipping restage" + fi - virusscan-integration-test: + # Virus scan tests run in parallel against the already-switched repo + # Skipped if versioned-test or virusscan-setup fails + virusscan-test: + environment: dev runs-on: ubuntu-latest - needs: virusscan-repo-setup - if: "!cancelled() && needs.virusscan-repo-setup.result == 'success'" + needs: virusscan-setup strategy: fail-fast: false matrix: @@ -503,9 +662,6 @@ jobs: - IntegrationTest_SingleFacet_Virus - IntegrationTest_MultipleFacet_Virus - IntegrationTest_Chapters_MultipleFacet_Virus - env: - FILE_URL: ${{ 'http://www.eicar.org/download/eicar.com.txt' }} - DOWNLOAD_PATH: ${{ 'sdm/eicar.com.txt' }} steps: - name: Checkout repository 📁 uses: actions/checkout@v6 @@ -529,7 +685,6 @@ jobs: - name: Install Cloud Foundry CLI 🔧 if: steps.cache-cf-cli.outputs.cache-hit != 'true' run: | - echo "🔄 Installing Cloud Foundry CLI..." wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list sudo apt-get update @@ -543,26 +698,41 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials run: | - echo "🔄 Fetching client details for single tenant..." service_instance_guid=$(cf service demoappjava-public-uaa --guid) if [ -z "$service_instance_guid" ]; then echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; @@ -586,69 +756,89 @@ jobs: echo "::add-mask::$clientID" echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT - echo "✅ Client details fetched successfully!" + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + - name: Download virus test file 📥 run: | - curl -fSL "$FILE_URL" -o "$DOWNLOAD_PATH" + curl -fSL "http://www.eicar.org/download/eicar.com.txt" -o "sdm/eicar.com.txt" sleep 5 - if [ -f "$DOWNLOAD_PATH" ]; then - FILE_NAME=$(basename "$DOWNLOAD_PATH") - FILE_SIZE=$(stat -c '%s' "$DOWNLOAD_PATH") - echo "File exists — Name: $FILE_NAME, Size: $FILE_SIZE bytes" + if [ -f "sdm/eicar.com.txt" ]; then + FILE_SIZE=$(stat -c '%s' "sdm/eicar.com.txt") + echo "File exists — Size: $FILE_SIZE bytes" else - echo "❌ File NOT found at path: $DOWNLOAD_PATH" + echo "❌ File NOT found at path: sdm/eicar.com.txt" exit 1 fi - - name: Run virus scan integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) + + - name: Run virus scan integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | - echo "🚀 Starting virus scan integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" - echo "::add-mask::$clientSecret" - echo "::add-mask::$clientID" - echo "::add-mask::$username" - echo "::add-mask::$password" - echo "::add-mask::$noSDMRoleUsername" - echo "::add-mask::$noSDMRoleUserPassword" - echo "::add-mask::$versionedRepositoryID" - echo "::add-mask::$virusScanRepositoryID" - echo "::add-mask::$defaultRepositoryID" - echo "::add-mask::$CMIS_URL" - echo "::add-mask::$cmisClientID" - echo "::add-mask::$cmisClientSecret" - if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi - if [ -z "$authUrl" ]; then echo "❌ Error: authUrl is not set"; exit 1; fi - if [ -z "$clientID" ]; then echo "❌ Error: clientID is not set"; exit 1; fi - if [ -z "$clientSecret" ]; then echo "❌ Error: clientSecret is not set"; exit 1; fi - if [ -z "$username" ]; then echo "❌ Error: username is not set"; exit 1; fi - if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi - if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi - if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi - if [ -z "$versionedRepositoryID" ]; then echo "❌ Error: versionedRepositoryID is not set"; exit 1; fi - if [ -z "$virusScanRepositoryID" ]; then echo "❌ Error: virusScanRepositoryID is not set"; exit 1; fi - if [ -z "$defaultRepositoryID" ]; then echo "❌ Error: defaultRepositoryID is not set"; exit 1; fi - if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi - if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi - if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + fi + - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Revert to Default Repository 🔄 + env: + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | echo "🔄 Reverting REPOSITORY_ID to default repository..." - cf set-env demoappjava-srv REPOSITORY_ID "${{ secrets.DEFAULTREPOSITORYID }}" + cf set-env demoappjava-srv REPOSITORY_ID "$DEFAULTREPOSITORYID" echo "🔄 Restaging application..." cf restage demoappjava-srv > /dev/null 2>&1 echo "✅ Reverted to default repository!" - repospecific-integration-test: + # Repo-specific tests run one at a time (max-parallel: 1) so each shows individually in UI + # DISABLED: set if to true to re-enable + repospecific-test: + environment: dev runs-on: ubuntu-latest - needs: virusscan-repo-cleanup - if: "!cancelled()" + needs: [virusscan-test, virusscan-cleanup] + if: false strategy: fail-fast: false max-parallel: 1 @@ -751,7 +992,6 @@ jobs: - name: Install Cloud Foundry CLI 🔧 if: steps.cache-cf-cli.outputs.cache-hit != 'true' run: | - echo "🔄 Installing Cloud Foundry CLI..." wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list sudo apt-get update @@ -765,26 +1005,41 @@ jobs: - name: Determine Cloud Foundry Space 🌌 id: determine_space + env: + CF_SPACE: ${{ secrets.CF_SPACE }} run: | if [ "${{ github.event.inputs.cf_space }}" == "developcap" ]; then - space="${{ secrets.CF_SPACE }}" + space="$CF_SPACE" else space="${{ github.event.inputs.cf_space }}" fi + echo "🌍 Space determined: $space" echo "space=$space" >> $GITHUB_OUTPUT - name: Login to Cloud Foundry 🔑 + env: + CF_API: ${{ secrets.CF_API }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + CF_ORG: ${{ secrets.CF_ORG }} + CF_SPACE: ${{ secrets.CF_SPACE }} run: | - cf login -a ${{ secrets.CF_API }} \ - -u ${{ secrets.CF_USER }} \ - -p ${{ secrets.CF_PASSWORD }} \ - -o ${{ secrets.CF_ORG }} \ - -s ${{ steps.determine_space.outputs.space }} + set +x + echo "::add-mask::$CF_API" + echo "::add-mask::$CF_USER" + echo "::add-mask::$CF_PASSWORD" + echo "::add-mask::$CF_ORG" + echo "::add-mask::$CF_SPACE" + echo "🔄 Logging in to Cloud Foundry using space: ${{ steps.determine_space.outputs.space }}" + cf login -a "$CF_API" \ + -u "$CF_USER" \ + -p "$CF_PASSWORD" \ + -o "$CF_ORG" \ + -s ${{ steps.determine_space.outputs.space }} > /dev/null - name: Fetch and Escape Client Details for single tenant 🔍 id: fetch_credentials run: | - echo "🔄 Fetching client details for single tenant..." service_instance_guid=$(cf service demoappjava-public-uaa --guid) if [ -z "$service_instance_guid" ]; then echo "❌ Error: Unable to retrieve service instance GUID"; exit 1; @@ -808,57 +1063,77 @@ jobs: echo "::add-mask::$clientID" echo "CLIENT_SECRET=$escapedClientSecret" >> $GITHUB_OUTPUT echo "CLIENT_ID=$clientID" >> $GITHUB_OUTPUT - echo "✅ Client details fetched successfully!" - - name: Run repo-specific integration tests 🎯 (${{ matrix.tokenFlow }} - ${{ matrix.testClass }}) + + - name: Fetch and Escape SDM CMIS Credentials 🔍 + id: fetch_credentials_cmis + run: | + echo "🔄 Fetching SDM CMIS credentials from CF service binding..." + service_instance_guid=$(cf service sdm --guid) + if [ -z "$service_instance_guid" ]; then + echo "❌ Error: Unable to retrieve SDM service instance GUID"; exit 1; + fi + bindings_response=$(cf curl "/v3/service_credential_bindings?service_instance_guids=${service_instance_guid}") + app_guid=$(cf app demoappjava-srv --guid) + binding_guid=$(echo "$bindings_response" | jq -r \ + --arg app_guid "$app_guid" \ + '.resources[] | select(.relationships.app.data.guid == $app_guid) | .guid' | head -1) + if [ -z "$binding_guid" ]; then + echo "❌ Error: Unable to retrieve SDM binding GUID for demoappjava-srv"; exit 1; + fi + binding_details=$(cf curl "/v3/service_credential_bindings/${binding_guid}/details") + cmis_client_secret=$(echo "$binding_details" | jq -r '.credentials.uaa.clientsecret // .credentials.clientsecret // empty') + if [ -z "$cmis_client_secret" ] || [ "$cmis_client_secret" == "null" ]; then + echo "❌ Error: SDM clientsecret is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_secret" + cmis_client_id=$(echo "$binding_details" | jq -r '.credentials.uaa.clientid // .credentials.clientid // empty') + if [ -z "$cmis_client_id" ] || [ "$cmis_client_id" == "null" ]; then + echo "❌ Error: SDM clientid is not set or is null"; exit 1; + fi + echo "::add-mask::$cmis_client_id" + cmis_url=$(echo "$binding_details" | jq -r '.credentials.uri // .credentials.endpoints.ecm_service // .credentials.url // empty') + if [ -z "$cmis_url" ]; then + echo "❌ Error: SDM CMIS URL not found in binding details"; exit 1; + fi + echo "::add-mask::$cmis_url" + printf 'CMIS_CLIENT_SECRET=%s\n' "$cmis_client_secret" >> $GITHUB_OUTPUT + printf 'CMIS_CLIENT_ID=%s\n' "$cmis_client_id" >> $GITHUB_OUTPUT + printf 'CMIS_URL=%s\n' "$cmis_url" >> $GITHUB_OUTPUT + echo "✅ SDM CMIS credentials fetched successfully!" + + - name: Run repo-specific integration tests 🎯 (${{ matrix.testClass }} - ${{ matrix.tokenFlow }}) env: CLIENT_SECRET: ${{ steps.fetch_credentials.outputs.CLIENT_SECRET }} CLIENT_ID: ${{ steps.fetch_credentials.outputs.CLIENT_ID }} - CMIS_CLIENT_ID: ${{ secrets.CMISCLIENTID }} - CMIS_CLIENT_SECRET: ${{ secrets.CMISCLIENTSECRET }} + CMIS_CLIENT_ID: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_ID }} + CMIS_CLIENT_SECRET: ${{ steps.fetch_credentials_cmis.outputs.CMIS_CLIENT_SECRET }} + CMIS_URL_FROM_CF: ${{ steps.fetch_credentials_cmis.outputs.CMIS_URL }} + CF_ORG: ${{ secrets.CF_ORG }} + CAPAUTH_URL: ${{ secrets.CAPAUTH_URL }} + CF_USER: ${{ secrets.CF_USER }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + NOSDMROLEUSERNAME: ${{ secrets.NOSDMROLEUSERNAME }} + NOSDMROLEUSERPASSWORD: ${{ secrets.NOSDMROLEUSERPASSWORD }} + VERSIONEDREPOSITORYID: ${{ secrets.VERSIONEDREPOSITORYID }} + VIRUSSCANREPOSITORYID: ${{ secrets.VIRUSSCANREPOSITORYID }} + DEFAULTREPOSITORYID: ${{ secrets.DEFAULTREPOSITORYID }} run: | - echo "🚀 Starting repo-specific integration tests for ${{ matrix.tokenFlow }} - ${{ matrix.testClass }}..." set -e PROPERTIES_FILE="sdm/src/test/resources/credentials.properties" - appUrl="${{ secrets.CF_ORG }}-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" - authUrl="${{ secrets.CAPAUTH_URL }}" - clientID="${{ env.CLIENT_ID }}" - clientSecret="${{ env.CLIENT_SECRET }}" - username="${{ secrets.CF_USER }}" - password="${{ secrets.CF_PASSWORD }}" - noSDMRoleUsername="${{ secrets.NOSDMROLEUSERNAME }}" - noSDMRoleUserPassword="${{ secrets.NOSDMROLEUSERPASSWORD }}" - versionedRepositoryID="${{ secrets.VERSIONEDREPOSITORYID }}" - virusScanRepositoryID="${{ secrets.VIRUSSCANREPOSITORYID }}" - defaultRepositoryID="${{ secrets.DEFAULTREPOSITORYID }}" - CMIS_URL="${{ secrets.CMIS_URL }}" + appUrl="$CF_ORG-${{ steps.determine_space.outputs.space }}-demoappjava-srv.cfapps.eu12.hana.ondemand.com" + authUrl="$CAPAUTH_URL" + clientID="$CLIENT_ID" + clientSecret="$CLIENT_SECRET" + username="$CF_USER" + password="$CF_PASSWORD" + noSDMRoleUsername="$NOSDMROLEUSERNAME" + noSDMRoleUserPassword="$NOSDMROLEUSERPASSWORD" + versionedRepositoryID="$VERSIONEDREPOSITORYID" + virusScanRepositoryID="$VIRUSSCANREPOSITORYID" + defaultRepositoryID="$DEFAULTREPOSITORYID" + CMIS_URL="$CMIS_URL_FROM_CF" cmisClientID="$CMIS_CLIENT_ID" cmisClientSecret="$CMIS_CLIENT_SECRET" - echo "::add-mask::$clientSecret" - echo "::add-mask::$clientID" - echo "::add-mask::$username" - echo "::add-mask::$password" - echo "::add-mask::$noSDMRoleUsername" - echo "::add-mask::$noSDMRoleUserPassword" - echo "::add-mask::$versionedRepositoryID" - echo "::add-mask::$virusScanRepositoryID" - echo "::add-mask::$defaultRepositoryID" - echo "::add-mask::$CMIS_URL" - echo "::add-mask::$cmisClientID" - echo "::add-mask::$cmisClientSecret" - if [ -z "$appUrl" ]; then echo "❌ Error: appUrl is not set"; exit 1; fi - if [ -z "$authUrl" ]; then echo "❌ Error: authUrl is not set"; exit 1; fi - if [ -z "$clientID" ]; then echo "❌ Error: clientID is not set"; exit 1; fi - if [ -z "$clientSecret" ]; then echo "❌ Error: clientSecret is not set"; exit 1; fi - if [ -z "$username" ]; then echo "❌ Error: username is not set"; exit 1; fi - if [ -z "$password" ]; then echo "❌ Error: password is not set"; exit 1; fi - if [ -z "$noSDMRoleUsername" ]; then echo "❌ Error: noSDMRoleUsername is not set"; exit 1; fi - if [ -z "$noSDMRoleUserPassword" ]; then echo "❌ Error: noSDMRoleUserPassword is not set"; exit 1; fi - if [ -z "$versionedRepositoryID" ]; then echo "❌ Error: versionedRepositoryID is not set"; exit 1; fi - if [ -z "$virusScanRepositoryID" ]; then echo "❌ Error: virusScanRepositoryID is not set"; exit 1; fi - if [ -z "$defaultRepositoryID" ]; then echo "❌ Error: defaultRepositoryID is not set"; exit 1; fi - if [ -z "$CMIS_URL" ]; then echo "❌ Error: CMIS_URL is not set"; exit 1; fi - if [ -z "$cmisClientID" ]; then echo "❌ Error: cmisClientID is not set"; exit 1; fi - if [ -z "$cmisClientSecret" ]; then echo "❌ Error: cmisClientSecret is not set"; exit 1; fi cat > "$PROPERTIES_FILE" < config) { this.config = new HashMap<>(config); @@ -44,6 +45,24 @@ private Response executeWithRetry(Request request) throws IOException { Thread.sleep(RETRY_DELAY_MS); continue; } + // Retry on updateConflict (repository lock) — HTTP 500 or 409 + if ((response.code() == 500 || response.code() == 409) && attempt < MAX_RETRIES) { + ResponseBody body = response.peekBody(8192); + String bodyStr = body.string(); + if (bodyStr.contains("updateConflict") || bodyStr.contains("is currently blocked")) { + System.out.println( + "Repository lock detected (updateConflict), retrying after " + + UPDATE_CONFLICT_RETRY_DELAY_MS + + "ms... (attempt " + + attempt + + "/" + + MAX_RETRIES + + ")"); + response.close(); + Thread.sleep(UPDATE_CONFLICT_RETRY_DELAY_MS); + continue; + } + } return response; } catch (java.net.SocketTimeoutException e) { lastException = e; diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/ApiMT.java b/sdm/src/test/java/integration/com/sap/cds/sdm/ApiMT.java index 8a88e2df..f5e4125c 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/ApiMT.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/ApiMT.java @@ -13,8 +13,9 @@ public class ApiMT implements ApiInterface { private final OkHttpClient httpClient; private static final ObjectMapper objectMapper = new ObjectMapper(); private final String token; - private static final int MAX_RETRIES = 3; + private static final int MAX_RETRIES = 5; private static final int RETRY_DELAY_MS = 1000; + private static final int UPDATE_CONFLICT_RETRY_DELAY_MS = 5000; public ApiMT(Map config) { this.config = new HashMap<>(config); @@ -43,6 +44,24 @@ private Response executeWithRetry(Request request) throws IOException { Thread.sleep(RETRY_DELAY_MS); continue; } + // Retry on updateConflict (repository lock) — HTTP 500 or 409 + if ((response.code() == 500 || response.code() == 409) && attempt < MAX_RETRIES) { + ResponseBody body = response.peekBody(8192); + String bodyStr = body.string(); + if (bodyStr.contains("updateConflict") || bodyStr.contains("is currently blocked")) { + System.out.println( + "Repository lock detected (updateConflict), retrying after " + + UPDATE_CONFLICT_RETRY_DELAY_MS + + "ms... (attempt " + + attempt + + "/" + + MAX_RETRIES + + ")"); + response.close(); + Thread.sleep(UPDATE_CONFLICT_RETRY_DELAY_MS); + continue; + } + } return response; } catch (java.net.SocketTimeoutException e) { lastException = e; diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Chapters_MultipleFacet.java b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Chapters_MultipleFacet.java index ebd2af8c..fcefeb51 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Chapters_MultipleFacet.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Chapters_MultipleFacet.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import integration.com.sap.cds.sdm.utils.CmisDocumentHelper; +import integration.com.sap.cds.sdm.utils.ShellScriptRunner; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -1066,6 +1067,34 @@ void testUpdateValidSecondaryPropertyInChapter_beforeBookIsSaved_single() throws response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, bookID5); System.out.println("Save response: " + response); if ("Saved".equals(response)) { + // --- CMIS backend validation (only for attachments facet i==0) --- + { + int i = 0; + String cmisName = + CmisDocumentHelper.getCmisProperty(chapterID5, name[i], "cmis:name"); + assertEquals( + name[i], cmisName, "CMIS should reflect renamed filename for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set for " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match for " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for " + facet[i]); + } testStatus = true; } } else { @@ -1337,6 +1366,32 @@ void testUpdateValidSecondaryPropertyInChapter_afterBookIsSaved_single() { if (counter == facet.length) { response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, bookID5); if (response.equals("Saved")) { + // --- CMIS backend validation (only for attachments facet i==0) --- + { + int i = 0; + String cmisName = CmisDocumentHelper.getCmisProperty(chapterID5, name[i], "cmis:name"); + assertEquals(name[i], cmisName, "CMIS should reflect renamed filename for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set for " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match for " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + chapterID5, name[i], "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for " + facet[i]); + } testStatus = true; System.out.println("Renamed & updated Secondary properties for chapter attachment"); } @@ -1477,6 +1532,27 @@ void testUpdateInvalidSecondaryPropertyInChapter_beforeBookIsSaved_single() thro if (hasAttachmentsError && hasReferencesError && hasFootnotesError) { System.out.println("Book saved with expected invalid property errors"); + // --- CMIS backend validation: no changes should persist in DI (only for attachments + // facet) --- + { + String cmisName = + CmisDocumentHelper.getCmisProperty(tempChapterID, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", + cmisName, + "Filename should NOT be changed in CMIS for " + facet[0]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist for " + facet[0]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.pdf", "Working:DocumentInfoRecordString"); + assertNull( + cmisString, + "Valid props should not persist when invalid props cause rejection for " + + facet[0]); + } testStatus = true; System.out.println( "Rename & update secondary properties for chapter attachment is unsuccessful"); @@ -1642,6 +1718,20 @@ void testUpdateInvalidSecondaryPropertyInChapter_afterBookIsSaved_single() throw if (hasAttachmentsError && hasReferencesError && hasFootnotesError) { System.out.println("Book saved with expected invalid property errors"); + // --- CMIS backend validation: no changes should persist in DI (only for attachments + // facet) --- + { + String cmisName = + CmisDocumentHelper.getCmisProperty(tempChapterID, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", + cmisName, + "Filename should NOT be changed in CMIS for " + facet[0]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist for " + facet[0]); + } testStatus = true; System.out.println( "Rename & update secondary properties for chapter attachment is unsuccessful"); @@ -1957,6 +2047,55 @@ void testUpdateSecondaryProperty_afterBookIsSaved_multipleChapterAttachments() response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, tempBookID); if (response.equals("Saved")) { System.out.println("Book saved"); + // --- CMIS backend validation (only for attachments facet i==0) --- + { + String cmisName = + CmisDocumentHelper.getCmisProperty(tempChapterID, name1, "cmis:name"); + assertEquals( + name1, cmisName, "CMIS should reflect renamed filename for PDF " + facet[0]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, name1, "Working:DocumentInfoRecordString"); + assertNotNull( + cmisString, "DocumentInfoRecordString should be set for PDF " + facet[0]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match for PDF " + facet[0]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBool, + "DocumentInfoRecordBoolean should be true for PDF " + facet[0]); + } + // TXT - only Boolean was set + { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be true for TXT " + facet[0]); + } + // EXE - String + Int were set + { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[0]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.exe", "Working:DocumentInfoRecordInt"); + assertNotNull( + cmisIntExe, "DocumentInfoRecordInt should be set for EXE " + facet[0]); + } testStatus = true; System.out.println("Renamed & updated Secondary properties for chapter attachments"); } @@ -2190,6 +2329,43 @@ void testUpdateInvalidSecondaryProperty_beforeBookIsSaved_multipleChapterAttachm if (hasAttachmentsError && hasReferencesError && hasFootnotesError) { System.out.println("Book saved with expected invalid property errors"); + // --- CMIS backend validation (only for attachments facet) --- + // PDF: invalid prop was used, so nothing should persist + { + String cmisName = + CmisDocumentHelper.getCmisProperty(tempChapterID, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", + cmisName, + "PDF filename should NOT be changed in CMIS for " + facet[0]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.pdf", "abc:myId1"); + assertNull( + cmisId1, "Invalid property abc:myId1 should not exist for PDF " + facet[0]); + } + // TXT: valid Boolean was set — should persist + { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be true for TXT " + facet[0]); + } + // EXE: valid String + Int were set — should persist + { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[0]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + tempChapterID, "sample.exe", "Working:DocumentInfoRecordInt"); + assertNotNull(cmisIntExe, "DocumentInfoRecordInt should be set for EXE " + facet[0]); + } testStatus = true; System.out.println( "Rename & update unsuccessful for invalid properties and successful for valid attachments"); @@ -6579,212 +6755,1417 @@ void testRenameChapterAttachmentWithExtensionChange_BeforeSave() throws IOExcept @Test @Order(73) - void testDownloadMultipleAttachmentsInDraftState() throws IOException { - System.out.println( - "Test (76): Create book+chapter, upload pdf/txt/exe per facet in draft state, download" - + " before saving"); + void testReadCmisMetadataCreatedBy() throws IOException { + System.out.println("Test (78) : Read CMIS metadata and verify createdBy field"); - String draftBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); - if (draftBookID.equals("Could not create entity")) { - fail("Could not create book"); - return; - } - String draftChapterID = - api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, draftBookID); - if (draftChapterID.equals("Could not create entity")) { - api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); - fail("Could not create chapter"); - return; - } - - ClassLoader classLoader = getClass().getClassLoader(); - Map> facetAttachmentIds = new HashMap<>(); - int facetIndex = 0; - for (String facetName : facet) { - List ids = new ArrayList<>(); - Map postData = new HashMap<>(); - postData.put("up__ID", draftChapterID); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + // Create own book and chapter to be self-contained + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); - postData.put("mimeType", "application/pdf"); - File pdfOrig = new File(classLoader.getResource("sample.pdf").getFile()); - File pdfFile = File.createTempFile("sample_ch_" + facetIndex + "_pdf_", ".pdf"); - Files.copy(pdfOrig.toPath(), pdfFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - List r1 = - api.createAttachment( - appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, pdfFile); - pdfFile.delete(); - if (!r1.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); - fail("Could not upload sample.pdf for facet " + facetName); - return; - } - ids.add(r1.get(1)); + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); - postData.put("mimeType", "application/txt"); - File txtOrig = new File(classLoader.getResource("sample.txt").getFile()); - File txtFile = File.createTempFile("sample_ch_" + facetIndex + "_txt_", ".txt"); - Files.copy(txtOrig.toPath(), txtFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - List r2 = - api.createAttachment( - appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, txtFile); - txtFile.delete(); - if (!r2.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); - fail("Could not upload sample.txt for facet " + facetName); - return; - } - ids.add(r2.get(1)); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - postData.put("mimeType", "application/exe"); - File exeOrig = new File(classLoader.getResource("sample.exe").getFile()); - File exeFile = File.createTempFile("sample_ch_" + facetIndex + "_exe_", ".exe"); - Files.copy(exeOrig.toPath(), exeFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - List r3 = - api.createAttachment( - appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, exeFile); - exeFile.delete(); - if (!r3.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); - fail("Could not upload sample.exe for facet " + facetName); - return; - } - ids.add(r3.get(1)); - facetAttachmentIds.put(facetName, ids); - facetIndex++; - } + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - for (String facetName : facet) { - List ids = facetAttachmentIds.get(facetName); + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - String singleResult = - api.downloadSelectedAttachmentsDraft( - appUrl, chapterEntityName, facetName, draftChapterID, List.of(ids.get(0))); - JSONArray singleArray = new JSONArray(singleResult); - assertEquals(1, singleArray.length(), "Expected 1 result for facet " + facetName); - assertEquals( - "success", - singleArray.getJSONObject(0).getString("status"), - "Download button should be enabled in draft state for facet " + facetName); - assertTrue( - singleArray.getJSONObject(0).has("content"), - "Attachment should have content field for facet " + facetName); + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); - String multiResult = - api.downloadSelectedAttachmentsDraft( - appUrl, chapterEntityName, facetName, draftChapterID, ids); - JSONArray multiArray = new JSONArray(multiResult); - assertEquals(3, multiArray.length(), "Expected 3 results for facet " + facetName); - for (int j = 0; j < multiArray.length(); j++) { - assertEquals( - "success", - multiArray.getJSONObject(j).getString("status"), - "Attachment " + (j + 1) + " should download successfully for facet " + facetName); - assertTrue( - multiArray.getJSONObject(j).has("content"), - "Attachment " + (j + 1) + " should have content field for facet " + facetName); - } + // Now check the createdBy CMIS property + String createdBy = + CmisDocumentHelper.getCmisProperty(testChapterID, "sample.pdf", "cmis:createdBy"); + System.out.println("cmis:createdBy value: " + createdBy); + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); + } else { + assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); + assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); } - api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + // Cleanup + api.deleteEntity(appUrl, bookEntityName, testBookID); } @Test @Order(74) - void testDownloadButtonDisabledWithLinkInDraftState() throws IOException { + void testUploadVirusFileInScanDisabledRepo() throws IOException { System.out.println( - "Test (77): Upload pdf and link per facet to chapter, save book, edit (draft state)," - + " pdf download enabled, pdf+link download disabled"); + "Test (79) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); - String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); - if (testBookID.equals("Could not create entity")) { - fail("Could not create book"); - return; - } - String testChapterID = - api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); - if (testChapterID.equals("Could not create entity")) { - api.deleteEntityDraft(appUrl, bookEntityName, testBookID); - fail("Could not create chapter"); - return; - } + for (int i = 0; i < facet.length; i++) { + boolean testStatus = false; + + // Create book and chapter + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + if (testBookID.equals("Could not create entity")) { + fail("Could not create book for facet: " + facet[i]); + } + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + if (testChapterID.equals("Could not create entity")) { + fail("Could not create chapter for facet: " + facet[i]); + } + + // Use EICAR test virus file + String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); + File file = new File(eicarFilePath); + if (!file.exists()) { + fail("EICAR virus test file not found at: " + file.getAbsolutePath()); + } - ClassLoader classLoader = getClass().getClassLoader(); - Map facetPdfId = new HashMap<>(); - Map facetLinkId = new HashMap<>(); - for (String facetName : facet) { Map postData = new HashMap<>(); postData.put("up__ID", testChapterID); - postData.put("mimeType", "application/pdf"); + postData.put("mimeType", "text/plain"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - List pdfResponse = + List createResponse = api.createAttachment( - appUrl, chapterEntityName, facetName, testChapterID, srvpath, postData, pdfFile); - if (!pdfResponse.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, bookEntityName, testBookID); - fail("Could not upload pdf for facet " + facetName); - return; + appUrl, chapterEntityName, facet[i], testChapterID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + String testAttachmentID = createResponse.get(1); + String savedResponse = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + if (savedResponse.equals("Saved")) { + // Verify attachment is readable (upload succeeded despite being a virus file) + savedResponse = + api.readAttachment( + appUrl, chapterEntityName, facet[i], testChapterID, testAttachmentID); + if (savedResponse.equals("OK")) { + testStatus = true; + } + } } - facetPdfId.put(facetName, pdfResponse.get(1)); - String linkResp = - api.createLink( - appUrl, - chapterEntityName, - facetName, - testChapterID, - "TestLink", - "https://www.example.com"); - if (!linkResp.equals("Link created successfully")) { - api.deleteEntityDraft(appUrl, bookEntityName, testBookID); - fail("Could not create link for facet " + facetName); - return; - } + // Clean up + api.deleteEntity(appUrl, bookEntityName, testBookID); - List> draftMeta = - api.fetchEntityMetadataDraft(appUrl, chapterEntityName, facetName, testChapterID); - String linkId = - draftMeta.stream() - .filter( - a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) - .map(a -> (String) a.get("ID")) - .findFirst() - .orElse(null); - if (linkId == null) { - api.deleteEntityDraft(appUrl, bookEntityName, testBookID); - fail("Could not find link attachment in draft metadata for facet " + facetName); - return; + if (!testStatus) { + fail( + "Virus file upload should succeed in a virus scan disabled repository for facet: " + + facet[i]); } - facetLinkId.put(facetName, linkId); } + } - String saveResponse = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); - if (!saveResponse.equals("Saved")) { - api.deleteEntityDraft(appUrl, bookEntityName, testBookID); - fail("Could not save book: " + saveResponse); - return; - } + @Test + @Order(75) + void testRenameToDuplicateFilename_BackendConflict_ErrorThrown() throws Exception { + System.out.println( + "Test (80) : Rename attachment to name that exists in backend — expect DI error"); - String editResponse = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); - if (!editResponse.equals("Entity in draft mode")) { - api.deleteEntity(appUrl, bookEntityName, testBookID); - fail("Could not put book into edit/draft mode: " + editResponse); - return; - } + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); - for (String facetName : facet) { - String pdfId = facetPdfId.get(facetName); - String linkId = facetLinkId.get(facetName); + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); - String pdfResult = - api.downloadSelectedAttachmentsDraft( - appUrl, chapterEntityName, facetName, testChapterID, List.of(pdfId)); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + String conflictingName = "backend-file.pdf"; + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testChapterID); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + response = + api.renameAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, testAttachmentID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + assertTrue( + response.contains("already exists") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); + } + + @Test + @Order(76) + void testUploadDuplicateAttachment_DIError_RemovedFromDrafts() throws IOException { + System.out.println( + "Test (81) : Upload duplicate attachment — expect DI error and removed from drafts"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "First upload should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + List duplicateResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + String errorResponse = duplicateResponse.get(0); + assertNotEquals("Attachment created", errorResponse, "Duplicate upload should fail"); + assertTrue( + errorResponse.contains("already exists") || errorResponse.contains("error"), + "Error should contain DI message. Actual: " + errorResponse); + + List> draftAttachments = + api.fetchEntityMetadataDraft(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals(1, draftAttachments.size(), "Only original attachment should remain in drafts"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(77) + void testReadAttachment_DeletedFromBackend_NotAvailable() throws IOException { + System.out.println( + "Test (82) : Read attachment after backend deletion — verify app handles gracefully"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + CmisDocumentHelper.deleteDocumentFromCmis(testChapterID, "sample.pdf"); + + response = + api.readAttachment(appUrl, chapterEntityName, facet[0], testChapterID, testAttachmentID); + System.out.println(" Read response after backend deletion: " + response); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals(1, attachments.size(), "App should still show the attachment in its metadata"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(78) + void testDeleteAttachment_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println("Test (83) : Delete attachment not in repository — expect removed from UI"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + CmisDocumentHelper.deleteDocumentFromCmis(testChapterID, "sample.pdf"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + response = + api.deleteAttachment(appUrl, chapterEntityName, facet[0], testChapterID, testAttachmentID); + assertEquals("Deleted", response, "Delete should succeed even if not in repo"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed after delete"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals(0, attachments.size(), "No attachments should remain after deletion"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(79) + void testDeleteEntity_FolderAndContentDeletedFromRepository() throws Exception { + System.out.println( + "Test (84) : Delete book — expect chapter folder and all attachments deleted from DI"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + response = api.deleteEntity(appUrl, bookEntityName, testBookID); + assertEquals("Entity Deleted", response, "Book deletion should succeed"); + + List> attachmentsAfterDelete = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals( + 0, + attachmentsAfterDelete.size(), + "Chapter attachments should not be accessible after book deletion"); + } + + @Test + @Order(80) + void testDiscardDraft_AttachmentsAndFolderDeletedFromDI() throws Exception { + System.out.println("Test (85) : Discard draft — expect attachments and folder deleted from DI"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + + String response = api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); + + List> attachmentsAfterDiscard = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals( + 0, + attachmentsAfterDiscard.size(), + "Chapter should have no attachments after discarding draft"); + } + + @Test + @Order(81) + void testDeleteAllAttachments_FolderDeletedFromDI() throws Exception { + System.out.println("Test (86) : Delete all attachments — expect folder deleted from DI"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String attachID1 = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + String folderName = testChapterID + "__attachments"; + ShellScriptRunner.Result folderCheck = + ShellScriptRunner.runAndCaptureAll( + CmisDocumentHelper.getCmisEnvPublic(), + "src/test/java/integration/com/sap/cds/sdm/utils/get-object-id.sh", + folderName); + assertEquals(0, folderCheck.getExitCode(), "Chapter folder should exist in CMIS"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + response = api.deleteAttachment(appUrl, chapterEntityName, facet[0], testChapterID, attachID1); + assertEquals("Deleted", response, "Delete attachment should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed after deleting all attachments"); + + List> attachmentsAfterDelete = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertEquals( + 0, attachmentsAfterDelete.size(), "Chapter should have no attachments after deleting all"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(82) + void testCopyInvalidAttachments_IntoNewEntity_NothingCopied() throws Exception { + System.out.println( + "Test (87) : Copy attachments with invalid secondary property into new entity" + + " — expect copy succeeds but invalid property not propagated"); + + String sourceBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceBookID, "Source book creation should succeed"); + + String sourceChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, sourceBookID); + assertNotEquals( + "Could not create entity", sourceChapterID, "Source chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], sourceChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Saved", response, "Source book save should succeed"); + + api.updateInvalidSecondaryProperty( + appUrl, + chapterEntityName, + facet[0], + sourceChapterID, + sourceAttachmentID, + "invalidTestValue"); + + Map sourceMetadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], sourceChapterID, sourceAttachmentID); + assertNotNull(sourceMetadata.get("objectId"), "Source should have objectId"); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, chapterEntityName, facet[0], targetChapterID, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Save should succeed — copy does not propagate invalid props"); + + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetChapterID, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); + + api.deleteEntity(appUrl, bookEntityName, sourceBookID); + api.deleteEntity(appUrl, bookEntityName, targetBookID); + } + + @Test + @Order(83) + void testCopyInvalidAttachments_IntoExistingEntity_NothingCopied() throws Exception { + System.out.println( + "Test (88) : Copy attachments with invalid secondary property into existing entity" + + " — expect copy succeeds, invalid property not propagated"); + + String sourceBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceBookID, "Source book creation should succeed"); + + String sourceChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, sourceBookID); + assertNotEquals( + "Could not create entity", sourceChapterID, "Source chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], sourceChapterID, srvpath, postData, filePdf); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Saved", response, "Source book save should succeed"); + + api.updateInvalidSecondaryProperty( + appUrl, + chapterEntityName, + facet[0], + sourceChapterID, + sourceAttachmentID, + "invalidTestValue"); + + Map sourceMetadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], sourceChapterID, sourceAttachmentID); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + File file1Pdf = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postDataTarget = new HashMap<>(); + postDataTarget.put("up__ID", targetChapterID); + postDataTarget.put("mimeType", "application/pdf"); + postDataTarget.put("createdAt", new Date().toString()); + postDataTarget.put("createdBy", "test@test.com"); + postDataTarget.put("modifiedBy", "test@test.com"); + + List targetCreateResponse = + api.createAttachment( + appUrl, + chapterEntityName, + facet[0], + targetChapterID, + srvpath, + postDataTarget, + file1Pdf); + assertEquals("Attachment created", targetCreateResponse.get(0)); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Target book save should succeed"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Entity in draft mode", response, "Target book should enter draft mode"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, chapterEntityName, facet[0], targetChapterID, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Save should succeed"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], targetChapterID); + assertEquals(2, targetAttachments.size(), "Target should have 2 attachments"); + + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetChapterID, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); + + api.deleteEntity(appUrl, bookEntityName, sourceBookID); + api.deleteEntity(appUrl, bookEntityName, targetBookID); + } + + @Test + @Order(84) + void testCopyEditedFileName_FromOneEntityToAnother() throws Exception { + System.out.println( + "Test (89) : Copy attachment with edited filename — expect target shows edited name"); + + String sourceBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceBookID, "Source book creation should succeed"); + + String sourceChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, sourceBookID); + assertNotEquals( + "Could not create entity", sourceChapterID, "Source chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], sourceChapterID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Saved", response, "Source book save should succeed"); + + String editedFileName = "sampleEdited.pdf"; + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Entity in draft mode", response); + + response = + api.renameAttachment( + appUrl, + chapterEntityName, + facet[0], + sourceChapterID, + sourceAttachmentID, + editedFileName); + assertEquals("Renamed", response, "Rename should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Saved", response, "Source book save after rename should succeed"); + + Map sourceMetadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], sourceChapterID, sourceAttachmentID); + assertEquals(editedFileName, sourceMetadata.get("fileName")); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, chapterEntityName, facet[0], targetChapterID, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Target book save should succeed"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], targetChapterID); + assertFalse(targetAttachments.isEmpty(), "Target should have attachments"); + + boolean foundEditedFile = false; + for (Map attachment : targetAttachments) { + if (editedFileName.equals(attachment.get("fileName"))) { + foundEditedFile = true; + break; + } + } + assertTrue(foundEditedFile, "Target should have attachment with edited filename"); + + api.deleteEntity(appUrl, bookEntityName, sourceBookID); + api.deleteEntity(appUrl, bookEntityName, targetBookID); + } + + @Test + @Order(85) + void testLinkAttachment_CreatedByIsUserNotClientId() throws Exception { + System.out.println( + "Test (90) : Create link and verify createdBy is the user, not the clientID"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + String linkName = "testLink"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], testChapterID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertFalse(attachments.isEmpty(), "Chapter should have at least one attachment (link)"); + + String linkID = (String) attachments.get(0).get("ID"); + Map metadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], testChapterID, linkID); + String createdBy = (String) metadata.get("createdBy"); + String modifiedBy = (String) metadata.get("modifiedBy"); + + assertNotNull(createdBy, "createdBy should not be null"); + assertNotNull(modifiedBy, "modifiedBy should not be null"); + + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals(username, createdBy, "createdBy should be the user"); + assertEquals(username, modifiedBy, "modifiedBy should be the user"); + } else { + assertFalse(createdBy.isEmpty(), "createdBy should not be empty"); + assertFalse(modifiedBy.isEmpty(), "modifiedBy should not be empty"); + } + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(86) + void testDeleteLink_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println("Test (91) : Delete link not in repository — expect removed from UI"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + String linkName = "linkToDelete"; + String linkUrl = "https://www.example.com/delete-test"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], testChapterID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertFalse(attachments.isEmpty(), "Chapter should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + CmisDocumentHelper.deleteDocumentFromCmis(testChapterID, linkName); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + response = api.deleteAttachment(appUrl, chapterEntityName, facet[0], testChapterID, linkID); + assertEquals("Deleted", response, "Delete link should succeed in the UI"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed after deleting link"); + + List> remainingAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertTrue(remainingAttachments.isEmpty(), "No attachments should remain"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(87) + void testRenameLinkToDuplicateName_BackendConflict_ErrorThrown() throws Exception { + System.out.println("Test (92) : Rename link to duplicate name — expect error"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + String linkName = "originalLink"; + String linkUrl = "https://www.example.com/original"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], testChapterID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertFalse(attachments.isEmpty(), "Chapter should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + String conflictingName = "backendLink"; + ClassLoader classLoader = getClass().getClassLoader(); + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testChapterID); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + response = + api.renameAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, linkID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + assertTrue( + response.contains("already exist") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(88) + void testRenameLink_WhitespaceOnly_WarningThrown() throws Exception { + System.out.println("Test (93) : Rename link with whitespace-only name — expect warning"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + String linkName = "linkToRename"; + String linkUrl = "https://www.example.com/rename-test"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], testChapterID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertFalse(attachments.isEmpty(), "Chapter should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + String whitespaceOnlyName = " "; + response = + api.renameAttachment( + appUrl, chapterEntityName, facet[0], testChapterID, linkID, whitespaceOnlyName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertNotEquals("Saved", response, "Save should not succeed with whitespace-only filename"); + assertTrue( + response.contains("cannot be empty") || response.contains("could not be updated"), + "Warning should indicate filename issue. Actual: " + response); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(89) + void testDiscardDraftEditedLink_RevertsToOriginalUrl() throws Exception { + System.out.println("Test (94) : Edit link URL, discard draft — expect revert to original URL"); + + String originalUrl = "https://abc.com"; + String editedUrl = "https://xyz.com"; + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", testBookID, "Book creation should succeed"); + + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + assertNotEquals("Could not create entity", testChapterID, "Chapter creation should succeed"); + + String linkName = "discardTestLink"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], testChapterID, linkName, originalUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Saved", response, "Book save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], testChapterID); + assertFalse(attachments.isEmpty(), "Chapter should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + Map metadataBefore = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], testChapterID, linkID); + assertEquals(originalUrl, metadataBefore.get("linkUrl"), "Link should have original URL"); + + response = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + assertEquals("Entity in draft mode", response, "Book should enter draft mode"); + + String editResponse = + api.editLink(appUrl, chapterEntityName, facet[0], testChapterID, linkID, editedUrl); + assertEquals("Link edited successfully", editResponse, "Link edit should succeed in draft"); + + response = api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); + + Map metadataAfterDiscard = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], testChapterID, linkID); + assertEquals( + originalUrl, metadataAfterDiscard.get("linkUrl"), "Link URL should revert to original"); + + api.deleteEntity(appUrl, bookEntityName, testBookID); + } + + @Test + @Order(90) + void testMoveAttachments_FromSdmFolder_ToTargetEntity() throws Exception { + System.out.println("Test (95) : Move attachments from SDM folder to target chapter"); + + String folderName = "move-test-folder-" + System.currentTimeMillis(); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); + + ClassLoader classLoader = getClass().getClassLoader(); + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); + + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created"); + assertNotNull(docId2, "Second document should be created"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Target book save should succeed"); + + String targetFacet = serviceName + "." + chapterEntityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + chapterEntityName, + facet[0], + targetChapterID, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], targetChapterID); + assertEquals(2, targetAttachments.size(), "Target should have 2 attachments after move"); + + for (Map attachment : targetAttachments) { + String attachmentId = (String) attachment.get("ID"); + String readResponse = + api.readAttachment(appUrl, chapterEntityName, facet[0], targetChapterID, attachmentId); + assertEquals("OK", readResponse, "Moved attachment should be readable"); + } + + api.deleteEntity(appUrl, bookEntityName, targetBookID); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); + } + + @Test + @Order(91) + void testMoveAttachments_FromSdmFolder_DuplicateInTarget_Skipped() throws Exception { + System.out.println( + "Test (96) : Move from SDM folder with duplicate in target — expect duplicate skipped"); + + String folderName = "move-dup-test-folder-" + System.currentTimeMillis(); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); + + ClassLoader classLoader = getClass().getClassLoader(); + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); + + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created"); + assertNotNull(docId2, "Second document should be created"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", targetChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, chapterEntityName, facet[0], targetChapterID, srvpath, postData, duplicateFile); + assertEquals("Attachment created", createResponse.get(0)); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Target book save should succeed"); + + String targetFacet = serviceName + "." + chapterEntityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + chapterEntityName, + facet[0], + targetChapterID, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], targetChapterID); + assertEquals( + 2, targetAttachments.size(), "Target should have 2 attachments (1 orig + 1 moved)"); + + List fileNames = + targetAttachments.stream() + .map(a -> (String) a.get("fileName")) + .collect(Collectors.toList()); + assertTrue(fileNames.contains("sample.pdf"), "Target should have sample.pdf"); + assertTrue(fileNames.contains("sample1.pdf"), "Target should have sample1.pdf (moved)"); + + api.deleteEntity(appUrl, bookEntityName, targetBookID); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); + } + + @Test + @Order(92) + void testMoveAttachments_FromSdmFolder_WithSecondaryProperties_Preserved() throws Exception { + System.out.println( + "Test (97) : Move from SDM folder with secondary properties — expect preserved"); + + String sourceBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceBookID, "Source book creation should succeed"); + + String sourceChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, sourceBookID); + assertNotEquals( + "Could not create entity", sourceChapterID, "Source chapter creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", sourceChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment( + appUrl, chapterEntityName, facet[0], sourceChapterID, srvpath, postData, filePdf); + assertEquals("Attachment created", createResponse1.get(0)); + String attachId1 = createResponse1.get(1); + + String linkName = "testMoveLink"; + String linkUrl = "https://www.example.com/move-test"; + String createLinkResponse = + api.createLink(appUrl, chapterEntityName, facet[0], sourceChapterID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String notesValue = "Move test note"; + MediaType mediaType = MediaType.parse("application/json"); + RequestBody notesBody = RequestBody.create(mediaType, "{\"note\": \"" + notesValue + "\"}"); + String updateNotes1 = + api.updateSecondaryProperty( + appUrl, chapterEntityName, facet[0], sourceChapterID, attachId1, notesBody); + assertEquals("Updated", updateNotes1, "Notes update should succeed"); + + Integer customIntValue = 42; + RequestBody intBody = + RequestBody.create(mediaType, "{\"customProperty2\": " + customIntValue + "}"); + String updateInt1 = + api.updateSecondaryProperty( + appUrl, chapterEntityName, facet[0], sourceChapterID, attachId1, intBody); + assertEquals("Updated", updateInt1, "Custom property update should succeed"); + + String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, sourceBookID); + assertEquals("Saved", response, "Source book save should succeed"); + + List objectIdsToMove = new ArrayList<>(); + String sourceFolderIdLocal = null; + + List> sourceAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], sourceChapterID); + for (Map attachment : sourceAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], sourceChapterID, attId); + if (metadata.get("objectId") != null) { + objectIdsToMove.add(metadata.get("objectId").toString()); + } + if (sourceFolderIdLocal == null && metadata.get("folderId") != null) { + sourceFolderIdLocal = metadata.get("folderId").toString(); + } + } + assertNotNull(sourceFolderIdLocal, "Source folder ID should be found"); + assertFalse(objectIdsToMove.isEmpty(), "Should have objectIds to move"); + + String targetBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetBookID, "Target book creation should succeed"); + + String targetChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, targetBookID); + assertNotEquals( + "Could not create entity", targetChapterID, "Target chapter creation should succeed"); + + response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, targetBookID); + assertEquals("Saved", response, "Target book save should succeed"); + + String targetFacet = serviceName + "." + chapterEntityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + chapterEntityName, + facet[0], + targetChapterID, + sourceFolderIdLocal, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, chapterEntityName, facet[0], targetChapterID); + assertEquals( + sourceAttachments.size(), targetAttachments.size(), "Target should have all attachments"); + + boolean foundWithNotes = false; + boolean foundLink = false; + for (Map attachment : targetAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, chapterEntityName, facet[0], targetChapterID, attId); + + if (notesValue.equals(metadata.get("note"))) { + foundWithNotes = true; + assertEquals( + customIntValue, metadata.get("customProperty2"), "Custom property should be preserved"); + } + if (linkUrl.equals(metadata.get("linkUrl"))) { + foundLink = true; + } + } + assertTrue( + foundWithNotes, "Attachment with notes and secondary properties should be preserved"); + assertTrue(foundLink, "Link should be moved successfully"); + + api.deleteEntity(appUrl, bookEntityName, targetBookID); + api.deleteEntity(appUrl, bookEntityName, sourceBookID); + } + + @Test + @Order(93) + void testDownloadMultipleAttachmentsInDraftState() throws IOException { + System.out.println( + "Test (76): Create book+chapter, upload pdf/txt/exe per facet in draft state, download" + + " before saving"); + + String draftBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + if (draftBookID.equals("Could not create entity")) { + fail("Could not create book"); + return; + } + String draftChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, draftBookID); + if (draftChapterID.equals("Could not create entity")) { + api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + fail("Could not create chapter"); + return; + } + + ClassLoader classLoader = getClass().getClassLoader(); + Map> facetAttachmentIds = new HashMap<>(); + int facetIndex = 0; + for (String facetName : facet) { + List ids = new ArrayList<>(); + Map postData = new HashMap<>(); + postData.put("up__ID", draftChapterID); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + postData.put("mimeType", "application/pdf"); + File pdfOrig = new File(classLoader.getResource("sample.pdf").getFile()); + File pdfFile = File.createTempFile("sample_ch_" + facetIndex + "_pdf_", ".pdf"); + Files.copy(pdfOrig.toPath(), pdfFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + List r1 = + api.createAttachment( + appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, pdfFile); + pdfFile.delete(); + if (!r1.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + fail("Could not upload sample.pdf for facet " + facetName); + return; + } + ids.add(r1.get(1)); + + postData.put("mimeType", "application/txt"); + File txtOrig = new File(classLoader.getResource("sample.txt").getFile()); + File txtFile = File.createTempFile("sample_ch_" + facetIndex + "_txt_", ".txt"); + Files.copy(txtOrig.toPath(), txtFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + List r2 = + api.createAttachment( + appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, txtFile); + txtFile.delete(); + if (!r2.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + fail("Could not upload sample.txt for facet " + facetName); + return; + } + ids.add(r2.get(1)); + + postData.put("mimeType", "application/exe"); + File exeOrig = new File(classLoader.getResource("sample.exe").getFile()); + File exeFile = File.createTempFile("sample_ch_" + facetIndex + "_exe_", ".exe"); + Files.copy(exeOrig.toPath(), exeFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + List r3 = + api.createAttachment( + appUrl, chapterEntityName, facetName, draftChapterID, srvpath, postData, exeFile); + exeFile.delete(); + if (!r3.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + fail("Could not upload sample.exe for facet " + facetName); + return; + } + ids.add(r3.get(1)); + facetAttachmentIds.put(facetName, ids); + facetIndex++; + } + + for (String facetName : facet) { + List ids = facetAttachmentIds.get(facetName); + + String singleResult = + api.downloadSelectedAttachmentsDraft( + appUrl, chapterEntityName, facetName, draftChapterID, List.of(ids.get(0))); + JSONArray singleArray = new JSONArray(singleResult); + assertEquals(1, singleArray.length(), "Expected 1 result for facet " + facetName); + assertEquals( + "success", + singleArray.getJSONObject(0).getString("status"), + "Download button should be enabled in draft state for facet " + facetName); + assertTrue( + singleArray.getJSONObject(0).has("content"), + "Attachment should have content field for facet " + facetName); + + String multiResult = + api.downloadSelectedAttachmentsDraft( + appUrl, chapterEntityName, facetName, draftChapterID, ids); + JSONArray multiArray = new JSONArray(multiResult); + assertEquals(3, multiArray.length(), "Expected 3 results for facet " + facetName); + for (int j = 0; j < multiArray.length(); j++) { + assertEquals( + "success", + multiArray.getJSONObject(j).getString("status"), + "Attachment " + (j + 1) + " should download successfully for facet " + facetName); + assertTrue( + multiArray.getJSONObject(j).has("content"), + "Attachment " + (j + 1) + " should have content field for facet " + facetName); + } + } + + api.deleteEntityDraft(appUrl, bookEntityName, draftBookID); + } + + @Test + @Order(94) + void testDownloadButtonDisabledWithLinkInDraftState() throws IOException { + System.out.println( + "Test (77): Upload pdf and link per facet to chapter, save book, edit (draft state)," + + " pdf download enabled, pdf+link download disabled"); + + String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); + if (testBookID.equals("Could not create entity")) { + fail("Could not create book"); + return; + } + String testChapterID = + api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); + if (testChapterID.equals("Could not create entity")) { + api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + fail("Could not create chapter"); + return; + } + + ClassLoader classLoader = getClass().getClassLoader(); + Map facetPdfId = new HashMap<>(); + Map facetLinkId = new HashMap<>(); + for (String facetName : facet) { + Map postData = new HashMap<>(); + postData.put("up__ID", testChapterID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List pdfResponse = + api.createAttachment( + appUrl, chapterEntityName, facetName, testChapterID, srvpath, postData, pdfFile); + if (!pdfResponse.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + fail("Could not upload pdf for facet " + facetName); + return; + } + facetPdfId.put(facetName, pdfResponse.get(1)); + + String linkResp = + api.createLink( + appUrl, + chapterEntityName, + facetName, + testChapterID, + "TestLink", + "https://www.example.com"); + if (!linkResp.equals("Link created successfully")) { + api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + fail("Could not create link for facet " + facetName); + return; + } + + List> draftMeta = + api.fetchEntityMetadataDraft(appUrl, chapterEntityName, facetName, testChapterID); + String linkId = + draftMeta.stream() + .filter( + a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) + .map(a -> (String) a.get("ID")) + .findFirst() + .orElse(null); + if (linkId == null) { + api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + fail("Could not find link attachment in draft metadata for facet " + facetName); + return; + } + facetLinkId.put(facetName, linkId); + } + + String saveResponse = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + if (!saveResponse.equals("Saved")) { + api.deleteEntityDraft(appUrl, bookEntityName, testBookID); + fail("Could not save book: " + saveResponse); + return; + } + + String editResponse = api.editEntityDraft(appUrl, bookEntityName, srvpath, testBookID); + if (!editResponse.equals("Entity in draft mode")) { + api.deleteEntity(appUrl, bookEntityName, testBookID); + fail("Could not put book into edit/draft mode: " + editResponse); + return; + } + + for (String facetName : facet) { + String pdfId = facetPdfId.get(facetName); + String linkId = facetLinkId.get(facetName); + + String pdfResult = + api.downloadSelectedAttachmentsDraft( + appUrl, chapterEntityName, facetName, testChapterID, List.of(pdfId)); JSONArray pdfArray = new JSONArray(pdfResult); assertEquals(1, pdfArray.length(), "Expected 1 result for pdf-only for facet " + facetName); assertEquals( @@ -6821,7 +8202,7 @@ void testDownloadButtonDisabledWithLinkInDraftState() throws IOException { } @Test - @Order(75) + @Order(95) void testDownloadMultipleAttachmentsInActiveState() throws IOException { System.out.println( "Test (78): Create book+chapter, upload pdf/txt/exe per facet, save, download in active" @@ -6931,7 +8312,7 @@ void testDownloadMultipleAttachmentsInActiveState() throws IOException { } @Test - @Order(76) + @Order(96) void testDownloadButtonDisabledWithLinkInActiveState() throws IOException { System.out.println( "Test (79): Upload pdf and link per facet to chapter, save book, pdf download enabled," @@ -7053,84 +8434,4 @@ void testDownloadButtonDisabledWithLinkInActiveState() throws IOException { api.deleteEntity(appUrl, bookEntityName, testBookID); } - - @Test - @Order(77) - void testReadCmisMetadataCreatedBy() { - System.out.println("Test (76) : Read CMIS metadata and verify createdBy field"); - String createdBy = - CmisDocumentHelper.getCmisProperty(chapterID, "sample.pdf", "cmis:createdBy"); - System.out.println("cmis:createdBy value: " + createdBy); - String tokenFlowFlag = System.getProperty("tokenFlow"); - if ("namedUser".equals(tokenFlowFlag)) { - assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); - } else { - assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); - assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); - } - } - - @Test - @Order(78) - void testUploadVirusFileInScanDisabledRepo() throws IOException { - System.out.println( - "Test (77) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); - - for (int i = 0; i < facet.length; i++) { - boolean testStatus = false; - - // Create book and chapter - String testBookID = api.createEntityDraft(appUrl, bookEntityName, entityName2, srvpath); - if (testBookID.equals("Could not create entity")) { - fail("Could not create book for facet: " + facet[i]); - } - - String testChapterID = - api.createEntityDraft(appUrl, chapterEntityName, entityName2, srvpath, testBookID); - if (testChapterID.equals("Could not create entity")) { - fail("Could not create chapter for facet: " + facet[i]); - } - - // Use EICAR test virus file - String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); - File file = new File(eicarFilePath); - if (!file.exists()) { - fail("EICAR virus test file not found at: " + file.getAbsolutePath()); - } - - Map postData = new HashMap<>(); - postData.put("up__ID", testChapterID); - postData.put("mimeType", "text/plain"); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); - - List createResponse = - api.createAttachment( - appUrl, chapterEntityName, facet[i], testChapterID, srvpath, postData, file); - String check = createResponse.get(0); - if (check.equals("Attachment created")) { - String testAttachmentID = createResponse.get(1); - String response = api.saveEntityDraft(appUrl, bookEntityName, srvpath, testBookID); - if (response.equals("Saved")) { - // Verify attachment is readable (upload succeeded despite being a virus file) - response = - api.readAttachment( - appUrl, chapterEntityName, facet[i], testChapterID, testAttachmentID); - if (response.equals("OK")) { - testStatus = true; - } - } - } - - // Clean up - api.deleteEntity(appUrl, bookEntityName, testBookID); - - if (!testStatus) { - fail( - "Virus file upload should succeed in a virus scan disabled repository for facet: " - + facet[i]); - } - } - } } diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_MultipleFacet.java b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_MultipleFacet.java index f67ddc43..85698a9c 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_MultipleFacet.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_MultipleFacet.java @@ -5,13 +5,17 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import integration.com.sap.cds.sdm.utils.CmisDocumentHelper; +import integration.com.sap.cds.sdm.utils.ShellScriptRunner; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; import okhttp3.*; +import okio.ByteString; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.*; @@ -356,7440 +360,8546 @@ void testUploadSinglePDF() throws IOException { } } - // @Test - // @Order(4) - // void testUploadSingleTXT() throws IOException { - // System.out.println("Test (4) : Upload attachment, reference, and footnote TXT"); - // Boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.txt").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "text/plain"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Entity in draft mode")) { - // // Creation of attachment, reference and footnote - // for (int i = 0; i < facet.length; i++) { - // ID2[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID, postData, file); - // } - // testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID, ID2); - // } - // if (!testStatus) { - // fail("Could not upload sample.txt " + response); - // } - // } + @Test + @Order(4) + void testUploadSingleTXT() throws IOException { + System.out.println("Test (4) : Upload attachment, reference, and footnote TXT"); + Boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.txt").getFile()); - // @Test - // @Order(5) - // void testUploadSingleEXE() throws IOException { - // System.out.println("Test (5) : Upload attachment, reference, and footnote EXE"); - // Boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.exe").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/octet-stream"); // Common mime-type for executables - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Entity in draft mode")) { - // // Creation of attachment, reference and footnote - // for (int i = 0; i < facet.length; i++) { - // ID3[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID, postData, file); - // } - // testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID, ID3); - // } - // if (!testStatus) { - // fail("Could not upload sample.exe " + response); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(6) - // void testUploadPDFDuplicate() throws IOException { - // System.out.println("Test (6) : Upload duplicate PDF as attachment, reference, and footnote"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if ("Entity in draft mode".equals(response)) { - // Boolean allFacetsFailedCorrectly = true; - // for (int i = 0; i < facet.length; i++) { - // List facetResponse = - // api.createAttachment(appUrl, entityName, facet[i], entityID, srvpath, postData, - // file); - // allFacetsFailedCorrectly &= checkDuplicateCreation(facet[i], facetResponse); - // } - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (!allFacetsFailedCorrectly) { - // fail("One or more facets were incorrectly accepted as new."); - // } - // } else { - // fail("Entity could not be edited to draft mode."); - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Entity in draft mode")) { + // Creation of attachment, reference and footnote + for (int i = 0; i < facet.length; i++) { + ID2[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID, postData, file); + } + testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID, ID2); + } + if (!testStatus) { + fail("Could not upload sample.txt " + response); + } + } - // @Test - // @Order(7) - // void testUploadSinglePDFWithAttachmentReferenceFootnote() throws IOException { - // System.out.println( - // "Test (7) : Upload duplicate PDF in different entity with attachment, reference, and - // footnote"); - // Boolean testStatus = false; - // // Create a new entity draft - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!"Could not create entity".equals(response)) { - // entityID2 = response; - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - - // if ("Saved".equals(response)) { - // response = api.checkEntity(appUrl, entityName, entityID2); - // if ("Entity exists".equals(response)) { - // testStatus = true; - // } - // } - // } - // if (!testStatus) { - // fail("Could not create entity"); - // } + @Test + @Order(5) + void testUploadSingleEXE() throws IOException { + System.out.println("Test (5) : Upload attachment, reference, and footnote EXE"); + Boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.exe").getFile()); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID2); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // // Edit entity to draft mode - // response = api.editEntityDraft(appUrl, entityName, srvpath, entityID2); - // if ("Entity in draft mode".equals(response)) { - // // Create attachment, reference, and footnote - // for (int i = 0; i < facet.length; i++) { - // ID4[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID2, postData, file); - // } - // // Verify and save - // testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID2, ID4); - // } - // if (!testStatus) { - // fail("Could not upload sample.pdf as an attachment, reference, or footnote: " + response); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/octet-stream"); // Common mime-type for executables + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(8) - // void testRenameEntities() { - // System.out.println("Test (8) : Rename single attachment, reference, and footnote"); - // Boolean testStatus = true; - - // try { - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - - // if ("Entity in draft mode".equals(response)) { - // String[] name = {"sample123", "reference123", "footnote123"}; - // for (int i = 0; i < facet.length; i++) { - // // Read the facet to ensure it exists - // response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID[i], - // name[i]); - // if (!"Renamed".equals(response)) { - // testStatus = false; - // System.out.println(facet[i] + " was not renamed: " + response); - // } - // } - // // Save entity draft if everything is renamed - // if (testStatus) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (!"Saved".equals(response)) { - // testStatus = false; - // System.out.println("Entity draft was not saved: " + response); - // } - // } else { - // // Attempt save despite potential rename failures - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } else { - // testStatus = false; - // System.out.println("Entity was not put into draft mode: " + response); - // } - // } catch (Exception e) { - // testStatus = false; - // System.out.println("Exception during renaming entities: " + e.getMessage()); - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Entity in draft mode")) { + // Creation of attachment, reference and footnote + for (int i = 0; i < facet.length; i++) { + ID3[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID, postData, file); + } + testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID, ID3); + } + if (!testStatus) { + fail("Could not upload sample.exe " + response); + } + } - // if (!testStatus) { - // fail("There was an error during the rename test process."); - // } - // } + @Test + @Order(6) + void testUploadPDFDuplicate() throws IOException { + System.out.println("Test (6) : Upload duplicate PDF as attachment, reference, and footnote"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(9) - // void testCreateEntitiesWithUnsupportedCharacter() throws IOException { - // System.out.println("Test (9): Create attachments with unsupported characters"); - // boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if ("Entity in draft mode".equals(response)) { + Boolean allFacetsFailedCorrectly = true; + for (int i = 0; i < facet.length; i++) { + List facetResponse = + api.createAttachment(appUrl, entityName, facet[i], entityID, srvpath, postData, file); + allFacetsFailedCorrectly &= checkDuplicateCreation(facet[i], facetResponse); + } + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (!allFacetsFailedCorrectly) { + fail("One or more facets were incorrectly accepted as new."); + } + } else { + fail("Entity could not be edited to draft mode."); + } + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (!"Entity in draft mode".equals(response)) { - // fail("Entity not in draft mode: " + response); - // return; - // } + @Test + @Order(7) + void testUploadSinglePDFWithAttachmentReferenceFootnote() throws IOException { + System.out.println( + "Test (7) : Upload duplicate PDF in different entity with attachment, reference, and footnote"); + Boolean testStatus = false; + // Create a new entity draft + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!"Could not create entity".equals(response)) { + entityID2 = response; + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // for (int i = 0; i < facet.length; i++) { - // postData.put("up__ID", entityID); - // List createResponse = - // api.createAttachment(appUrl, entityName, facet[i], entityID, srvpath, postData, - // tempFile); + if ("Saved".equals(response)) { + response = api.checkEntity(appUrl, entityName, entityID2); + if ("Entity exists".equals(response)) { + testStatus = true; + } + } + } + if (!testStatus) { + fail("Could not create entity"); + } - // String check = createResponse.get(0); - // if (!"Attachment created".equals(check)) { - // System.out.println("Failed to create attachment for facet: " + facet[i]); - // continue; - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // String restrictedName = "a/\\bc.pdf"; - // response = - // api.renameAttachment(appUrl, entityName, facet[i], entityID, ID4[i], restrictedName); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID2); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"a/\\bc.pdf\\\" - // contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: - // attachments\\nPage: - // IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // if (response.equals(expected)) { - // api.deleteEntityDraft(appUrl, entityName, entityID); - // testStatus = true; - // } + // Edit entity to draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, entityID2); + if ("Entity in draft mode".equals(response)) { + // Create attachment, reference, and footnote + for (int i = 0; i < facet.length; i++) { + ID4[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID2, postData, file); + } + // Verify and save + testStatus = verifyDraftAndSave(appUrl, serviceName, entityName, entityID2, ID4); + } + if (!testStatus) { + fail("Could not upload sample.pdf as an attachment, reference, or footnote: " + response); + } + } - // if (!testStatus) { - // fail("Facets renamed with restricted characters were not correctly rejected."); - // } - // } + @Test + @Order(8) + void testRenameEntities() { + System.out.println("Test (8) : Rename single attachment, reference, and footnote"); + Boolean testStatus = true; - // @Test - // @Order(10) - // void testRenameEntitiesWithUnsupportedCharacter() { - // System.out.println("Test (10) : Rename attachments with unsupported characters"); - // Boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String[] name = {"sample/1234", "reference1/234", "footnote1/234"}; - // if (response.equals("Entity in draft mode")) { - // for (int i = 0; i < facet.length; i++) { - // response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name[i]); - // if (response.equals("Renamed")) counter++; - // } - // if (counter >= 2) { - // counter = -1; // Reset counter for the next check - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"reference1/234\\\" contains - // unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"sample/1234\\\" - // contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: - // attachments\\nPage: - // IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"footnote1/234\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // if (response.equals(expected)) { - // for (int i = 0; i < facet.length; i++) { - // response = - // api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], - // "sample.pdf"); - // } - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment was renamed with unsupported characters"); - // } - // } + try { + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // @Test - // @Order(11) - // void testRenameMultipleEntityComponents() { - // System.out.println("Test (11) : Rename multiple attachments, references, and footnotes"); - // boolean testStatus = true; - - // String draftResponse = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (!"Entity in draft mode".equals(draftResponse)) { - // fail("Entity is not in draft mode."); - // return; - // } - // String[] name = {"sample1234", "reference1234", "footnote1234"}; - // String[] name2 = {"sample12345", "reference12345", "footnote12345"}; - // for (int i = 0; i < facet.length; i++) { - // // Read the facet to ensure it exists - // testStatus &= renameAndCheck(facet[i], ID2[i], entityID, name[i]); - // testStatus &= renameAndCheck(facet[i], ID3[i], entityID, name2[i]); - // } - // // Save the draft if all renames succeeded - // if (testStatus) { - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (!"Saved".equals(saveResponse)) { - // fail("Entity draft was not saved after renaming."); - // } - // } else { - // // Save draft even if renaming failed to preserve state - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // fail("One or more components were not renamed."); - // } - // } + if ("Entity in draft mode".equals(response)) { + String[] name = {"sample123", "reference123", "footnote123"}; + for (int i = 0; i < facet.length; i++) { + // Read the facet to ensure it exists + response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID[i], name[i]); + if (!"Renamed".equals(response)) { + testStatus = false; + System.out.println(facet[i] + " was not renamed: " + response); + } + } + // Save entity draft if everything is renamed + if (testStatus) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (!"Saved".equals(response)) { + testStatus = false; + System.out.println("Entity draft was not saved: " + response); + } + } else { + // Attempt save despite potential rename failures + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } else { + testStatus = false; + System.out.println("Entity was not put into draft mode: " + response); + } + } catch (Exception e) { + testStatus = false; + System.out.println("Exception during renaming entities: " + e.getMessage()); + } - // @Test - // @Order(12) - // void testRenameSingleDuplicate() { - // System.out.println("Test (12) : Rename duplicates for attachment, reference, and footnote"); - // Boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String[] name = {"sample1234", "reference1234", "footnote1234"}; - // String[] name2 = {"sample123456", "reference123456", "footnote123456"}; - // if (response.equals("Entity in draft mode")) { - // for (int i = 0; i < facet.length; i++) { - // response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name[i]); - // if (response.equals("Renamed")) counter++; - // } - // if (counter >= 2) { - // counter = -1; // Reset counter for the next check - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // String.format( - // "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"%s\\\" already - // exists. Rename the object and try again.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"An object named - // \\\"%s\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An - // object named \\\"%s\\\" already exists. Rename the object and try again.\\n\\nTable: - // footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}", - // name[1], name[0], name[2]); - // if (response.equals(expected)) { - // for (int i = 0; i < facet.length; i++) { - // // Attempt to rename again with a different name - // response = - // api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name2[i]); - // if (response.equals("Renamed")) counter++; - // } - // } - // if (counter >= 2) { - // // If all renames were successful, save the draft - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } else { - // testStatus = false; - // fail("Attachment was renamed"); - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // } + if (!testStatus) { + fail("There was an error during the rename test process."); + } + } - // @Test - // @Order(13) - // void testRenameMultipleEntitiesWithOneUnsupportedCharacter() { - // System.out.println( - // "Test (13) : Rename multiple files out of which one file name contains unsupported - // characters"); - // boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String[] names = {"summary_1234", "reference_4567", "note/invalid"}; - - // if (response.equals("Entity in draft mode")) { - // int successCount = 0; - // for (int i = 0; i < facet.length; i++) { - // response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], - // names[i]); - // if (response.equals("Renamed")) successCount++; - // } + @Test + @Order(9) + void testCreateEntitiesWithUnsupportedCharacter() throws IOException { + System.out.println("Test (9): Create attachments with unsupported characters"); + boolean testStatus = false; - // if (successCount >= 2) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"note/invalid\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: - // IntegrationTestEntity\"}}"; - // if (response.equals(expected)) { - // response = - // api.renameAttachment(appUrl, entityName, facet[2], entityID, ID3[2], "note_valid"); - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) testStatus = true; - // } - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // if (!testStatus) { - // fail("Attachment was renamed with unsupported characters"); - // } - // } + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - // @Test - // @Order(14) - // void testRenameToValidateNames() throws IOException { - // System.out.println("Test (14) : Rename attachments to validate names"); - // String[] generatedIDs = new String[3]; - // String[] duplicateIDs = new String[1]; - // boolean testStatus = false, allRenamedSuccessfully = true; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID3 = response; + Map postData = new HashMap<>(); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String[] invalidNames = {"Restricted/Character", " ", "duplicateName.pdf"}; - // String duplicateName = "duplicateName.pdf"; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (!"Entity in draft mode".equals(response)) { + fail("Entity not in draft mode: " + response); + return; + } - // ClassLoader classLoader = getClass().getClassLoader(); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + for (int i = 0; i < facet.length; i++) { + postData.put("up__ID", entityID); + List createResponse = + api.createAttachment(appUrl, entityName, facet[i], entityID, srvpath, postData, tempFile); - // // Creation of attachment, reference and footnote - // for (int i = 0; i < facet.length; i++) { - // File file = new File(classLoader.getResource("sample2.pdf").getFile()); - // generatedIDs[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // response = - // api.renameAttachment( - // appUrl, entityName, facet[i], entityID3, generatedIDs[i], invalidNames[i]); - // allRenamedSuccessfully &= "Renamed".equals(response); - // } - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Creating duplicate name for last facet - // duplicateIDs[0] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[2], entityID3, postData, file); - // String response2 = - // api.renameAttachment( - // appUrl, entityName, facet[2], entityID3, duplicateIDs[0], duplicateName); - - // if (allRenamedSuccessfully && "Renamed".equals(response2)) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"The object name cannot be empty or - // consist entirely of space characters. Enter a value.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"Restricted/Character\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"duplicateName.pdf\\\" already exists. Rename the object and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // if (response.equals(expected)) { - // response = api.deleteEntityDraft(appUrl, entityName, entityID3); - // if (response.equals("Entity Draft Deleted")) testStatus = true; - // } - // } - // if (!testStatus) fail("Could not create entity"); - // } else { - // fail("Could not create entity"); - // return; - // } - // } + String check = createResponse.get(0); + if (!"Attachment created".equals(check)) { + System.out.println("Failed to create attachment for facet: " + facet[i]); + continue; + } - // @Test - // @Order(15) - // void testRenameEntitiesWithoutSDMRole() throws IOException { - // System.out.println("Test (15) : Rename attachments where user don't have SDM-Roles"); - // boolean testStatus = true; - // try { - // String apiResponse = apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if ("Entity in draft mode".equals(apiResponse)) { - // String[] name = {"sample456", "reference456", "footnote456"}; - // for (int i = 0; i < facet.length; i++) { - // apiResponse = - // apiNoRoles.renameAttachment(appUrl, entityName, facet[i], entityID, ID[i], - // name[i]); - // if (!"Renamed".equals(apiResponse)) { - // testStatus = false; - // } - // } - // if (testStatus) { - // apiResponse = apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "[{\"code\":\"\",\"message\":\"Could not update the following files. - // \\n\\n\\t\\u2022 reference123\\n\\nYou do not have the required permissions to update - // attachments. Kindly contact the admin\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"Could not - // update the following files. \\n\\n\\t\\u2022 sample123\\n\\nYou do not have the required - // permissions to update attachments. Kindly contact the admin\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"Could not - // update the following files. \\n\\n\\t\\u2022 footnote123\\n\\nYou do not have the required - // permissions to update attachments. Kindly contact the admin\\n\\nTable: footnotes\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (!apiResponse.equals(expected)) { - // testStatus = false; - // } - // } else { - // apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // } catch (Exception e) { - // testStatus = false; - // } - // if (!testStatus) { - // fail("Attachment got renamed without SDM roles."); - // } - // } + String restrictedName = "a/\\bc.pdf"; + response = + api.renameAttachment(appUrl, entityName, facet[i], entityID, ID4[i], restrictedName); + } - // @Test - // @Order(16) - // void testDeleteSingleAttachment() throws IOException { - // System.out.println("Test (16) : Delete single attachment, reference, and footnote"); - // Boolean testStatus = false; - // counter = -1; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Entity in draft mode")) { - // for (int i = 0; i < facet.length; i++) { - // response = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID[i]); - // if (response.equals("Deleted")) counter++; - // } - // if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // counter = -1; // Reset counter for the next check - // if (response.equals("Saved")) { - // for (int i = 0; i < facet.length; i++) { - // response = api.readAttachment(appUrl, entityName, facet[i], entityID, ID[i]); - // if (response.equals("Could not read Attachment")) counter++; - // } - // if (counter >= 2) testStatus = true; - // else fail("Could not read deleted facets"); - // } else { - // fail("Could not save entity after deletion"); - // } - // } - // } + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // @Test - // @Order(17) - // void testDeleteMultipleAttachmentsReferencesFootnotes() throws IOException { - // System.out.println("Test (17) : Delete multiple attachments, references, and footnotes"); - // Boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Entity in draft mode")) { - // for (int i = 0; i < facet.length; i++) { - // String response1 = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID2[i]); - // String response2 = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID3[i]); - // if (response1.equals("Deleted") && response2.equals("Deleted")) counter++; - // } - // } - // if (counter >= 2) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // if (response.equals("Saved")) { - // for (int i = 0; i < facet.length; i++) { - // String response1 = api.readAttachment(appUrl, entityName, facet[i], entityID, ID2[i]); - // String response2 = api.readAttachment(appUrl, entityName, facet[i], entityID, ID3[i]); - // if (response1.equals("Could not read " + facet[i]) - // && response2.equals("Could not read " + facet[i])) { - // counter++; - // } - // } - // if (counter >= 2) testStatus = true; - // else fail("Could not read deleted facets"); - // } else fail("Could not save entity after deletion"); - // } + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + if (response.equals(expected)) { + api.deleteEntityDraft(appUrl, entityName, entityID); + testStatus = true; + } - // @Test - // @Order(18) - // void testUploadBlockedMimeType() throws IOException { - // System.out.println("Test (18) : Upload blocked mimeType .rtf"); - // Boolean testStatus = false; + if (!testStatus) { + fail("Facets renamed with restricted characters were not correctly rejected."); + } + } - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!"Could not create entity".equals(response)) { - // entityID2 = response; + @Test + @Order(10) + void testRenameEntitiesWithUnsupportedCharacter() { + System.out.println("Test (10) : Rename attachments with unsupported characters"); + Boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.rtf")).getFile()); + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String[] name = {"sample/1234", "reference1/234", "footnote1/234"}; + if (response.equals("Entity in draft mode")) { + for (int i = 0; i < facet.length; i++) { + response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name[i]); + if (response.equals("Renamed")) counter++; + } + if (counter >= 2) { + counter = -1; // Reset counter for the next check + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"reference1/234\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"sample/1234\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"footnote1/234\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + if (response.equals(expected)) { + for (int i = 0; i < facet.length; i++) { + response = + api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], "sample.pdf"); + } + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment was renamed with unsupported characters"); + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID2); - // postData.put("mimeType", "application/rtf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(11) + void testRenameMultipleEntityComponents() { + System.out.println("Test (11) : Rename multiple attachments, references, and footnotes"); + boolean testStatus = true; + + String draftResponse = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (!"Entity in draft mode".equals(draftResponse)) { + fail("Entity is not in draft mode."); + return; + } + String[] name = {"sample1234", "reference1234", "footnote1234"}; + String[] name2 = {"sample12345", "reference12345", "footnote12345"}; + for (int i = 0; i < facet.length; i++) { + // Read the facet to ensure it exists + testStatus &= renameAndCheck(facet[i], ID2[i], entityID, name[i]); + testStatus &= renameAndCheck(facet[i], ID3[i], entityID, name2[i]); + } + // Save the draft if all renames succeeded + if (testStatus) { + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (!"Saved".equals(saveResponse)) { + fail("Entity draft was not saved after renaming."); + } + } else { + // Save draft even if renaming failed to preserve state + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + fail("One or more components were not renamed."); + } + } - // boolean allBlocked = true; - // for (int i = 0; i < facet.length; i++) { - // List createResponse = - // api.createAttachment(appUrl, entityName, facet[i], entityID2, srvpath, postData, - // file); + @Test + @Order(12) + void testRenameSingleDuplicate() { + System.out.println("Test (12) : Rename duplicates for attachment, reference, and footnote"); + Boolean testStatus = false; - // String actualResponse = createResponse.get(0); - // String expectedJson = - // "{\"error\":{\"code\":\"500\",\"message\":\"This file type is not allowed in this - // repository. Contact your administrator for assistance.\"}}"; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String[] name = {"sample1234", "reference1234", "footnote1234"}; + String[] name2 = {"sample123456", "reference123456", "footnote123456"}; + if (response.equals("Entity in draft mode")) { + for (int i = 0; i < facet.length; i++) { + response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name[i]); + if (response.equals("Renamed")) counter++; + } + if (counter >= 2) { + counter = -1; // Reset counter for the next check + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + String.format( + "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"%s\\\" already exists. Rename the object and try again.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"An object named \\\"%s\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"%s\\\" already exists. Rename the object and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}", + name[1], name[0], name[2]); + if (response.equals(expected)) { + for (int i = 0; i < facet.length; i++) { + // Attempt to rename again with a different name + response = + api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], name2[i]); + if (response.equals("Renamed")) counter++; + } + } + if (counter >= 2) { + // If all renames were successful, save the draft + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + testStatus = true; + } + } else { + testStatus = false; + fail("Attachment was renamed"); + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + } - // if (!expectedJson.equals(actualResponse)) { - // allBlocked = false; - // System.out.println( - // "Facet " + facet[i] + " incorrectly accepted blocked mimeType: " + actualResponse); - // } - // } + @Test + @Order(13) + void testRenameMultipleEntitiesWithOneUnsupportedCharacter() { + System.out.println( + "Test (13) : Rename multiple files out of which one file name contains unsupported characters"); + boolean testStatus = false; - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // if ("Saved".equals(response) && allBlocked) { - // testStatus = true; - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String[] names = {"summary_1234", "reference_4567", "note/invalid"}; - // if (!testStatus) { - // fail("Attachment got uploaded with blocked .rtf MIME type"); - // } - // } + if (response.equals("Entity in draft mode")) { + int successCount = 0; + for (int i = 0; i < facet.length; i++) { + response = api.renameAttachment(appUrl, entityName, facet[i], entityID, ID3[i], names[i]); + if (response.equals("Renamed")) successCount++; + } - // @Test - // @Order(19) - // void testDeleteEntity() { - // System.out.println("Test (19) : Delete entity"); - // Boolean testStatus = false; - // String response = api.deleteEntity(appUrl, entityName, entityID); - // String response2 = api.deleteEntity(appUrl, entityName, entityID2); - // if (response.equals("Entity Deleted") && response2.equals("Entity Deleted")) testStatus = - // true; - // if (!testStatus) fail("Could not delete entity"); - // } + if (successCount >= 2) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"note/invalid\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\"}}"; + if (response.equals(expected)) { + response = + api.renameAttachment(appUrl, entityName, facet[2], entityID, ID3[2], "note_valid"); + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) testStatus = true; + } + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } - // @Test - // @Order(20) - // void testUpdateValidSecondaryProperty_beforeEntityIsSaved_single() throws IOException { - // System.out.println("Test (20) : Rename & Update secondary property before entity is saved"); - // System.out.println("Creating entity"); + if (!testStatus) { + fail("Attachment was renamed with unsupported characters"); + } + } - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + @Test + @Order(14) + void testRenameToValidateNames() throws IOException { + System.out.println("Test (14) : Rename attachments to validate names"); + String[] generatedIDs = new String[3]; + String[] duplicateIDs = new String[1]; + boolean testStatus = false, allRenamedSuccessfully = true; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID3 = response; - // if (!response.equals("Could not create entity")) { - // entityID3 = response; + String[] invalidNames = {"Restricted/Character", " ", "duplicateName.pdf"}; + String duplicateName = "duplicateName.pdf"; - // System.out.println("Creating attachment, reference, and footnote"); + ClassLoader classLoader = getClass().getClassLoader(); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); + // Creation of attachment, reference and footnote + for (int i = 0; i < facet.length; i++) { + File file = new File(classLoader.getResource("sample2.pdf").getFile()); + generatedIDs[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + response = + api.renameAttachment( + appUrl, entityName, facet[i], entityID3, generatedIDs[i], invalidNames[i]); + allRenamedSuccessfully &= "Renamed".equals(response); + } + File file = new File(classLoader.getResource("sample.pdf").getFile()); + // Creating duplicate name for last facet + duplicateIDs[0] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[2], entityID3, postData, file); + String response2 = + api.renameAttachment( + appUrl, entityName, facet[2], entityID3, duplicateIDs[0], duplicateName); + + if (allRenamedSuccessfully && "Renamed".equals(response2)) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"The object name cannot be empty or consist entirely of space characters. Enter a value.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"Restricted/Character\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"duplicateName.pdf\\\" already exists. Rename the object and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + if (response.equals(expected)) { + response = api.deleteEntityDraft(appUrl, entityName, entityID3); + if (response.equals("Entity Draft Deleted")) testStatus = true; + } + } + if (!testStatus) fail("Could not create entity"); + } else { + fail("Could not create entity"); + return; + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(15) + void testRenameEntitiesWithoutSDMRole() throws IOException { + System.out.println("Test (15) : Rename attachments where user don't have SDM-Roles"); + boolean testStatus = true; + try { + String apiResponse = apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, entityID); + if ("Entity in draft mode".equals(apiResponse)) { + String[] name = {"sample456", "reference456", "footnote456"}; + for (int i = 0; i < facet.length; i++) { + apiResponse = + apiNoRoles.renameAttachment(appUrl, entityName, facet[i], entityID, ID[i], name[i]); + if (!"Renamed".equals(apiResponse)) { + testStatus = false; + } + } + if (testStatus) { + apiResponse = apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "[{\"code\":\"\",\"message\":\"Could not update the following files. \\n\\n\\t\\u2022 reference123\\n\\nYou do not have the required permissions to update attachments. Kindly contact the admin\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"Could not update the following files. \\n\\n\\t\\u2022 sample123\\n\\nYou do not have the required permissions to update attachments. Kindly contact the admin\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"Could not update the following files. \\n\\n\\t\\u2022 footnote123\\n\\nYou do not have the required permissions to update attachments. Kindly contact the admin\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (!apiResponse.equals(expected)) { + testStatus = false; + } + } else { + apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + } catch (Exception e) { + testStatus = false; + } + if (!testStatus) { + fail("Attachment got renamed without SDM roles."); + } + } - // for (int i = 0; i < facet.length; i++) { - // ID[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + @Test + @Order(16) + void testDeleteSingleAttachment() throws IOException { + System.out.println("Test (16) : Delete single attachment, reference, and footnote"); + Boolean testStatus = false; + counter = -1; - // System.out.println("Attachments, References, and Footnotes created"); - - // // Use valid dropdown value for customProperty1 - // Integer secondaryPropertyInt = 1234; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - - // String[] name = {"sample1234.pdf", "reference1234.pdf", "footnote1234.pdf"}; - - // for (int i = 0; i < facet.length; i++) { - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name[i]); - - // // Update customProperty1 (String - dropdown value) - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - - // // Update customProperty2 (Integer) - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - - // // Update customProperty5 (DateTime) - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - - // // Update customProperty6 (Boolean) - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) { - // counter++; - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Entity in draft mode")) { + for (int i = 0; i < facet.length; i++) { + response = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID[i]); + if (response.equals("Deleted")) counter++; + } + if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + counter = -1; // Reset counter for the next check + if (response.equals("Saved")) { + for (int i = 0; i < facet.length; i++) { + response = api.readAttachment(appUrl, entityName, facet[i], entityID, ID[i]); + if (response.equals("Could not read Attachment")) counter++; + } + if (counter >= 2) testStatus = true; + else fail("Could not read deleted facets"); + } else { + fail("Could not save entity after deletion"); + } + } + } - // if (counter >= 2) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // } - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } + @Test + @Order(17) + void testDeleteMultipleAttachmentsReferencesFootnotes() throws IOException { + System.out.println("Test (17) : Delete multiple attachments, references, and footnotes"); + Boolean testStatus = false; - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Entity in draft mode")) { + for (int i = 0; i < facet.length; i++) { + String response1 = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID2[i]); + String response2 = api.deleteAttachment(appUrl, entityName, facet[i], entityID, ID3[i]); + if (response1.equals("Deleted") && response2.equals("Deleted")) counter++; + } + } + if (counter >= 2) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + if (response.equals("Saved")) { + for (int i = 0; i < facet.length; i++) { + String response1 = api.readAttachment(appUrl, entityName, facet[i], entityID, ID2[i]); + String response2 = api.readAttachment(appUrl, entityName, facet[i], entityID, ID3[i]); + if (response1.equals("Could not read " + facet[i]) + && response2.equals("Could not read " + facet[i])) { + counter++; + } + } + if (counter >= 2) testStatus = true; + else fail("Could not read deleted facets"); + } else fail("Could not save entity after deletion"); + } - // @Test - // @Order(21) - // void testUpdateValidSecondaryProperty_afterEntityIsSaved_single() { - // System.out.println("Test (21): Rename & Update secondary property after entity is saved"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // System.out.println("Editing entity"); - - // if (response.equals("Entity in draft mode")) { - // // Sample secondary properties - // String name[] = {"sample.pdf", "reference_sample.pdf", "footnote_sample.pdf"}; - // Integer secondaryPropertyInt = 42; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - - // System.out.println("Renaming and updating secondary properties for attachment"); - // for (int i = 0; i < facet.length; i++) { - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name[i]); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) counter++; - // } - // if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachment"); - // } - // // Clean up - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (!deleteEntityResponse.equals("Entity Deleted")) fail("Could not delete entity"); - // } - // if (!testStatus) fail("Could not update secondary properties after entity is saved"); - // } + @Test + @Order(18) + void testUploadBlockedMimeType() throws IOException { + System.out.println("Test (18) : Upload blocked mimeType .rtf"); + Boolean testStatus = false; - // @Test - // @Order(22) - // void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_single() throws IOException { - // System.out.println( - // "Test (22): Rename & Update invalid secondary property before entity is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID3 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!"Could not create entity".equals(response)) { + entityID2 = response; - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.rtf")).getFile()); - // for (int i = 0; i < facet.length; i++) { - // ID[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } - // // Prepare test data - // String name1 = "sample1234.pdf"; - // Integer secondaryPropertyInt = 1234; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // String invalidProperty = "testid"; - - // for (int i = 0; i < facet.length; i++) { - // // Rename and update secondary properties - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for invalid ID - // String updateSecondaryPropertyResponse4 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], invalidProperty); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) counter++; - // } - // if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // for (int i = 0; i < facet.length; i++) { - // Map FacetMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); - // assertEquals("sample.pdf", FacetMetadata.get("fileName")); - // assertNull(FacetMetadata.get("customProperty3")); - // assertNull(FacetMetadata.get("customProperty4")); - // assertNull(FacetMetadata.get("customProperty1_code")); - // assertNull(FacetMetadata.get("customProperty2")); - // assertNull(FacetMetadata.get("customProperty6")); - // assertNull(FacetMetadata.get("customProperty5")); - // } - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Rename & update secondary properties for attachment is - // unsuccessfull"); - // } - // } - // if (!testStatus) - // fail( - // "Could not update secondary property before entity is saved for attachment, reference, - // or footnote"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID2); + postData.put("mimeType", "application/rtf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(23) - // void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_single() throws IOException { - // System.out.println( - // "Test (23): Rename & Update invalid secondary property after entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Entity in draft mode")) { - // String name1 = "sample.pdf"; - // Integer secondaryPropertyInt = 12; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // String invalidProperty = "testidinvalid"; - - // for (int i = 0; i < facet.length; i++) { - // // Rename and update secondary properties - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - // // Update secondary properties for Drop down - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for invalid ID - // String updateSecondaryPropertyResponse4 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], invalidProperty); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) counter++; - // } - // if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // for (int i = 0; i < facet.length; i++) { - // Map FacetMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); - // assertEquals("sample.pdf", FacetMetadata.get("fileName")); - // assertNull(FacetMetadata.get("customProperty3")); - // assertNull(FacetMetadata.get("customProperty4")); - // assertNull(FacetMetadata.get("customProperty1_code")); - // assertNull(FacetMetadata.get("customProperty2")); - // assertNull(FacetMetadata.get("customProperty6")); - // assertNull(FacetMetadata.get("customProperty5")); - // } - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update secondary properties for attachment, reference, footnote is - // unsuccessfull"); - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (!deleteEntityResponse.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } - // if (!testStatus) - // fail( - // "Could not update secondary property after entity is saved for attachment, reference, - // or footnote"); - // } + boolean allBlocked = true; + for (int i = 0; i < facet.length; i++) { + List createResponse = + api.createAttachment(appUrl, entityName, facet[i], entityID2, srvpath, postData, file); - // @Test - // @Order(24) - // void testUpdateValidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (24): Rename & Update valid secondary properties for multiple facets before entity - // is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID3 = response; + String actualResponse = createResponse.get(0); + String expectedJson = + "{\"error\":{\"code\":\"500\",\"message\":\"This file type is not allowed in this repository. Contact your administrator for assistance.\"}}"; - // System.out.println("Entity created"); - // ClassLoader classLoader = getClass().getClassLoader(); + if (!expectedJson.equals(actualResponse)) { + allBlocked = false; + System.out.println( + "Facet " + facet[i] + " incorrectly accepted blocked mimeType: " + actualResponse); + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); + if ("Saved".equals(response) && allBlocked) { + testStatus = true; + } + } - // System.out.println("Creating attachment, reference, and footnote PDF"); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // for (int i = 0; i < facet.length; i++) { - // ID[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + if (!testStatus) { + fail("Attachment got uploaded with blocked .rtf MIME type"); + } + } - // System.out.println("Creating attachment, reference, and footnote TXT"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // postData.put("mimeType", "application/txt"); - // for (int i = 0; i < facet.length; i++) { - // ID2[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + @Test + @Order(19) + void testDeleteEntity() { + System.out.println("Test (19) : Delete entity"); + Boolean testStatus = false; + String response = api.deleteEntity(appUrl, entityName, entityID); + String response2 = api.deleteEntity(appUrl, entityName, entityID2); + if (response.equals("Entity Deleted") && response2.equals("Entity Deleted")) testStatus = true; + if (!testStatus) fail("Could not delete entity"); + } - // System.out.println("Creating attachment, reference, and footnote EXE"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // postData.put("mimeType", "application/exe"); - // for (int i = 0; i < facet.length; i++) { - // ID3[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } - // Boolean Updated1[] = new Boolean[3]; - // Boolean Updated2[] = new Boolean[3]; - // Boolean Updated3[] = new Boolean[3]; - // String name1 = "sample1234.pdf"; - // Integer secondaryPropertyInt1 = 1234; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // // PDF - // System.out.println("Renaming and updating secondary properties for PDF"); - // for (int i = 0; i < facet.length; i++) { - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) { - // Updated1[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); - // } - // } + @Test + @Order(20) + void testUpdateValidSecondaryProperty_beforeEntityIsSaved_single() throws IOException { + System.out.println("Test (20) : Rename & Update secondary property before entity is saved"); + System.out.println("Creating entity"); - // // TXT - // System.out.println("Renaming and updating secondary properties for TXT"); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], - // bodyBool); - // if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { - // Updated2[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); - // } - // } + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // // EXE - // System.out.println("Renaming and updating secondary properties for EXE"); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], - // bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponseEXE3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], - // bodyDate); - - // if (updateSecondaryPropertyResponseEXE1.equals("Updated") - // && updateSecondaryPropertyResponseEXE2.equals("Updated") - // && updateSecondaryPropertyResponseEXE3.equals("Updated")) { - // Updated3[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); - // } - // } - // if (Updated1[0] - // && Updated1[1] - // && Updated1[2] - // && Updated2[0] - // && Updated2[1] - // && Updated2[2] - // && Updated3[0] - // && Updated3[1] - // && Updated3[2]) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties"); - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + if (!response.equals("Could not create entity")) { + entityID3 = response; - // @Test - // @Order(25) - // void testUpdateValidSecondaryProperty_afterEntityIsSaved_multipleAttachments() { - // System.out.println( - // "Test (25): Rename & Update valid secondary properties for multiple facets after entity - // is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Entity in draft mode")) { - // Boolean Updated1[] = new Boolean[3]; - // Boolean Updated2[] = new Boolean[3]; - // Boolean Updated3[] = new Boolean[3]; - - // String name1 = "sample1.pdf"; - // Integer secondaryPropertyInt1 = 12; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // System.out.println("Renaming and updating secondary properties for PDF"); - // for (int i = 0; i < facet.length; i++) { - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - // // Update secondary properties for Drop down - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated")) { - // Updated1[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); - // } - // } + System.out.println("Creating attachment, reference, and footnote"); - // // TXT - // System.out.println("Renaming and updating secondary properties for TXT"); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], - // bodyBool); - // if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { - // Updated2[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // EXE - // System.out.println("Renaming and updating secondary properties for EXE"); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for Drop down - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], - // bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponseEXE3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], - // bodyDate); - - // if (updateSecondaryPropertyResponseEXE1.equals("Updated") - // && updateSecondaryPropertyResponseEXE2.equals("Updated") - // && updateSecondaryPropertyResponseEXE3.equals("Updated")) { - // Updated3[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (Updated1[0] - // && Updated1[1] - // && Updated1[2] - // && Updated2[0] - // && Updated2[1] - // && Updated2[2] - // && Updated3[0] - // && Updated3[1] - // && Updated3[2]) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachments"); - // } - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property after entity is saved"); - // } - // } + for (int i = 0; i < facet.length; i++) { + ID[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // @Test - // @Order(26) - // void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (26): Rename & Update invalid and valid secondary properties for multiple facets - // before entity is saved"); - // System.out.println("Creating entity"); + System.out.println("Attachments, References, and Footnotes created"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // Use valid dropdown value for customProperty1 + Integer secondaryPropertyInt = 1234; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // if (!"Could not create entity".equals(response)) { - // entityID3 = response; - // System.out.println("Entity created"); + String[] name = {"sample1234.pdf", "reference1234.pdf", "footnote1234.pdf"}; - // ClassLoader classLoader = getClass().getClassLoader(); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + for (int i = 0; i < facet.length; i++) { + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name[i]); + + // Update customProperty1 (String - dropdown value) + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + + // Update customProperty2 (Integer) + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + + // Update customProperty5 (DateTime) + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + + // Update customProperty6 (Boolean) + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) { + counter++; + } + } - // // Create PDF attachments - // postData.put("mimeType", "application/pdf"); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // for (int i = 0; i < facet.length; i++) { - // ID[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + if (counter >= 2) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + } + if (response.equals("Saved")) { + // --- CMIS backend validation (only for attachments facet) --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name[i], "cmis:name"); + assertEquals(name[i], cmisName, "CMIS should reflect renamed filename for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set for " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match for " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for " + facet[i]); + } + } + testStatus = true; + } + } - // // Create TXT attachments - // postData.put("mimeType", "application/txt"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // for (int i = 0; i < facet.length; i++) { - // ID2[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } - // // Create EXE attachments - // postData.put("mimeType", "application/exe"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // for (int i = 0; i < facet.length; i++) { - // ID3[i] = - // CreateandReturnFacetID( - // appUrl, serviceName, entityName, facet[i], entityID3, postData, file); - // } + @Test + @Order(21) + void testUpdateValidSecondaryProperty_afterEntityIsSaved_single() { + System.out.println("Test (21): Rename & Update secondary property after entity is saved"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + System.out.println("Editing entity"); - // Boolean[] Updated1 = new Boolean[3]; - // Boolean[] Updated2 = new Boolean[3]; - // Boolean[] Updated3 = new Boolean[3]; - - // String name1 = "sample1234.pdf"; - // String dropdownValue = - // integrationTestUtils.getDropDownValue(); // returns a plain string like "option-123" - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // Integer secondaryPropertyInt1 = 1234; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // String invalidPropertyPDF = "testidinvalidPDF"; - - // // Update PDF properties - // System.out.println("Renaming and updating secondary properties for PDF"); - // for (int i = 0; i < facet.length; i++) { - // String renameResp = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // "{ \"customProperty2\" : " + secondaryPropertyInt1 + " }"); - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // "{ \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\" }"); - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), "{ \"customProperty6\" : true }"); - - // String upd1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // String upd2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // String upd3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // String upd4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - // String updInvalid = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], invalidPropertyPDF); - - // if ("Renamed".equals(renameResp) - // && "Updated".equals(upd1) - // && "Updated".equals(upd2) - // && "Updated".equals(upd3) - // && "Updated".equals(upd4) - // && "Updated".equals(updInvalid)) { - // Updated1[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); - // } - // } + if (response.equals("Entity in draft mode")) { + // Sample secondary properties + String name[] = {"sample.pdf", "reference_sample.pdf", "footnote_sample.pdf"}; + Integer secondaryPropertyInt = 42; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // // Update TXT properties - // System.out.println("Renaming and updating secondary properties for TXT"); - // for (int i = 0; i < facet.length; i++) { - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), "{ \"customProperty6\" : true }"); - // String upd = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], - // bodyBool); - // if ("Updated".equals(upd)) { - // Updated2[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); - // } - // } + System.out.println("Renaming and updating secondary properties for attachment"); + for (int i = 0; i < facet.length; i++) { + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name[i]); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) counter++; + } + if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + // --- CMIS backend validation (only for attachments facet) --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name[i], "cmis:name"); + assertEquals(name[i], cmisName, "CMIS should reflect renamed filename for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set for " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match for " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name[i], "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for " + facet[i]); + } + } + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachment"); + } + // Clean up + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (!deleteEntityResponse.equals("Entity Deleted")) fail("Could not delete entity"); + } + if (!testStatus) fail("Could not update secondary properties after entity is saved"); + } - // // Update EXE properties - // System.out.println("Renaming and updating secondary properties for EXE"); - // String dropdownValueExe = integrationTestUtils.getDropDownValue(); - // String jsonDropdownExe = "{ \"customProperty1_code\" : \"" + dropdownValueExe + "\" }"; - - // for (int i = 0; i < facet.length; i++) { - // RequestBody bodyDropdownExe = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdownExe); - // RequestBody bodyIntExe = - // RequestBody.create( - // MediaType.parse("application/json"), "{ \"customProperty2\" : 1234 }"); - - // String upd1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdownExe); - // String upd2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID3[i], bodyIntExe); - - // if ("Updated".equals(upd1) && "Updated".equals(upd2)) { - // Updated3[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); - // } - // } + @Test + @Order(22) + void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_single() throws IOException { + System.out.println( + "Test (22): Rename & Update invalid secondary property before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID3 = response; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // if (Arrays.stream(Updated1).allMatch(Boolean.TRUE::equals) - // && Arrays.stream(Updated2).allMatch(Boolean.TRUE::equals) - // && Arrays.stream(Updated3).allMatch(Boolean.TRUE::equals)) { - - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // String[] expectedNames = {"sample.pdf", "sample.txt", "sample.exe"}; - - // // Verify PDF metadata - // for (int i = 0; i < facet.length; i++) { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); - // assertEquals(expectedNames[0], metadata.get("fileName")); - // assertNull(metadata.get("customProperty3")); - // assertNull(metadata.get("customProperty4")); - // assertNull(metadata.get("customProperty1_code")); - // assertNull(metadata.get("customProperty2")); - // assertNull(metadata.get("customProperty6")); - // assertNull(metadata.get("customProperty5")); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Verify TXT metadata - // for (int i = 0; i < facet.length; i++) { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID2[i]); - // assertEquals(expectedNames[1], metadata.get("fileName")); - // assertNull(metadata.get("customProperty3")); - // assertNull(metadata.get("customProperty4")); - // assertNull(metadata.get("customProperty1_code")); - // assertNull(metadata.get("customProperty2")); - // assertTrue((Boolean) metadata.get("customProperty6")); - // assertNull(metadata.get("customProperty5")); - // } + for (int i = 0; i < facet.length; i++) { + ID[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } + // Prepare test data + String name1 = "sample1234.pdf"; + Integer secondaryPropertyInt = 1234; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + String invalidProperty = "testid"; - // // Verify EXE metadata - // for (int i = 0; i < facet.length; i++) { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID3[i]); - // assertEquals(expectedNames[2], metadata.get("fileName")); - // assertNull(metadata.get("customProperty3")); - // assertNull(metadata.get("customProperty4")); - // assertEquals( - // dropdownValueExe, - // metadata.get("customProperty1_code")); // Adjust expected value if needed - // assertEquals(1234, metadata.get("customProperty2")); - // } + for (int i = 0; i < facet.length; i++) { + // Rename and update secondary properties + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for invalid ID + String updateSecondaryPropertyResponse4 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], invalidProperty); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) counter++; + } + if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + for (int i = 0; i < facet.length; i++) { + Map FacetMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); + assertEquals("sample.pdf", FacetMetadata.get("fileName")); + assertNull(FacetMetadata.get("customProperty3")); + assertNull(FacetMetadata.get("customProperty4")); + assertNull(FacetMetadata.get("customProperty1_code")); + assertNull(FacetMetadata.get("customProperty2")); + assertNull(FacetMetadata.get("customProperty6")); + assertNull(FacetMetadata.get("customProperty5")); + } + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation: no changes should persist in DI (only for attachments facet) + // --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", cmisName, "Filename should NOT be changed in CMIS for " + facet[i]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordString"); + assertNull( + cmisString, + "Valid props should not persist when invalid props cause rejection for " + + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordInt"); + assertNull( + cmisInt, + "Valid props should not persist when invalid props cause rejection for " + + facet[i]); + } + } + testStatus = true; + System.out.println("Rename & update secondary properties for attachment is unsuccessfull"); + } + } + if (!testStatus) + fail( + "Could not update secondary property before entity is saved for attachment, reference, or footnote"); + } - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update unsuccessful for invalid properties and successful for valid - // attachments"); - // } - // } - // } + @Test + @Order(23) + void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_single() throws IOException { + System.out.println( + "Test (23): Rename & Update invalid secondary property after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Entity in draft mode")) { + String name1 = "sample.pdf"; + Integer secondaryPropertyInt = 12; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + String invalidProperty = "testidinvalid"; - // @Test - // @Order(27) - // void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (27): Rename & Update invalid and valid secondary properties for multiple - // attachments after entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Entity in draft mode")) { - // Boolean Updated1[] = new Boolean[3]; - // Boolean Updated2[] = new Boolean[3]; - // Boolean Updated3[] = new Boolean[3]; - // String name1 = "sample.pdf"; - // Integer secondaryPropertyInt1 = 12; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // String invalidPropertyPDF = "testidinvalidPDF"; - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // System.out.println("drop down value is: " + dropdownValue); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - - // // PDF - // System.out.println("Renaming and updating secondary properties for PDF"); - // for (int i = 0; i < facet.length; i++) { - // String response1 = - // api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); - // // Update secondary properties for String - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); - // // Update secondary properties for LocalDateTime - // RequestBody bodyDate = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyDate); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], - // bodyBool); - // // Update invalid secondary property - // String updateSecondaryPropertyResponse5 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID[i], invalidPropertyPDF); - - // if (response1.equals("Renamed") - // && updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponse2.equals("Updated") - // && updateSecondaryPropertyResponse3.equals("Updated") - // && updateSecondaryPropertyResponse4.equals("Updated") - // && updateSecondaryPropertyResponse5.equals("Updated")) { - // Updated1[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); - // } - // } - // // TXT - // System.out.println("Renaming and updating secondary properties for TXT"); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + false + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], - // bodyBool); - // if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { - // Updated2[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); - // } - // } + for (int i = 0; i < facet.length; i++) { + // Rename and update secondary properties + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + // Update secondary properties for Drop down + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for invalid ID + String updateSecondaryPropertyResponse4 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], invalidProperty); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) counter++; + } + if (counter >= 2) response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + for (int i = 0; i < facet.length; i++) { + Map FacetMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); + assertEquals("sample.pdf", FacetMetadata.get("fileName")); + assertNull(FacetMetadata.get("customProperty3")); + assertNull(FacetMetadata.get("customProperty4")); + assertNull(FacetMetadata.get("customProperty1_code")); + assertNull(FacetMetadata.get("customProperty2")); + assertNull(FacetMetadata.get("customProperty6")); + assertNull(FacetMetadata.get("customProperty5")); + } + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation: no changes should persist in DI (only for attachments facet) + // --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", cmisName, "Filename should NOT be changed in CMIS for " + facet[i]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist for " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordString"); + assertNull( + cmisString, + "Valid props should not persist when invalid props cause rejection for " + + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordInt"); + assertNull( + cmisInt, + "Valid props should not persist when invalid props cause rejection for " + + facet[i]); + } + } + testStatus = true; + System.out.println( + "Rename & update secondary properties for attachment, reference, footnote is unsuccessfull"); + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (!deleteEntityResponse.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } + if (!testStatus) + fail( + "Could not update secondary property after entity is saved for attachment, reference, or footnote"); + } - // Integer secondaryPropertyInt3 = 12; - // // EXE - // System.out.println("Renaming and updating secondary properties for EXE"); - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // for (int i = 0; i < facet.length; i++) { - // // Update secondary properties for String - // System.out.println("drop down value is: " + dropdownValue1); - // String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown1); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], - // bodyInt); - - // if (updateSecondaryPropertyResponse1.equals("Updated") - // && updateSecondaryPropertyResponseEXE2.equals("Updated")) { - // Updated3[i] = true; - // System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); - // } - // } + @Test + @Order(24) + void testUpdateValidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (24): Rename & Update valid secondary properties for multiple facets before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID3 = response; - // if (Updated1[0] - // && Updated1[1] - // && Updated1[2] - // && Updated2[0] - // && Updated2[1] - // && Updated2[2] - // && Updated3[0] - // && Updated3[1] - // && Updated3[2]) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // String name[] = {"sample.pdf", "sample.txt", "sample.exe"}; - // // for PDF - // for (int i = 0; i < facet.length; i++) { - // Map FacetMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); - // assertEquals(name[0], FacetMetadata.get("fileName")); - // assertNull(FacetMetadata.get("customProperty3")); - // assertNull(FacetMetadata.get("customProperty4")); - // assertNull(FacetMetadata.get("customProperty1_code")); - // assertNull(FacetMetadata.get("customProperty2")); - // assertNull(FacetMetadata.get("customProperty6")); - // assertNull(FacetMetadata.get("customProperty5")); - // } - // // for TXT - // for (int i = 0; i < facet.length; i++) { - // Map FacetMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID2[i]); - // assertEquals(name[1], FacetMetadata.get("fileName")); - // assertNull(FacetMetadata.get("customProperty3")); - // assertNull(FacetMetadata.get("customProperty4")); - // assertNull(FacetMetadata.get("customProperty1_code")); - // assertNull(FacetMetadata.get("customProperty2")); - // assertFalse((Boolean) FacetMetadata.get("customProperty6")); - // assertNull(FacetMetadata.get("customProperty5")); - // } - // // for EXE - // for (int i = 0; i < facet.length; i++) { - // Map FacetMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID3[i]); - // assertEquals(name[2], FacetMetadata.get("fileName")); - // assertNull(FacetMetadata.get("customProperty3")); - // assertNull(FacetMetadata.get("customProperty4")); - // assertEquals(dropdownValue1, FacetMetadata.get("customProperty1_code")); - // assertEquals(12, FacetMetadata.get("customProperty2")); - // } + System.out.println("Entity created"); + ClassLoader classLoader = getClass().getClassLoader(); - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following - // secondary properties are not supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update unsuccessfull for invalid Secondary properties and successfull for - // valid property attachments"); - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(28) - // void testNAttachments_NewEntity() throws IOException { - // System.out.println( - // "Test (28): Creating new entity and checking only max 4 attachments are allowed to be - // uploaded"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID4 = response; + System.out.println("Creating attachment, reference, and footnote PDF"); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + for (int i = 0; i < facet.length; i++) { + ID[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // System.out.println("Entity created"); + System.out.println("Creating attachment, reference, and footnote TXT"); + file = new File(classLoader.getResource("sample.txt").getFile()); + postData.put("mimeType", "application/txt"); + for (int i = 0; i < facet.length; i++) { + ID2[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // System.out.println("Creating attachment PDF"); - // ClassLoader classLoader = getClass().getClassLoader(); + System.out.println("Creating attachment, reference, and footnote EXE"); + file = new File(classLoader.getResource("sample.exe").getFile()); + postData.put("mimeType", "application/exe"); + for (int i = 0; i < facet.length; i++) { + ID3[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } + boolean Updated1[] = new boolean[3]; + boolean Updated2[] = new boolean[3]; + boolean Updated3[] = new boolean[3]; + String name1 = "sample1234.pdf"; + Integer secondaryPropertyInt1 = 1234; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + // PDF + System.out.println("Renaming and updating secondary properties for PDF"); + for (int i = 0; i < facet.length; i++) { + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) { + Updated1[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); + } + } - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID4); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); + // TXT + System.out.println("Renaming and updating secondary properties for TXT"); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], bodyBool); + if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { + Updated2[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); + } + } - // List createResponse1 = - // api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData1, - // file); - // if (createResponse1.get(0).equals("Attachment created")) { - // ID[0] = createResponse1.get(1); - // System.out.println("Attachment created"); - // } + // EXE + System.out.println("Renaming and updating secondary properties for EXE"); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponseEXE3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], bodyDate); + + if (updateSecondaryPropertyResponseEXE1.equals("Updated") + && updateSecondaryPropertyResponseEXE2.equals("Updated") + && updateSecondaryPropertyResponseEXE3.equals("Updated")) { + Updated3[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); + } + } + if (Updated1[0] + && Updated1[1] + && Updated1[2] + && Updated2[0] + && Updated2[1] + && Updated2[2] + && Updated3[0] + && Updated3[1] + && Updated3[2]) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation (only for attachments facet) --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals( + name1, cmisName, "CMIS should reflect renamed filename for PDF " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordString"); + assertNotNull( + cmisString, "DocumentInfoRecordString should be set for PDF " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisInt, + "DocumentInfoRecordInt should match for PDF " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for PDF " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for PDF " + facet[i]); + } + } + // TXT - only Boolean was set + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be true for TXT " + facet[i]); + } + } + // EXE - String, Int, Date were set + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[i]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisIntExe, + "DocumentInfoRecordInt should match for EXE " + facet[i]); + } + } + testStatus = true; + System.out.println("Renamed & updated Secondary properties"); + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } - // System.out.println("Creating attachment TXT"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData2 = new HashMap<>(); - // postData2.put("up__ID", entityID4); - // postData2.put("mimeType", "application/txt"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // List createResponse2 = - // api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData2, - // file); - // if (createResponse2.get(0).equals("Attachment created")) { - // ID2[0] = createResponse2.get(1); - // System.out.println("Attachment created"); - // } + @Test + @Order(25) + void testUpdateValidSecondaryProperty_afterEntityIsSaved_multipleAttachments() { + System.out.println( + "Test (25): Rename & Update valid secondary properties for multiple facets after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Entity in draft mode")) { + boolean Updated1[] = new boolean[3]; + boolean Updated2[] = new boolean[3]; + boolean Updated3[] = new boolean[3]; + + String name1 = "sample1.pdf"; + Integer secondaryPropertyInt1 = 12; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + System.out.println("Renaming and updating secondary properties for PDF"); + for (int i = 0; i < facet.length; i++) { + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + // Update secondary properties for Drop down + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated")) { + Updated1[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); + } + } - // System.out.println("Creating attachment EXE"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // Map postData3 = new HashMap<>(); - // postData3.put("up__ID", entityID4); - // postData3.put("mimeType", "application/exe"); - // postData3.put("createdAt", new Date().toString()); - // postData3.put("createdBy", "test@test.com"); - // postData3.put("modifiedBy", "test@test.com"); - - // List createResponse3 = - // api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, - // file); - // if (createResponse3.get(0).equals("Attachment created")) { - // ID[0] = createResponse3.get(1); - // System.out.println("Attachment created"); - // } + // TXT + System.out.println("Renaming and updating secondary properties for TXT"); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], bodyBool); + if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { + Updated2[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); + } + } - // System.out.println("Creating second attachment pdf"); - // file = new File(classLoader.getResource("sample1.pdf").getFile()); - // Map postData4 = new HashMap<>(); - // postData4.put("up__ID", entityID4); - // postData4.put("mimeType", "application/pdf"); - // postData4.put("createdAt", new Date().toString()); - // postData4.put("createdBy", "test@test.com"); - // postData4.put("modifiedBy", "test@test.com"); - - // List createResponse4 = - // api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, - // file); - // if (createResponse4.get(0).equals("Attachment created")) { - // ID4[0] = createResponse4.get(1); - // System.out.println("Attachment created"); - // } + // EXE + System.out.println("Renaming and updating secondary properties for EXE"); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for Drop down + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponseEXE3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], bodyDate); + + if (updateSecondaryPropertyResponseEXE1.equals("Updated") + && updateSecondaryPropertyResponseEXE2.equals("Updated") + && updateSecondaryPropertyResponseEXE3.equals("Updated")) { + Updated3[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); + } + } - // System.out.println("Creating third attachment pdf"); - // file = new File(classLoader.getResource("sample2.pdf").getFile()); - // Map postData5 = new HashMap<>(); - // postData5.put("up__ID", entityID4); - // postData5.put("mimeType", "application/pdf"); - // postData5.put("createdAt", new Date().toString()); - // postData5.put("createdBy", "test@test.com"); - // postData5.put("modifiedBy", "test@test.com"); - - // List createResponse5 = - // api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, - // file); - // if (createResponse5.get(0).equals("Only 4 attachments allowed.")) { - // testStatus = true; - // ID5[0] = createResponse5.get(1); - // System.out.println("Expected error received: Only 4 attachments allowed."); - // } - // String check = createResponse5.get(0); - // if (check.equals("Attachment created")) { - // testStatus = false; - // } else { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID4); - // if (response.equals("Saved")) { - // String expectedJson = - // "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 - // attachments.\"}}"; - // ObjectMapper objectMapper = new ObjectMapper(); - // JsonNode actualJsonNode = objectMapper.readTree(check); - // JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); - // if (expectedJsonNode.equals(actualJsonNode)) { - // testStatus = true; - // } - // } - // } - // } - // if (!testStatus) { - // fail("Attachment was created"); - // } - // } + if (Updated1[0] + && Updated1[1] + && Updated1[2] + && Updated2[0] + && Updated2[1] + && Updated2[2] + && Updated3[0] + && Updated3[1] + && Updated3[2]) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation (only for attachments facet) --- + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals( + name1, cmisName, "CMIS should reflect renamed filename for PDF " + facet[i]); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordString"); + assertNotNull( + cmisString, "DocumentInfoRecordString should be set for PDF " + facet[i]); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisInt, + "DocumentInfoRecordInt should match for PDF " + facet[i]); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", cmisBool, "DocumentInfoRecordBoolean should be true for PDF " + facet[i]); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set for PDF " + facet[i]); + } + } + // TXT - only Boolean was set + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be true for TXT " + facet[i]); + } + } + // EXE - String, Int, Date were set + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[i]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisIntExe, + "DocumentInfoRecordInt should match for EXE " + facet[i]); + } + } + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachments"); + } + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + if (!testStatus) { + fail("Could not update secondary property after entity is saved"); + } + } - // @Test - // @Order(29) - // void testUploadNAttachments() throws IOException { - // System.out.println("Test (29): Upload maximum 4 attachments in an exsisting entity"); + @Test + @Order(26) + void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (26): Rename & Update invalid and valid secondary properties for multiple facets before entity is saved"); + System.out.println("Creating entity"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File originalFile = new File(classLoader.getResource("sample.exe").getFile()); - - // boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID4); - // System.out.println("response: " + response); - - // if ("Entity in draft mode".equals(response)) { - // for (int i = 1; i <= 5; i++) { - // // Ensure only one file is uploaded at a time and complete before next - // File tempFile = File.createTempFile("sample_" + i + "_", ".exe"); - // Files.copy(originalFile.toPath(), tempFile.toPath(), - // StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID4); - // postData.put("mimeType", "application/exe"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[0], entityID4, srvpath, postData, tempFile); - - // String resultMessage = createResponse.get(0); - // System.out.println("Result message for attachment " + i + ": " + resultMessage); - - // String expectedResponse = - // "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 - // attachments.\"}}"; - // if (resultMessage.equals(expectedResponse)) { - // ObjectMapper objectMapper = new ObjectMapper(); - // JsonNode actualJsonNode = objectMapper.readTree(resultMessage); - // JsonNode expectedJsonNode = objectMapper.readTree(expectedResponse); - // if (expectedJsonNode.equals(actualJsonNode)) { - // testStatus = true; - // } - // } else { - // testStatus = false; - // } - // tempFile.delete(); - // } - // if (!testStatus) { - // fail("5th attachment did not trigger the expected error."); - // } - // // Delete the newly created entity - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID4); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } else { - // System.out.println("Successfully deleted the test entity4"); - // } - // } - // } + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // @Test - // @Order(30) - // void testDiscardDraftWithoutAttachments() { - // System.out.println("Test (30) : Discard draft without adding attachments"); - // Boolean testStatus = false; + if (!"Could not create entity".equals(response)) { + entityID3 = response; + System.out.println("Entity created"); - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID6 = response; - // response = api.deleteEntityDraft(appUrl, entityName, entityID6); - // if (response.equals("Entity Draft Deleted")) { - // testStatus = true; - // } - // } - // if (!testStatus) { - // fail("Draft was not discarded properly"); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(31) - // void testDiscardDraftWithAttachments() throws IOException { - // System.out.println("Test (31): Discard draft with attachments, references, and footnotes"); - // boolean testStatus = false; + // Create PDF attachments + postData.put("mimeType", "application/pdf"); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + for (int i = 0; i < facet.length; i++) { + ID[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!"Could not create entity".equals(response)) { - // entityID6 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); + // Create TXT attachments + postData.put("mimeType", "application/txt"); + file = new File(classLoader.getResource("sample.txt").getFile()); + for (int i = 0; i < facet.length; i++) { + ID2[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID6); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - // for (int i = 0; i < facet.length; i++) { - // List createResponse = - // api.createAttachment(appUrl, entityName, facet[i], entityID6, srvpath, postData, - // file); - // if ("Attachment created".equals(createResponse.get(0))) { - // System.out.println("Attachment created in facet: " + facet[i]); - // } else { - // System.out.println("Attachment creation failed in facet: " + facet[i]); - // } - // } - // response = api.deleteEntityDraft(appUrl, entityName, entityID6); - // if ("Entity Draft Deleted".equals(response)) { - // testStatus = true; - // } - // } - // if (!testStatus) { - // fail("Draft with attachments was not discarded properly"); - // } - // } + // Create EXE attachments + postData.put("mimeType", "application/exe"); + file = new File(classLoader.getResource("sample.exe").getFile()); + for (int i = 0; i < facet.length; i++) { + ID3[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], entityID3, postData, file); + } - // @Test - // @Order(32) - // void testDraftUpdateUploadTwoDeleteOneAndCreate() throws IOException { - // System.out.println("Test (32): Upload to all facets, delete one, and create entity"); + Boolean[] Updated1 = new Boolean[3]; + Boolean[] Updated2 = new Boolean[3]; + Boolean[] Updated3 = new Boolean[3]; - // boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + String name1 = "sample1234.pdf"; + String dropdownValue = + integrationTestUtils.getDropDownValue(); // returns a plain string like "option-123" + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + Integer secondaryPropertyInt1 = 1234; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + String invalidPropertyPDF = "testidinvalidPDF"; - // if (!"Could not create entity".equals(response)) { - // entityID5 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - - // File file1 = - // new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // File file2 = - // new File(Objects.requireNonNull(classLoader.getResource("sample.txt")).getFile()); - - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID5); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // Map postData2 = new HashMap<>(postData1); - // postData2.put("up__ID", entityID5); - // postData2.put("mimeType", "text/plain"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // boolean allCreated = true; - // for (int i = 0; i < facet.length; i++) { - // List response1 = - // api.createAttachment( - // appUrl, entityName, facet[i], entityID5, srvpath, postData1, file1); - // List response2 = - // api.createAttachment( - // appUrl, entityName, facet[i], entityID5, srvpath, postData2, file2); - - // if (response1.get(0).equals("Attachment created") - // && response2.get(0).equals("Attachment created")) { - // ID4[i] = response1.get(1); // to keep one - // ID5[i] = response2.get(1); // will delete this one - // } else { - // allCreated = false; - // break; - // } + // Update PDF properties + System.out.println("Renaming and updating secondary properties for PDF"); + for (int i = 0; i < facet.length; i++) { + String renameResp = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + "{ \"customProperty2\" : " + secondaryPropertyInt1 + " }"); + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + "{ \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\" }"); + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), "{ \"customProperty6\" : true }"); + + String upd1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + String upd2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + String upd3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + String upd4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + String updInvalid = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], invalidPropertyPDF); + + if ("Renamed".equals(renameResp) + && "Updated".equals(upd1) + && "Updated".equals(upd2) + && "Updated".equals(upd3) + && "Updated".equals(upd4) + && "Updated".equals(updInvalid)) { + Updated1[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); + } + } - // String deleteResponse = - // api.deleteAttachment(appUrl, entityName, facet[i], entityID5, ID5[i]); - // if (!"Deleted".equals(deleteResponse)) { - // allCreated = false; - // break; - // } - // } + // Update TXT properties + System.out.println("Renaming and updating secondary properties for TXT"); + for (int i = 0; i < facet.length; i++) { + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), "{ \"customProperty6\" : true }"); + String upd = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], bodyBool); + if ("Updated".equals(upd)) { + Updated2[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); + } + } - // if (allCreated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); - // if ("Saved".equals(response)) { - // testStatus = true; - // } - // } - // } + // Update EXE properties + System.out.println("Renaming and updating secondary properties for EXE"); + String dropdownValueExe = integrationTestUtils.getDropDownValue(); + String jsonDropdownExe = "{ \"customProperty1_code\" : \"" + dropdownValueExe + "\" }"; - // if (!testStatus) { - // fail("Failed to upload multiple facet entries, delete one per facet and create entity"); - // } - // } + for (int i = 0; i < facet.length; i++) { + RequestBody bodyDropdownExe = + RequestBody.create(MediaType.parse("application/json"), jsonDropdownExe); + RequestBody bodyIntExe = + RequestBody.create( + MediaType.parse("application/json"), "{ \"customProperty2\" : 1234 }"); + + String upd1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdownExe); + String upd2 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID3[i], bodyIntExe); + + if ("Updated".equals(upd1) && "Updated".equals(upd2)) { + Updated3[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); + } + } - // @Test - // @Order(33) - // void testUpdateEntityDraft() throws IOException { - // System.out.println("Test (33): Update entity draft with new facet content"); - // boolean testStatus = false; + if (Arrays.stream(Updated1).allMatch(Boolean.TRUE::equals) + && Arrays.stream(Updated2).allMatch(Boolean.TRUE::equals) + && Arrays.stream(Updated3).allMatch(Boolean.TRUE::equals)) { - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID5); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID5); - // if ("Entity in draft mode".equals(response)) { - // boolean allCreated = true; - - // for (int i = 0; i < facet.length; i++) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], entityID5, srvpath, postData, tempFile); - // if (!"Attachment created".equals(createResponse.get(0))) { - // allCreated = false; - // } - // } + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + String[] expectedNames = {"sample.pdf", "sample.txt", "sample.exe"}; - // if (allCreated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); - // if ("Saved".equals(response)) { - // testStatus = true; - // } - // } - // } - // api.deleteEntity(appUrl, entityName, entityID5); - // if (!testStatus) { - // fail("Failed to update draft with new attachments for all facets"); - // } - // } + // Verify PDF metadata + for (int i = 0; i < facet.length; i++) { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); + assertEquals(expectedNames[0], metadata.get("fileName")); + assertNull(metadata.get("customProperty3")); + assertNull(metadata.get("customProperty4")); + assertNull(metadata.get("customProperty1_code")); + assertNull(metadata.get("customProperty2")); + assertNull(metadata.get("customProperty6")); + assertNull(metadata.get("customProperty5")); + } - // @Test - // @Order(34) - // void testUploadAttachmentWithoutSDMRole() throws IOException { - // System.out.println("Test (34): Upload attachment across facets without SDM role"); - // boolean testStatus = true; - - // String response = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID7 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // for (int i = 0; i < facet.length; i++) { - // List createResponse = - // apiNoRoles.createAttachment( - // appUrl, entityName, facet[i], entityID7, srvpath, postData, tempFile); - // String check = createResponse.get(0); - // String expectedError = - // "{\"error\":{\"code\":\"500\",\"message\":\"You do not have the required permissions - // to upload attachments. Please contact your administrator for access.\"}}"; - // if (!expectedError.equals(check)) { - // testStatus = false; - // } - // } - // } - // api.deleteEntityDraft(appUrl, entityName, entityID7); - // if (!testStatus) { - // fail("Attachment uploaded without SDM role for one or more facets"); - // } - // } - - // @Test - // @Order(35) - // void testCopyAttachmentsSuccessNewEntity() throws IOException { - // System.out.println("Test (35): Copy attachments from one entity to another new entity"); - // List> attachments = new ArrayList<>(); - // for (int i = 0; i < 3; i++) { - // attachments.add(new ArrayList<>()); - // } - // copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!copyAttachmentSourceEntity.equals("Could not create entity") - // && !copyAttachmentTargetEntity.equals("Could not create entity")) { - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample1.pdf").getFile())); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // for (int i = 0; i < facet.length; i++) { - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facet[i], - // copyAttachmentSourceEntity, - // srvpath, - // postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.get(i).add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } - // } - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (int i = 0; i < attachments.size(); i++) { - // for (String attachment : attachments.get(i)) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadataDraft( - // appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } - // } - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - - // if (sourceObjectIds.size() == 6) { - // String copyResponse; - // int i = 0; - // for (String facetName : facet) { - // if (i != 0) { - // String editResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } - // } - // copyResponse = - // api.copyAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size()))); - // i += 2; - // if (copyResponse.equals("Attachments copied successfully")) { - // // Fetch copied attachment IDs from target draft - // List> copiedMetadataResponse = - // api.fetchEntityMetadata(appUrl, entityName, facetName, - // copyAttachmentTargetEntity); - // List copiedAttachmentIds = - // copiedMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadata( - // appUrl, entityName, facetName, copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not create entities"); - // } - // } - - // @Test - // @Order(36) - // void testCopyAttachmentsUnsuccessfulNewEntity() throws IOException { - // System.out.println( - // "Test (36): Copy incorrect attachments from one entity to another new entity"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // copyAttachmentTargetEntityEmpty = - // api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (editResponse1.equals("Entity in draft mode") - // && !copyAttachmentTargetEntityEmpty.equals("Could not create entity")) { - // if (sourceObjectIds.size() == 6) { - // int i = 0; - // for (String facet : facet) { - // try { - // List currentFacetObjectIds = - // sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); - // currentFacetObjectIds.add("incorrectObjectId"); - // if (currentFacetObjectIds.size() != 3) { - // fail("Not enough object IDs to copy attachments for facet: " + facet); - // } - // api.copyAttachment( - // appUrl, entityName, facet, copyAttachmentTargetEntityEmpty, - // currentFacetObjectIds); - // fail("Copy attachments did not throw an error"); - // } catch (IOException e) { - // i += 2; - // } - // } - // String saveEntityResponse1 = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String saveEntityResponse2 = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntityEmpty); - // String deleteResponse = - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntityEmpty); - // if (!saveEntityResponse1.equals("Saved") - // || !saveEntityResponse2.equals("Saved") - // || !deleteResponse.equals("Entity Deleted")) { - // fail("Could not save entities"); - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + // Verify TXT metadata + for (int i = 0; i < facet.length; i++) { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID2[i]); + assertEquals(expectedNames[1], metadata.get("fileName")); + assertNull(metadata.get("customProperty3")); + assertNull(metadata.get("customProperty4")); + assertNull(metadata.get("customProperty1_code")); + assertNull(metadata.get("customProperty2")); + assertTrue((Boolean) metadata.get("customProperty6")); + assertNull(metadata.get("customProperty5")); + } - // @Test - // @Order(37) - // void testCopyAttachmentWithNotesField() throws IOException { - // System.out.println( - // "Test (37): Create entity with attachments containing notes in multiple facets, copy to - // new entity and verify notes field"); - // Boolean testStatus = false; + // Verify EXE metadata + for (int i = 0; i < facet.length; i++) { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID3[i]); + assertEquals(expectedNames[2], metadata.get("fileName")); + assertNull(metadata.get("customProperty3")); + assertNull(metadata.get("customProperty4")); + assertEquals( + dropdownValueExe, + metadata.get("customProperty1_code")); // Adjust expected value if needed + assertEquals(1234, metadata.get("customProperty2")); + } - // copyCustomSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyCustomSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation (only for attachments facet) --- + // PDF: invalid prop was used, so nothing should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", + cmisName, + "PDF filename should NOT be changed in CMIS for " + facet[i]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull( + cmisId1, "Invalid property abc:myId1 should not exist for PDF " + facet[i]); + } + } + // TXT: only valid Boolean was set — should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "true", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be true for TXT " + facet[i]); + } + } + // EXE: valid String + Int were set — should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[i]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertNotNull(cmisIntExe, "DocumentInfoRecordInt should be set for EXE " + facet[i]); + } + } + testStatus = true; + System.out.println( + "Rename & update unsuccessful for invalid properties and successful for valid attachments"); + } + } + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // String notesValue = "This is a test note for copy attachment verification"; - // MediaType mediaType = MediaType.parse("application/json"); + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } - // for (String facetName : facet) { - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(27) + void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (27): Rename & Update invalid and valid secondary properties for multiple attachments after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Entity in draft mode")) { + boolean Updated1[] = new boolean[3]; + boolean Updated2[] = new boolean[3]; + boolean Updated3[] = new boolean[3]; + String name1 = "sample.pdf"; + Integer secondaryPropertyInt1 = 12; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + String invalidPropertyPDF = "testidinvalidPDF"; + String dropdownValue = integrationTestUtils.getDropDownValue(); + System.out.println("drop down value is: " + dropdownValue); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + + // PDF + System.out.println("Renaming and updating secondary properties for PDF"); + for (int i = 0; i < facet.length; i++) { + String response1 = + api.renameAttachment(appUrl, entityName, facet[i], entityID3, ID[i], name1); + // Update secondary properties for String + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyInt); + // Update secondary properties for LocalDateTime + RequestBody bodyDate = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyDate); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID[i], bodyBool); + // Update invalid secondary property + String updateSecondaryPropertyResponse5 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID[i], invalidPropertyPDF); + + if (response1.equals("Renamed") + && updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponse2.equals("Updated") + && updateSecondaryPropertyResponse3.equals("Updated") + && updateSecondaryPropertyResponse4.equals("Updated") + && updateSecondaryPropertyResponse5.equals("Updated")) { + Updated1[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " PDF"); + } + } + // TXT + System.out.println("Renaming and updating secondary properties for TXT"); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + false + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID2[i], bodyBool); + if (updateSecondaryPropertyResponseTXT1.equals("Updated")) { + Updated2[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " TXT"); + } + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + Integer secondaryPropertyInt3 = 12; + // EXE + System.out.println("Renaming and updating secondary properties for EXE"); + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + for (int i = 0; i < facet.length; i++) { + // Update secondary properties for String + System.out.println("drop down value is: " + dropdownValue1); + String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], entityID3, ID3[i], bodyDropdown1); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty(appUrl, entityName, facet[i], entityID3, ID3[i], bodyInt); + + if (updateSecondaryPropertyResponse1.equals("Updated") + && updateSecondaryPropertyResponseEXE2.equals("Updated")) { + Updated3[i] = true; + System.out.println("Renamed & updated Secondary properties for " + facet[i] + " EXE"); + } + } - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment in facet: " + facetName); - // } + if (Updated1[0] + && Updated1[1] + && Updated1[2] + && Updated2[0] + && Updated2[1] + && Updated2[2] + && Updated3[0] + && Updated3[1] + && Updated3[2]) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + String name[] = {"sample.pdf", "sample.txt", "sample.exe"}; + // for PDF + for (int i = 0; i < facet.length; i++) { + Map FacetMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID[i]); + assertEquals(name[0], FacetMetadata.get("fileName")); + assertNull(FacetMetadata.get("customProperty3")); + assertNull(FacetMetadata.get("customProperty4")); + assertNull(FacetMetadata.get("customProperty1_code")); + assertNull(FacetMetadata.get("customProperty2")); + assertNull(FacetMetadata.get("customProperty6")); + assertNull(FacetMetadata.get("customProperty5")); + } + // for TXT + for (int i = 0; i < facet.length; i++) { + Map FacetMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID2[i]); + assertEquals(name[1], FacetMetadata.get("fileName")); + assertNull(FacetMetadata.get("customProperty3")); + assertNull(FacetMetadata.get("customProperty4")); + assertNull(FacetMetadata.get("customProperty1_code")); + assertNull(FacetMetadata.get("customProperty2")); + assertFalse((Boolean) FacetMetadata.get("customProperty6")); + assertNull(FacetMetadata.get("customProperty5")); + } + // for EXE + for (int i = 0; i < facet.length; i++) { + Map FacetMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], entityID3, ID3[i]); + assertEquals(name[2], FacetMetadata.get("fileName")); + assertNull(FacetMetadata.get("customProperty3")); + assertNull(FacetMetadata.get("customProperty4")); + assertEquals(dropdownValue1, FacetMetadata.get("customProperty1_code")); + assertEquals(12, FacetMetadata.get("customProperty2")); + } - // String sourceAttachmentId = createResponse.get(1); + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3},{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation (only for attachments facet) --- + // PDF: invalid prop was used, so nothing should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals( + "sample.pdf", + cmisName, + "PDF filename should NOT be changed in CMIS for " + facet[i]); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull( + cmisId1, "Invalid property abc:myId1 should not exist for PDF " + facet[i]); + } + } + // TXT: only valid Boolean (false) was set — should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals( + "false", + cmisBoolTxt, + "DocumentInfoRecordBoolean should be false for TXT " + facet[i]); + } + } + // EXE: valid String + Int were set — should persist + for (int i = 0; i < facet.length; i++) { + if (i == 0) { + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull( + cmisStringExe, "DocumentInfoRecordString should be set for EXE " + facet[i]); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + "12", cmisIntExe, "DocumentInfoRecordInt should match for EXE " + facet[i]); + } + } + testStatus = true; + System.out.println( + "Rename & update unsuccessfull for invalid Secondary properties and successfull for valid property attachments"); + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateBody = RequestBody.create(jsonPayload, mediaType); + @Test + @Order(28) + void testNAttachments_NewEntity() throws IOException { + System.out.println( + "Test (28): Creating new entity and checking only max 4 attachments are allowed to be uploaded"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID4 = response; - // String updateResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // copyCustomSourceEntity, - // sourceAttachmentId, - // updateBody); + System.out.println("Entity created"); - // if (!updateResponse.equals("Updated")) { - // fail("Could not update attachment notes field in facet: " + facetName); - // } - // } + System.out.println("Creating attachment PDF"); + ClassLoader classLoader = getClass().getClassLoader(); - // List objectIdsToStore = new ArrayList<>(); - // for (String facetName : facet) { - // List> sourceAttachmentsMetadata = - // api.fetchEntityMetadataDraft(appUrl, entityName, facetName, copyCustomSourceEntity); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID4); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData1, file); + if (createResponse1.get(0).equals("Attachment created")) { + ID[0] = createResponse1.get(1); + System.out.println("Attachment created"); + } - // if (sourceAttachmentsMetadata.isEmpty()) { - // fail("No attachments found in source entity for facet: " + facetName); - // } + System.out.println("Creating attachment TXT"); + file = new File(classLoader.getResource("sample.txt").getFile()); + Map postData2 = new HashMap<>(); + postData2.put("up__ID", entityID4); + postData2.put("mimeType", "application/txt"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + List createResponse2 = + api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData2, file); + if (createResponse2.get(0).equals("Attachment created")) { + ID2[0] = createResponse2.get(1); + System.out.println("Attachment created"); + } - // Map sourceAttachmentMetadata = sourceAttachmentsMetadata.get(0); + System.out.println("Creating attachment EXE"); + file = new File(classLoader.getResource("sample.exe").getFile()); + Map postData3 = new HashMap<>(); + postData3.put("up__ID", entityID4); + postData3.put("mimeType", "application/exe"); + postData3.put("createdAt", new Date().toString()); + postData3.put("createdBy", "test@test.com"); + postData3.put("modifiedBy", "test@test.com"); + + List createResponse3 = + api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, file); + if (createResponse3.get(0).equals("Attachment created")) { + ID[0] = createResponse3.get(1); + System.out.println("Attachment created"); + } - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId for facet: " + facetName); - // } + System.out.println("Creating second attachment pdf"); + file = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postData4 = new HashMap<>(); + postData4.put("up__ID", entityID4); + postData4.put("mimeType", "application/pdf"); + postData4.put("createdAt", new Date().toString()); + postData4.put("createdBy", "test@test.com"); + postData4.put("modifiedBy", "test@test.com"); + + List createResponse4 = + api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, file); + if (createResponse4.get(0).equals("Attachment created")) { + ID4[0] = createResponse4.get(1); + System.out.println("Attachment created"); + } - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // objectIdsToStore.add(sourceObjectId); - - // String sourceNoteValue = - // sourceAttachmentMetadata.get("note") != null - // ? sourceAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(sourceNoteValue)) { - // fail( - // "Notes field was not properly set in source attachment for facet " - // + facetName - // + ". Expected: " - // + notesValue - // + ", Got: " - // + sourceNoteValue); - // } - // } + System.out.println("Creating third attachment pdf"); + file = new File(classLoader.getResource("sample2.pdf").getFile()); + Map postData5 = new HashMap<>(); + postData5.put("up__ID", entityID4); + postData5.put("mimeType", "application/pdf"); + postData5.put("createdAt", new Date().toString()); + postData5.put("createdBy", "test@test.com"); + postData5.put("modifiedBy", "test@test.com"); + + List createResponse5 = + api.createAttachment(appUrl, entityName, facet[0], entityID4, srvpath, postData3, file); + if (createResponse5.get(0).equals("Only 4 attachments allowed.")) { + testStatus = true; + ID5[0] = createResponse5.get(1); + System.out.println("Expected error received: Only 4 attachments allowed."); + } + String check = createResponse5.get(0); + if (check.equals("Attachment created")) { + testStatus = false; + } else { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID4); + if (response.equals("Saved")) { + String expectedJson = + "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 attachments.\"}}"; + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode actualJsonNode = objectMapper.readTree(check); + JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); + if (expectedJsonNode.equals(actualJsonNode)) { + testStatus = true; + } + } + } + } + if (!testStatus) { + fail("Attachment was created"); + } + } - // int startIndex = sourceObjectIds.size(); - // sourceObjectIds.addAll(objectIdsToStore); + @Test + @Order(29) + void testUploadNAttachments() throws IOException { + System.out.println("Test (29): Upload maximum 4 attachments in an exsisting entity"); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File originalFile = new File(classLoader.getResource("sample.exe").getFile()); + + boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID4); + System.out.println("response: " + response); + + if ("Entity in draft mode".equals(response)) { + for (int i = 1; i <= 5; i++) { + // Ensure only one file is uploaded at a time and complete before next + File tempFile = File.createTempFile("sample_" + i + "_", ".exe"); + Files.copy(originalFile.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID4); + postData.put("mimeType", "application/exe"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facet[0], entityID4, srvpath, postData, tempFile); + + String resultMessage = createResponse.get(0); + System.out.println("Result message for attachment " + i + ": " + resultMessage); + + String expectedResponse = + "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 attachments.\"}}"; + if (resultMessage.equals(expectedResponse)) { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode actualJsonNode = objectMapper.readTree(resultMessage); + JsonNode expectedJsonNode = objectMapper.readTree(expectedResponse); + if (expectedJsonNode.equals(actualJsonNode)) { + testStatus = true; + } + } else { + testStatus = false; + } + tempFile.delete(); + } + if (!testStatus) { + fail("5th attachment did not trigger the expected error."); + } + // Delete the newly created entity + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID4); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } else { + System.out.println("Successfully deleted the test entity4"); + } + } + } - // copyCustomTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyCustomTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + @Test + @Order(30) + void testDiscardDraftWithoutAttachments() { + System.out.println("Test (30) : Discard draft without adding attachments"); + Boolean testStatus = false; - // int facetIndex = 0; - // for (String facetName : facet) { - // if (facetIndex > 0) { - // String editResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID6 = response; + response = api.deleteEntityDraft(appUrl, entityName, entityID6); + if (response.equals("Entity Draft Deleted")) { + testStatus = true; + } + } + if (!testStatus) { + fail("Draft was not discarded properly"); + } + } - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); + @Test + @Order(31) + void testDiscardDraftWithAttachments() throws IOException { + System.out.println("Test (31): Discard draft with attachments, references, and footnotes"); + boolean testStatus = false; - // String copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!"Could not create entity".equals(response)) { + entityID6 = response; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity for facet: " + facetName); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID6); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + for (int i = 0; i < facet.length; i++) { + List createResponse = + api.createAttachment(appUrl, entityName, facet[i], entityID6, srvpath, postData, file); + if ("Attachment created".equals(createResponse.get(0))) { + System.out.println("Attachment created in facet: " + facet[i]); + } else { + System.out.println("Attachment creation failed in facet: " + facet[i]); + } + } + response = api.deleteEntityDraft(appUrl, entityName, entityID6); + if ("Entity Draft Deleted".equals(response)) { + testStatus = true; + } + } + if (!testStatus) { + fail("Draft with attachments was not discarded properly"); + } + } - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity for facet: " + facetName); - // } + @Test + @Order(32) + void testDraftUpdateUploadTwoDeleteOneAndCreate() throws IOException { + System.out.println("Test (32): Upload to all facets, delete one, and create entity"); - // facetIndex++; - // } + boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // for (String facetName : facet) { - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + if (!"Could not create entity".equals(response)) { + entityID5 = response; + ClassLoader classLoader = getClass().getClassLoader(); - // if (targetAttachmentsMetadata.isEmpty()) { - // fail("No attachments found in target entity for facet: " + facetName); - // } + File file1 = + new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); + File file2 = + new File(Objects.requireNonNull(classLoader.getResource("sample.txt")).getFile()); + + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID5); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + Map postData2 = new HashMap<>(postData1); + postData2.put("up__ID", entityID5); + postData2.put("mimeType", "text/plain"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + boolean allCreated = true; + for (int i = 0; i < facet.length; i++) { + List response1 = + api.createAttachment( + appUrl, entityName, facet[i], entityID5, srvpath, postData1, file1); + List response2 = + api.createAttachment( + appUrl, entityName, facet[i], entityID5, srvpath, postData2, file2); + + if (response1.get(0).equals("Attachment created") + && response2.get(0).equals("Attachment created")) { + ID4[i] = response1.get(1); // to keep one + ID5[i] = response2.get(1); // will delete this one + } else { + allCreated = false; + break; + } - // Map copiedAttachmentMetadata = targetAttachmentsMetadata.get(0); - // String copiedNoteValue = - // copiedAttachmentMetadata.get("note") != null - // ? copiedAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(copiedNoteValue)) { - // fail( - // "Notes field was not properly copied for facet " - // + facetName - // + ". Expected: " - // + notesValue - // + ", Got: " - // + copiedNoteValue); - // } + String deleteResponse = + api.deleteAttachment(appUrl, entityName, facet[i], entityID5, ID5[i]); + if (!"Deleted".equals(deleteResponse)) { + allCreated = false; + break; + } + } - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + if (allCreated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); + if ("Saved".equals(response)) { + testStatus = true; + } + } + } - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment from target entity for facet: " + facetName); - // } else { - // testStatus = true; - // } - // } + if (!testStatus) { + fail("Failed to upload multiple facet entries, delete one per facet and create entity"); + } + } - // if (!testStatus) { - // fail( - // "Could not verify that notes field was copied from source to target attachment for all - // facets"); - // } - // } + @Test + @Order(33) + void testUpdateEntityDraft() throws IOException { + System.out.println("Test (33): Update entity draft with new facet content"); + boolean testStatus = false; - // @Test - // @Order(38) - // void testCopyAttachmentWithSecondaryPropertiesField() throws IOException { - // System.out.println( - // "Test (38): Verify that secondary properties are preserved when copying attachments - // between entities across multiple facets"); - // Boolean testStatus = false; - - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyCustomSourceEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample1.pdf").getFile()); + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - // List objectIdsToStore = new ArrayList<>(); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID5); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // for (String facetName : facet) { - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID5); + if ("Entity in draft mode".equals(response)) { + boolean allCreated = true; - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + for (int i = 0; i < facet.length; i++) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], entityID5, srvpath, postData, tempFile); + if (!"Attachment created".equals(createResponse.get(0))) { + allCreated = false; + } + } - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment in facet: " + facetName); - // } + if (allCreated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); + if ("Saved".equals(response)) { + testStatus = true; + } + } + } + api.deleteEntity(appUrl, entityName, entityID5); + if (!testStatus) { + fail("Failed to update draft with new attachments for all facets"); + } + } - // String sourceAttachmentId = createResponse.get(1); - - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // copyCustomSourceEntity, - // sourceAttachmentId, - // bodyBoolean); - - // if (!updateSecondaryPropertyResponse1.equals("Updated")) { - // fail("Could not update attachment DocumentInfoRecordBoolean field for facet: " + - // facetName); - // } + @Test + @Order(34) + void testUploadAttachmentWithoutSDMRole() throws IOException { + System.out.println("Test (34): Upload attachment across facets without SDM role"); + boolean testStatus = true; + + String response = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID7 = response; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // Integer customProperty2Value = 12345; - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + - // "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, - // bodyInt); - - // if (!updateSecondaryPropertyResponse2.equals("Updated")) { - // fail("Could not update attachment customProperty2 field for facet: " + facetName); - // } - // } + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - // // Save source entity to persist attachments before fetching metadata - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity after creating attachments"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // Integer customProperty2Value = 12345; - // for (String facetName : facet) { - // List> sourceAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomSourceEntity); + for (int i = 0; i < facet.length; i++) { + List createResponse = + apiNoRoles.createAttachment( + appUrl, entityName, facet[i], entityID7, srvpath, postData, tempFile); + String check = createResponse.get(0); + String expectedError = + "{\"error\":{\"code\":\"500\",\"message\":\"You do not have the required permissions to upload attachments. Please contact your administrator for access.\"}}"; + if (!expectedError.equals(check)) { + testStatus = false; + } + } + } + api.deleteEntityDraft(appUrl, entityName, entityID7); + if (!testStatus) { + fail("Attachment uploaded without SDM role for one or more facets"); + } + } - // Map sourceAttachmentMetadata = - // sourceAttachmentsMetadata.stream() - // .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + @Test + @Order(35) + void testCopyAttachmentsSuccessNewEntity() throws IOException { + System.out.println("Test (35): Copy attachments from one entity to another new entity"); + List> attachments = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + attachments.add(new ArrayList<>()); + } + copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!copyAttachmentSourceEntity.equals("Could not create entity") + && !copyAttachmentTargetEntity.equals("Could not create entity")) { + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample1.pdf").getFile())); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (sourceAttachmentMetadata == null) { - // fail("Could not find attachment with filename 'sample1.pdf' in facet: " + facetName); - // } - - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId for facet: " + facetName); - // } + for (int i = 0; i < facet.length; i++) { + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, + entityName, + facet[i], + copyAttachmentSourceEntity, + srvpath, + postData, + file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.get(i).add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } + } + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (int i = 0; i < attachments.size(); i++) { + for (String attachment : attachments.get(i)) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadataDraft( + appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + } + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + + if (sourceObjectIds.size() == 6) { + String copyResponse; + int i = 0; + for (String facetName : facet) { + if (i != 0) { + String editResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + } + copyResponse = + api.copyAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size()))); + i += 2; + if (copyResponse.equals("Attachments copied successfully")) { + // Fetch copied attachment IDs from target draft + List> copiedMetadataResponse = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyAttachmentTargetEntity); + List copiedAttachmentIds = + copiedMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadata( + appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not create entities"); + } + } - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // objectIdsToStore.add(sourceObjectId); - - // Boolean sourceCustomProperty6 = - // sourceAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) sourceAttachmentMetadata.get("customProperty6") - // : null; - // Integer sourceCustomProperty2 = - // sourceAttachmentMetadata.get("customProperty2") != null - // ? (Integer) sourceAttachmentMetadata.get("customProperty2") - // : null; - - // if (sourceCustomProperty6 == null || !sourceCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly set in source attachment for facet " - // + facetName - // + ". Expected: true, Got: " - // + sourceCustomProperty6); - // } + @Test + @Order(36) + void testCopyAttachmentsUnsuccessfulNewEntity() throws IOException { + System.out.println( + "Test (36): Copy incorrect attachments from one entity to another new entity"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + copyAttachmentTargetEntityEmpty = + api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (editResponse1.equals("Entity in draft mode") + && !copyAttachmentTargetEntityEmpty.equals("Could not create entity")) { + if (sourceObjectIds.size() == 6) { + int i = 0; + for (String facet : facet) { + try { + List currentFacetObjectIds = + sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); + currentFacetObjectIds.add("incorrectObjectId"); + if (currentFacetObjectIds.size() != 3) { + fail("Not enough object IDs to copy attachments for facet: " + facet); + } + api.copyAttachment( + appUrl, entityName, facet, copyAttachmentTargetEntityEmpty, currentFacetObjectIds); + fail("Copy attachments did not throw an error"); + } catch (IOException e) { + i += 2; + } + } + String saveEntityResponse1 = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String saveEntityResponse2 = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntityEmpty); + String deleteResponse = + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntityEmpty); + if (!saveEntityResponse1.equals("Saved") + || !saveEntityResponse2.equals("Saved") + || !deleteResponse.equals("Entity Deleted")) { + fail("Could not save entities"); + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } - // if (!customProperty2Value.equals(sourceCustomProperty2)) { - // fail( - // "customProperty2 was not properly set in source attachment for facet " - // + facetName - // + ". Expected: " - // + customProperty2Value - // + ", Got: " - // + sourceCustomProperty2); - // } - // } + @Test + @Order(37) + void testCopyAttachmentWithNotesField() throws IOException { + System.out.println( + "Test (37): Create entity with attachments containing notes in multiple facets, copy to new entity and verify notes field"); + Boolean testStatus = false; - // int startIndex = sourceObjectIds.size(); - // sourceObjectIds.addAll(objectIdsToStore); + copyCustomSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyCustomSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // int facetIndex = 0; - // for (String facetName : facet) { - // String editTargetResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!editTargetResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + String notesValue = "This is a test note for copy attachment verification"; + MediaType mediaType = MediaType.parse("application/json"); - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); + for (String facetName : facet) { + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity for facet: " + facetName); - // } + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment in facet: " + facetName); + } - // // Fetch copied attachment IDs from target draft - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity for facet: " + facetName); - // } + String sourceAttachmentId = createResponse.get(1); - // facetIndex++; - // } + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateBody = RequestBody.create(jsonPayload, mediaType); - // for (String facetName : facet) { - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + String updateResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + copyCustomSourceEntity, + sourceAttachmentId, + updateBody); - // Map copiedAttachmentMetadata = - // targetAttachmentsMetadata.stream() - // .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + if (!updateResponse.equals("Updated")) { + fail("Could not update attachment notes field in facet: " + facetName); + } + } - // if (copiedAttachmentMetadata == null) { - // fail( - // "Could not find the copied attachment with file in target entity for facet: " - // + facetName); - // } + List objectIdsToStore = new ArrayList<>(); + for (String facetName : facet) { + List> sourceAttachmentsMetadata = + api.fetchEntityMetadataDraft(appUrl, entityName, facetName, copyCustomSourceEntity); - // Boolean copiedCustomProperty6 = - // copiedAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) copiedAttachmentMetadata.get("customProperty6") - // : null; - // Integer copiedCustomProperty2 = - // copiedAttachmentMetadata.get("customProperty2") != null - // ? (Integer) copiedAttachmentMetadata.get("customProperty2") - // : null; - - // if (copiedCustomProperty6 == null || !copiedCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly copied for facet " - // + facetName - // + ". Expected: true, Got: " - // + copiedCustomProperty6); - // } + if (sourceAttachmentsMetadata.isEmpty()) { + fail("No attachments found in source entity for facet: " + facetName); + } - // if (!customProperty2Value.equals(copiedCustomProperty2)) { - // fail( - // "customProperty2 was not properly copied for facet " - // + facetName - // + ". Expected: " - // + customProperty2Value - // + ", Got: " - // + copiedCustomProperty2); - // } + Map sourceAttachmentMetadata = sourceAttachmentsMetadata.get(0); - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId for facet: " + facetName); + } - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment from target entity for facet: " + facetName); - // } else { - // testStatus = true; - // } - // } + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + objectIdsToStore.add(sourceObjectId); - // if (!testStatus) { - // fail( - // "Could not verify that all secondary properties were copied from source to target - // attachment for all facets"); - // } - // } + String sourceNoteValue = + sourceAttachmentMetadata.get("note") != null + ? sourceAttachmentMetadata.get("note").toString() + : null; - // @Test - // @Order(39) - // void testCopyAttachmentWithNotesAndSecondaryPropertiesField() throws IOException { - // System.out.println( - // "Test (39): Verify that both notes field and secondary properties are preserved during - // attachment copy across multiple facets"); - // Boolean testStatus = false; - - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyCustomSourceEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity"); - // } + if (!notesValue.equals(sourceNoteValue)) { + fail( + "Notes field was not properly set in source attachment for facet " + + facetName + + ". Expected: " + + notesValue + + ", Got: " + + sourceNoteValue); + } + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample2.pdf").getFile()); + int startIndex = sourceObjectIds.size(); + sourceObjectIds.addAll(objectIdsToStore); - // String notesValue = "This attachment has both notes and secondary properties for testing"; - // MediaType mediaType = MediaType.parse("application/json"); - // Integer customProperty2Value = 99999; - // List objectIdsToStore = new ArrayList<>(); + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity"); + } - // for (String facetName : facet) { - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + copyCustomTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyCustomTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + int facetIndex = 0; + for (String facetName : facet) { + if (facetIndex > 0) { + String editResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + } - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment in facet: " + facetName); - // } + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); - // String sourceAttachmentId = createResponse.get(1); + String copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity for facet: " + facetName); + } - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // copyCustomSourceEntity, - // sourceAttachmentId, - // updateNotesBody); + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity for facet: " + facetName); + } - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update attachment notes field for facet: " + facetName); - // } + facetIndex++; + } - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // copyCustomSourceEntity, - // sourceAttachmentId, - // bodyBoolean); - - // if (!updateSecondaryPropertyResponse1.equals("Updated")) { - // fail("Could not update attachment DocumentInfoRecordBoolean field for facet: " + - // facetName); - // } + for (String facetName : facet) { + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + - // "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, - // bodyInt); - - // if (!updateSecondaryPropertyResponse2.equals("Updated")) { - // fail("Could not update attachment customProperty2 field for facet: " + facetName); - // } - // } + if (targetAttachmentsMetadata.isEmpty()) { + fail("No attachments found in target entity for facet: " + facetName); + } - // // Save source entity to persist attachments before fetching metadata and copying - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity after creating attachments"); - // } + Map copiedAttachmentMetadata = targetAttachmentsMetadata.get(0); + String copiedNoteValue = + copiedAttachmentMetadata.get("note") != null + ? copiedAttachmentMetadata.get("note").toString() + : null; - // for (String facetName : facet) { - // List> sourceAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomSourceEntity); + if (!notesValue.equals(copiedNoteValue)) { + fail( + "Notes field was not properly copied for facet " + + facetName + + ". Expected: " + + notesValue + + ", Got: " + + copiedNoteValue); + } - // Map sourceAttachmentMetadata = - // sourceAttachmentsMetadata.stream() - // .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); - // if (sourceAttachmentMetadata == null) { - // fail("Could not find attachment with file in facet: " + facetName); - // } + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment from target entity for facet: " + facetName); + } else { + testStatus = true; + } + } - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId for facet: " + facetName); - // } + if (!testStatus) { + fail( + "Could not verify that notes field was copied from source to target attachment for all facets"); + } + } - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // objectIdsToStore.add(sourceObjectId); - - // String sourceNoteValue = - // sourceAttachmentMetadata.get("note") != null - // ? sourceAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(sourceNoteValue)) { - // fail( - // "Notes field was not properly set in source attachment for facet " - // + facetName - // + ". Expected: " - // + notesValue - // + ", Got: " - // + sourceNoteValue); - // } + @Test + @Order(38) + void testCopyAttachmentWithSecondaryPropertiesField() throws IOException { + System.out.println( + "Test (38): Verify that secondary properties are preserved when copying attachments between entities across multiple facets"); + Boolean testStatus = false; - // Boolean sourceCustomProperty6 = - // sourceAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) sourceAttachmentMetadata.get("customProperty6") - // : null; - // Integer sourceCustomProperty2 = - // sourceAttachmentMetadata.get("customProperty2") != null - // ? (Integer) sourceAttachmentMetadata.get("customProperty2") - // : null; - - // if (sourceCustomProperty6 == null || !sourceCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly set in source attachment for facet " - // + facetName - // + ". Expected: true, Got: " - // + sourceCustomProperty6); - // } + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity"); + } - // if (!customProperty2Value.equals(sourceCustomProperty2)) { - // fail( - // "customProperty2 was not properly set in source attachment for facet " - // + facetName - // + ". Expected: " - // + customProperty2Value - // + ", Got: " - // + sourceCustomProperty2); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample1.pdf").getFile()); - // int startIndex = sourceObjectIds.size(); - // sourceObjectIds.addAll(objectIdsToStore); + List objectIdsToStore = new ArrayList<>(); - // int facetIndex = 0; - // for (String facetName : facet) { - // String editTargetResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!editTargetResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity"); - // } + for (String facetName : facet) { + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); - // String copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment in facet: " + facetName); + } - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity for facet: " + facetName); - // } + String sourceAttachmentId = createResponse.get(1); - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity for facet: " + facetName); - // } + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + copyCustomSourceEntity, + sourceAttachmentId, + bodyBoolean); - // facetIndex++; - // } + if (!updateSecondaryPropertyResponse1.equals("Updated")) { + fail("Could not update attachment DocumentInfoRecordBoolean field for facet: " + facetName); + } - // for (String facetName : facet) { - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + Integer customProperty2Value = 12345; + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); + + if (!updateSecondaryPropertyResponse2.equals("Updated")) { + fail("Could not update attachment customProperty2 field for facet: " + facetName); + } + } - // Map copiedAttachmentMetadata = - // targetAttachmentsMetadata.stream() - // .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + // Save source entity to persist attachments before fetching metadata + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity after creating attachments"); + } - // if (copiedAttachmentMetadata == null) { - // fail( - // "Could not find the copied attachment with file in target entity for facet: " - // + facetName); - // } + Integer customProperty2Value = 12345; + for (String facetName : facet) { + List> sourceAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomSourceEntity); - // String copiedNoteValue = - // copiedAttachmentMetadata.get("note") != null - // ? copiedAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(copiedNoteValue)) { - // fail( - // "Notes field was not properly copied for facet " - // + facetName - // + ". Expected: " - // + notesValue - // + ", Got: " - // + copiedNoteValue); - // } + Map sourceAttachmentMetadata = + sourceAttachmentsMetadata.stream() + .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); - // Boolean copiedCustomProperty6 = - // copiedAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) copiedAttachmentMetadata.get("customProperty6") - // : null; - // Integer copiedCustomProperty2 = - // copiedAttachmentMetadata.get("customProperty2") != null - // ? (Integer) copiedAttachmentMetadata.get("customProperty2") - // : null; - - // if (copiedCustomProperty6 == null || !copiedCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean (customProperty6) was not properly copied for facet " - // + facetName - // + ". Expected: true, Got: " - // + copiedCustomProperty6); - // } - // if (!customProperty2Value.equals(copiedCustomProperty2)) { - // fail( - // "customProperty2 was not properly copied for facet " - // + facetName - // + ". Expected: " - // + customProperty2Value - // + ", Got: " - // + copiedCustomProperty2); - // } - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + if (sourceAttachmentMetadata == null) { + fail("Could not find attachment with filename 'sample1.pdf' in facet: " + facetName); + } - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment from target entity for facet: " + facetName); - // } else { - // testStatus = true; - // } - // } - // api.deleteEntity(appUrl, entityName, copyCustomSourceEntity); - // api.deleteEntity(appUrl, entityName, copyCustomTargetEntity); - // if (!testStatus) { - // fail( - // "Could not verify that notes field and all secondary properties were copied from source - // to target attachment for all facets"); - // } - // } + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId for facet: " + facetName); + } - // @Test - // @Order(40) - // void testCopyAttachmentsSuccessExistingEntity() throws IOException { - // System.out.println("Test (40): Copy attachments from one entity to another existing entity"); - // List> attachments = new ArrayList<>(); - // for (int i = 0; i < 3; i++) { - // attachments.add(new ArrayList<>()); - // } - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // File file1 = new File(classLoader.getResource("sample.pdf").getFile()); - // File file2 = new File(classLoader.getResource("sample1.pdf").getFile()); - // File tempFile1 = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file1.toPath(), tempFile1.toPath(), StandardCopyOption.REPLACE_EXISTING); - // File tempFile2 = new File(System.getProperty("java.io.tmpdir"), "sample4.pdf"); - // Files.copy(file2.toPath(), tempFile2.toPath(), StandardCopyOption.REPLACE_EXISTING); - // files.add(tempFile1); - // files.add(tempFile2); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String editResponse2 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (editResponse1.equals("Entity in draft mode") - // && editResponse2.equals("Entity in draft mode")) { - // for (int i = 0; i < facet.length; i++) { - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facet[i], - // copyAttachmentSourceEntity, - // srvpath, - // postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.get(i).add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } - // } - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (int i = 0; i < attachments.size(); i++) { - // for (String attachment : attachments.get(i)) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadataDraft( - // appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } - // } + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + objectIdsToStore.add(sourceObjectId); - // sourceObjectIds.clear(); - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - - // if (sourceObjectIds.size() == 6) { - // String copyResponse; - // int i = 0; - // for (String facetName : facet) { - // if (i != 0) { - // String editResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } - // } - // List currentFacetObjectIds = - // sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); - // if (currentFacetObjectIds.size() != 2) { - // fail("Not enough object IDs to copy attachments for facet: " + facet); - // } - // copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, - // currentFacetObjectIds); - // i += 2; - // if (copyResponse.equals("Attachments copied successfully")) { - // // Fetch copied attachment IDs from target draft - // List> copiedMetadataResponse = - // api.fetchEntityMetadata(appUrl, entityName, facetName, - // copyAttachmentTargetEntity); - // List copiedAttachmentIds = - // copiedMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadata( - // appUrl, entityName, facetName, copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // if (targetAttachmentIds.size() == 4) { - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + Boolean sourceCustomProperty6 = + sourceAttachmentMetadata.get("customProperty6") != null + ? (Boolean) sourceAttachmentMetadata.get("customProperty6") + : null; + Integer sourceCustomProperty2 = + sourceAttachmentMetadata.get("customProperty2") != null + ? (Integer) sourceAttachmentMetadata.get("customProperty2") + : null; - // @Test - // @Order(41) - // void testCopyAttachmentsUnsuccessfulExistingEntity() throws IOException { - // System.out.println("Test (41): Copy attachments from one entity to another new entity"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String editResponse2 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (editResponse1.equals("Entity in draft mode") - // && editResponse2.equals("Entity in draft mode")) { - // if (sourceObjectIds.size() == 6) { - // int i = 0; - // for (String facetName : facet) { - // List currentFacetObjectIds = - // sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); - // currentFacetObjectIds.add("incorrectObjectId"); - // if (currentFacetObjectIds.size() != 3) { - // fail("Not enough object IDs to copy attachments for facet: " + facet); - // } - // try { - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); - // fail("Copy attachments did not throw an error"); - // } catch (IOException e) { - // i += 2; - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + if (sourceCustomProperty6 == null || !sourceCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly set in source attachment for facet " + + facetName + + ". Expected: true, Got: " + + sourceCustomProperty6); + } - // @Test - // @Order(42) - // void testCreateLinkSuccess() throws IOException { - // System.out.println("Test (42): Create link in entity"); - // List attachments = new ArrayList<>(); + if (!customProperty2Value.equals(sourceCustomProperty2)) { + fail( + "customProperty2 was not properly set in source attachment for facet " + + facetName + + ". Expected: " + + customProperty2Value + + ", Got: " + + sourceCustomProperty2); + } + } - // createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + int startIndex = sourceObjectIds.size(); + sourceObjectIds.addAll(objectIdsToStore); - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // for (String facetName : facet) { - // String createLinkResponse1 = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // String createLinkResponse2 = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName + "1", - // linkUrl); - // if (!createLinkResponse1.equals("Link created successfully") - // || !createLinkResponse2.equals("Link created successfully")) { - // fail("Could not create links for facet : " + facetName + createLinkResponse1); - // } - // } + int facetIndex = 0; + for (String facetName : facet) { + String editTargetResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!editTargetResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity"); + } - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); - // for (String facetName : facet) { - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String openAttachmentResponse; - // for (String attachment : attachments) { - // openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, createLinkEntity, attachment); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open created link in facet : " + facetName); - // } - // } - // } - // } + String copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); - // @Test - // @Order(43) - // void testCreateLinkDifferentEntity() throws IOException { - // System.out.println("Test (43): Create link with same name in different entity"); + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity for facet: " + facetName); + } - // String createLinkDifferentEntity = - // api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkDifferentEntity.equals("Could not edit entity")) { - // fail("Could not create entity"); - // } + // Fetch copied attachment IDs from target draft + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity for facet: " + facetName); + } - // String linkName = "sample"; - // String linkUrl = "https://example.com"; - // for (String facetName : facet) { - // String createResponse = - // api.createLink( - // appUrl, entityName, facetName, createLinkDifferentEntity, linkName, linkUrl); - // if (!createResponse.equals("Link created successfully")) { - // fail("Could not create link in different entity with same name"); - // } - // } + facetIndex++; + } - // String response = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkDifferentEntity); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } + for (String facetName : facet) { + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); - // response = api.deleteEntity(appUrl, entityName, createLinkDifferentEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } + Map copiedAttachmentMetadata = + targetAttachmentsMetadata.stream() + .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); - // @Test - // @Order(44) - // void testCreateLinkFailure() throws IOException { - // System.out.println("Test (41): Create link fails due to invalid URL and name"); - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (editEntityResponse.equals("Could not edit entity")) { - // fail("Could not edit entity"); - // } - // for (String facetName : facet) { - // String linkName = "sample"; - // String linkUrl = "example.com"; - // try { - // String response = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // fail("Create link did not throw an error for invalid url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("400018", errorCode); - // assertTrue( - // errorMessage.equals("Enter a value that is within the expected pattern.") - // || errorMessage.equals("Enter a value that matches the expected pattern."), - // "Unexpected error message: " + errorMessage); - // } - // try { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName + "//", "https://" + - // linkUrl); - // fail("Create link did not throw an error for invalid name"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = - // "\"sample//\" contains unsupported characters (‘/’ or ‘\\’). Rename and try again."; - // assertEquals("500", errorCode); - // assertEquals( - // expected.replaceAll("\\s+", " ").trim(), errorMessage.replaceAll("\\s+", " - // ").trim()); - // } - // try { - // api.createLink(appUrl, entityName, facetName, createLinkEntity, "", ""); - // fail("Create link did not throw an error for empty name and url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = "Provide the missing value."; - // assertEquals("409008", errorCode); - // assertEquals(expected, errorMessage); - // } - // try { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName, "https://" + linkUrl); - // fail("Create link did not throw an error for duplicate name"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals( - // "An object named \"sample\" already exists. Rename the object and try again.", - // errorMessage); - // } - // try { - // for (int i = 2; i < 6; i++) { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName + i, "https://" + - // linkUrl); - // } - // System.out.println("Created 5 links in facet: " + facetName); - // if (!facetName.equals("footnotes")) { - // fail("More than 5 links were created in the same entity"); - // } - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // if (facetName.equals("references")) { - // assertEquals("Cannot upload more than 5 attachments.", errorMessage); - // } else if (facetName.equals("attachments")) { - // assertEquals("Cannot upload more than 4 attachments.", errorMessage); - // } - // } - // } + if (copiedAttachmentMetadata == null) { + fail( + "Could not find the copied attachment with file in target entity for facet: " + + facetName); + } - // String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } + Boolean copiedCustomProperty6 = + copiedAttachmentMetadata.get("customProperty6") != null + ? (Boolean) copiedAttachmentMetadata.get("customProperty6") + : null; + Integer copiedCustomProperty2 = + copiedAttachmentMetadata.get("customProperty2") != null + ? (Integer) copiedAttachmentMetadata.get("customProperty2") + : null; - // response = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } + if (copiedCustomProperty6 == null || !copiedCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly copied for facet " + + facetName + + ". Expected: true, Got: " + + copiedCustomProperty6); + } - // @Test - // @Order(45) - // void testCreateLinkNoSDMRoles() throws IOException { - // System.out.println("Test (42): Create link fails due to no SDM roles assigned"); + if (!customProperty2Value.equals(copiedCustomProperty2)) { + fail( + "customProperty2 was not properly copied for facet " + + facetName + + ". Expected: " + + customProperty2Value + + ", Got: " + + copiedCustomProperty2); + } - // String createLinkEntityNoSDMRoles = - // apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntityNoSDMRoles.equals("Could not edit entity")) { - // fail("Could not create entity"); - // } + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); - // for (String facetName : facet) { - // String linkName = "sample27"; - // String linkUrl = "https://example.com"; - // try { - // apiNoRoles.createLink( - // appUrl, entityName, facetName, createLinkEntityNoSDMRoles, linkName, linkUrl); - // fail("Link got created without SDM roles"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals( - // "You do not have the required permissions to upload attachments. Please contact your - // administrator for access.", - // errorMessage); - // } - // } + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment from target entity for facet: " + facetName); + } else { + testStatus = true; + } + } - // String response = - // apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntityNoSDMRoles); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } + if (!testStatus) { + fail( + "Could not verify that all secondary properties were copied from source to target attachment for all facets"); + } + } - // response = api.deleteEntity(appUrl, entityName, createLinkEntityNoSDMRoles); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } + @Test + @Order(39) + void testCopyAttachmentWithNotesAndSecondaryPropertiesField() throws IOException { + System.out.println( + "Test (39): Verify that both notes field and secondary properties are preserved during attachment copy across multiple facets"); + Boolean testStatus = false; - // @Test - // @Order(46) - // void testDeleteLink() throws IOException { - // System.out.println("Test (43): Delete link in entity"); - // List> attachments = new ArrayList<>(); + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity"); + } - // String createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample2.pdf").getFile()); - // for (String facetName : facet) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet : " + facetName); - // } - // } + String notesValue = "This attachment has both notes and secondary properties for testing"; + MediaType mediaType = MediaType.parse("application/json"); + Integer customProperty2Value = 99999; + List objectIdsToStore = new ArrayList<>(); - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + for (String facetName : facet) { + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // for (String facetName : facet) { - // attachments.add( - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList())); - // } + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment in facet: " + facetName); + } - // int index = 0; - // for (String facetName : facet) { - // String deleteLinkResponse = - // api.deleteAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(index).get(0)); - // System.out.println("Delete response for facet " + facetName + ": " + deleteLinkResponse); - // if (!deleteLinkResponse.equals("Deleted")) { - // fail("Could not delete created link"); - // } - // index += 1; - // } + String sourceAttachmentId = createResponse.get(1); - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - // index = 0; - // attachments.clear(); - // for (String facetName : facet) { - // attachments.add( - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList())); - // System.out.println( - // "Attachments after deletion in facet " + facetName + ": " + attachments.get(index)); - // if (attachments.get(index).size() != 0) { - // fail("Link wasn't deleted"); - // } - // index += 1; - // } + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + copyCustomSourceEntity, + sourceAttachmentId, + updateNotesBody); - // String response = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update attachment notes field for facet: " + facetName); + } - // @Test - // @Order(47) - // void testRenameLinkSuccess() throws IOException { - // System.out.println("Test (44): Rename link in entity"); - // List> attachments = new ArrayList<>(); + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + copyCustomSourceEntity, + sourceAttachmentId, + bodyBoolean); - // createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + if (!updateSecondaryPropertyResponse1.equals("Updated")) { + fail("Could not update attachment DocumentInfoRecordBoolean field for facet: " + facetName); + } - // for (String facetName : facet) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } - // } + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + if (!updateSecondaryPropertyResponse2.equals("Updated")) { + fail("Could not update attachment customProperty2 field for facet: " + facetName); + } + } - // for (String facetName : facet) { - // attachments.add( - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList())); - // } + // Save source entity to persist attachments before fetching metadata and copying + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity after creating attachments"); + } - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + for (String facetName : facet) { + List> sourceAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomSourceEntity); - // int index = 0; - // for (String facetName : facet) { - // successfullyRenamedAttachments.add(attachments.get(index).get(0)); - // String renameLinkResponse = - // api.renameAttachment( - // appUrl, - // entityName, - // facetName, - // createLinkEntity, - // attachments.get(index).get(0), - // "sampleRenamed"); - // if (!renameLinkResponse.equals("Renamed")) { - // fail("Could not Renamed created link"); - // } - // index += 1; - // } + Map sourceAttachmentMetadata = + sourceAttachmentsMetadata.stream() + .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } - // } + if (sourceAttachmentMetadata == null) { + fail("Could not find attachment with file in facet: " + facetName); + } - // @Test - // @Order(48) - // void testRenameLinkDuplicate() throws IOException { - // System.out.println("Test (45): Rename link in entity fails due to duplicate error"); - // List attachments = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId for facet: " + facetName); + } - // int index = 0; - // for (String facetName : facet) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } - // } + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + objectIdsToStore.add(sourceObjectId); - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (saveResponse.equals("Could not save entity")) { - // fail("Could not save entity"); - // } + String sourceNoteValue = + sourceAttachmentMetadata.get("note") != null + ? sourceAttachmentMetadata.get("note").toString() + : null; - // index = 0; - // List facetAttachments; - // for (String facetName : facet) { - // int lambdaIndex = index; - // facetAttachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .filter( - // item -> - // !successfullyRenamedAttachments - // .get(lambdaIndex) - // .equals(item.get("ID"))) // skip unwanted filename - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // index += 1; - // attachments.add(facetAttachments.get(0)); - // } + if (!notesValue.equals(sourceNoteValue)) { + fail( + "Notes field was not properly set in source attachment for facet " + + facetName + + ". Expected: " + + notesValue + + ", Got: " + + sourceNoteValue); + } - // System.out.println("Attachments to be renamed: " + attachments); - // String response = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!response.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + Boolean sourceCustomProperty6 = + sourceAttachmentMetadata.get("customProperty6") != null + ? (Boolean) sourceAttachmentMetadata.get("customProperty6") + : null; + Integer sourceCustomProperty2 = + sourceAttachmentMetadata.get("customProperty2") != null + ? (Integer) sourceAttachmentMetadata.get("customProperty2") + : null; - // index = 0; - // for (String facetName : facet) { - // api.renameAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(index), - // "sampleRenamed"); - // index += 1; - // } + if (sourceCustomProperty6 == null || !sourceCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly set in source attachment for facet " + + facetName + + ". Expected: true, Got: " + + sourceCustomProperty6); + } - // String saveError = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // String expectedWarning = - // "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sampleRenamed\\\" already - // exists. Rename the object and try again.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"An object named - // \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: - // attachments\\nPage: - // IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An - // object named \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: - // footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // ObjectMapper mapper = new ObjectMapper(); - // assertEquals(mapper.readTree(expectedWarning), mapper.readTree(saveError)); - - // String deleteEntityResponse = api.deleteEntityDraft(appUrl, entityName, createLinkEntity); - // if (!deleteEntityResponse.equals("Entity Draft Deleted")) { - // fail("Entity draft not deleted"); - // } - // } + if (!customProperty2Value.equals(sourceCustomProperty2)) { + fail( + "customProperty2 was not properly set in source attachment for facet " + + facetName + + ". Expected: " + + customProperty2Value + + ", Got: " + + sourceCustomProperty2); + } + } - // @Test - // @Order(49) - // void testRenameLinkUnsupportedCharacters() throws IOException { - // System.out.println( - // "Test (46): Rename link in entity fails due to unsupported characters in name"); - // List> attachments = new ArrayList<>(); + int startIndex = sourceObjectIds.size(); + sourceObjectIds.addAll(objectIdsToStore); - // createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + int facetIndex = 0; + for (String facetName : facet) { + String editTargetResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!editTargetResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity"); + } - // String linkName = "sample2"; - // String linkUrl = "https://www.example.com"; + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(startIndex + facetIndex)); - // for (String facetName : facet) { - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } - // } + String copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity for facet: " + facetName); + } - // for (String facetName : facet) { - // attachments.add( - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList())); - // } + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity for facet: " + facetName); + } - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + facetIndex++; + } - // int index = 0; - // for (String facetName : facet) { - // api.renameAttachment( - // appUrl, - // entityName, - // facetName, - // createLinkEntity, - // attachments.get(index).get(0), - // "sampleRenamed//"); - // index += 1; - // } + for (String facetName : facet) { + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); - // String error = - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // String expectedError = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"sampleRenamed//\\\" - // contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: - // attachments\\nPage: - // IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // ObjectMapper mapper = new ObjectMapper(); - // assertEquals(mapper.readTree(expectedError), mapper.readTree(error)); - - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!deleteEntityResponse.equals("Entity Deleted")) { - // fail("Entity draft not deleted"); - // } - // } + Map copiedAttachmentMetadata = + targetAttachmentsMetadata.stream() + .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); - // @Test - // @Order(50) - // void testEditLinkSuccess() throws IOException { - // System.out.println("Test (47): Edit existing link in entity"); - // List> attachmentsPerFacet = new ArrayList<>(); + if (copiedAttachmentMetadata == null) { + fail( + "Could not find the copied attachment with file in target entity for facet: " + + facetName); + } - // editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (editLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + String copiedNoteValue = + copiedAttachmentMetadata.get("note") != null + ? copiedAttachmentMetadata.get("note").toString() + : null; - // for (String facetName : facet) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet: " + facetName); - // } - // } + if (!notesValue.equals(copiedNoteValue)) { + fail( + "Notes field was not properly copied for facet " + + facetName + + ". Expected: " + + notesValue + + ", Got: " + + copiedNoteValue); + } - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + Boolean copiedCustomProperty6 = + copiedAttachmentMetadata.get("customProperty6") != null + ? (Boolean) copiedAttachmentMetadata.get("customProperty6") + : null; + Integer copiedCustomProperty2 = + copiedAttachmentMetadata.get("customProperty2") != null + ? (Integer) copiedAttachmentMetadata.get("customProperty2") + : null; - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + if (copiedCustomProperty6 == null || !copiedCustomProperty6) { + fail( + "DocumentInfoRecordBoolean (customProperty6) was not properly copied for facet " + + facetName + + ". Expected: true, Got: " + + copiedCustomProperty6); + } + if (!customProperty2Value.equals(copiedCustomProperty2)) { + fail( + "customProperty2 was not properly copied for facet " + + facetName + + ". Expected: " + + customProperty2Value + + ", Got: " + + copiedCustomProperty2); + } + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); - // for (String facetName : facet) { - // List attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment from target entity for facet: " + facetName); + } else { + testStatus = true; + } + } + api.deleteEntity(appUrl, entityName, copyCustomSourceEntity); + api.deleteEntity(appUrl, entityName, copyCustomTargetEntity); + if (!testStatus) { + fail( + "Could not verify that notes field and all secondary properties were copied from source to target attachment for all facets"); + } + } - // if (attachments.isEmpty()) { - // fail("Could not find link in facet: " + facetName); - // } - // attachmentsPerFacet.add(attachments); - // } + @Test + @Order(40) + void testCopyAttachmentsSuccessExistingEntity() throws IOException { + System.out.println("Test (40): Copy attachments from one entity to another existing entity"); + List> attachments = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + attachments.add(new ArrayList<>()); + } + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + File file1 = new File(classLoader.getResource("sample.pdf").getFile()); + File file2 = new File(classLoader.getResource("sample1.pdf").getFile()); + File tempFile1 = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file1.toPath(), tempFile1.toPath(), StandardCopyOption.REPLACE_EXISTING); + File tempFile2 = new File(System.getProperty("java.io.tmpdir"), "sample4.pdf"); + Files.copy(file2.toPath(), tempFile2.toPath(), StandardCopyOption.REPLACE_EXISTING); + files.add(tempFile1); + files.add(tempFile2); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String editResponse2 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (editResponse1.equals("Entity in draft mode") + && editResponse2.equals("Entity in draft mode")) { + for (int i = 0; i < facet.length; i++) { + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, + entityName, + facet[i], + copyAttachmentSourceEntity, + srvpath, + postData, + file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.get(i).add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } + } + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (int i = 0; i < attachments.size(); i++) { + for (String attachment : attachments.get(i)) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadataDraft( + appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + } - // int index = 0; - // for (String facetName : facet) { - // String linkId = attachmentsPerFacet.get(index).get(0); - // String updatedUrl = "https://editedexample.com"; - // String editLinkResponse = - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // if (!editLinkResponse.equals("Link edited successfully")) { - // fail("Could not edit link in facet: " + facetName); - // } - // index++; - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - - // int verificationIndex = 0; - // for (String facetName : facet) { - // List attachmentsInFacet = attachmentsPerFacet.get(verificationIndex); - // for (String attachmentId : attachmentsInFacet) { - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, editLinkEntity, attachmentId); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open edited link " + attachmentId + " in facet: " + facetName); - // } - // } - // verificationIndex++; - // } - // } + sourceObjectIds.clear(); + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + + if (sourceObjectIds.size() == 6) { + String copyResponse; + int i = 0; + for (String facetName : facet) { + if (i != 0) { + String editResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + } + List currentFacetObjectIds = + sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); + if (currentFacetObjectIds.size() != 2) { + fail("Not enough object IDs to copy attachments for facet: " + facet); + } + copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, currentFacetObjectIds); + i += 2; + if (copyResponse.equals("Attachments copied successfully")) { + // Fetch copied attachment IDs from target draft + List> copiedMetadataResponse = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyAttachmentTargetEntity); + List copiedAttachmentIds = + copiedMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadata( + appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + if (targetAttachmentIds.size() == 4) { + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } - // @Test - // @Order(51) - // void testEditLinkFailureInvalidURL() throws IOException { - // System.out.println("Test (48): Edit existing link with invalid url"); - // List> attachmentsPerFacet = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + @Test + @Order(41) + void testCopyAttachmentsUnsuccessfulExistingEntity() throws IOException { + System.out.println("Test (41): Copy attachments from one entity to another new entity"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String editResponse2 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (editResponse1.equals("Entity in draft mode") + && editResponse2.equals("Entity in draft mode")) { + if (sourceObjectIds.size() == 6) { + int i = 0; + for (String facetName : facet) { + List currentFacetObjectIds = + sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size())); + currentFacetObjectIds.add("incorrectObjectId"); + if (currentFacetObjectIds.size() != 3) { + fail("Not enough object IDs to copy attachments for facet: " + facet); + } + try { + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); + fail("Copy attachments did not throw an error"); + } catch (IOException e) { + i += 2; + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } - // for (String facetName : facet) { - // List attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + @Test + @Order(42) + void testCreateLinkSuccess() throws IOException { + System.out.println("Test (42): Create link in entity"); + List attachments = new ArrayList<>(); - // if (attachments.isEmpty()) { - // fail("Could not edit link in facet: " + facetName); - // } - // attachmentsPerFacet.add(attachments); - // } + createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } - // int index = 0; - // for (String facetName : facet) { - // try { - // String linkId = attachmentsPerFacet.get(index).get(0); - // String updatedUrl = "https://editedexample"; - // index++; - // String editLinkResponse = - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // System.out.println("response " + editLinkResponse); - // fail("Edit link did not throw an error for invalid url in facet: " + facetName); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("400018", errorCode); - // assertTrue( - // errorMessage.equals("Enter a value that is within the expected pattern.") - // || errorMessage.equals("Enter a value that matches the expected pattern."), - // "Unexpected error message: " + errorMessage); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // } + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + for (String facetName : facet) { + String createLinkResponse1 = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + String createLinkResponse2 = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName + "1", linkUrl); + if (!createLinkResponse1.equals("Link created successfully") + || !createLinkResponse2.equals("Link created successfully")) { + fail("Could not create links for facet : " + facetName + createLinkResponse1); + } + } - // @Test - // @Order(52) - // void testEditLinkFailureEmptyURL() throws IOException { - // System.out.println("Test (49): Edit existing link with an empty url"); - // List> attachmentsPerFacet = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // for (String facetName : facet) { - // List attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + for (String facetName : facet) { + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String openAttachmentResponse; + for (String attachment : attachments) { + openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, createLinkEntity, attachment); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open created link in facet : " + facetName); + } + } + } + } - // if (attachments.isEmpty()) { - // fail("Could not edit link in facet: " + facetName); - // } - // attachmentsPerFacet.add(attachments); - // } + @Test + @Order(43) + void testCreateLinkDifferentEntity() throws IOException { + System.out.println("Test (43): Create link with same name in different entity"); - // int index = 0; - // for (String facetName : facet) { - // try { - // String linkId = attachmentsPerFacet.get(index).get(0); - // String updatedUrl = ""; - // index++; - - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // fail("Edit link did not throw an error for empty url in facet: " + facetName); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = "Provide the missing value."; - // assertEquals("409008", errorCode); - // assertEquals(expected, errorMessage); - // } - // } - // api.deleteEntity(appUrl, entityName, editLinkEntity); - // } + String createLinkDifferentEntity = + api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkDifferentEntity.equals("Could not edit entity")) { + fail("Could not create entity"); + } - // @Test - // @Order(53) - // void testEditLinkNoSDMRoles() throws IOException { - // System.out.println("Test (50): Edit link fails due to no SDM roles assigned"); + String linkName = "sample"; + String linkUrl = "https://example.com"; + for (String facetName : facet) { + String createResponse = + api.createLink( + appUrl, entityName, facetName, createLinkDifferentEntity, linkName, linkUrl); + if (!createResponse.equals("Link created successfully")) { + fail("Could not create link in different entity with same name"); + } + } - // Boolean testStatus = false; + String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkDifferentEntity); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } - // editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (editLinkEntity.equals("Could not create entity")) { - // fail("Could not edit entity"); - // } + response = api.deleteEntity(appUrl, entityName, createLinkDifferentEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } - // for (String facetName : facet) { - // String linkName = "sampleNoRole_" + facetName; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link in facet: " + facetName); - // } - // } + @Test + @Order(44) + void testCreateLinkFailure() throws IOException { + System.out.println("Test (41): Create link fails due to invalid URL and name"); + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (editEntityResponse.equals("Could not edit entity")) { + fail("Could not edit entity"); + } + for (String facetName : facet) { + String linkName = "sample"; + String linkUrl = "example.com"; + try { + String response = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + fail("Create link did not throw an error for invalid url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("400018", errorCode); + assertTrue( + errorMessage.equals("Enter a value that is within the expected pattern.") + || errorMessage.equals("Enter a value that matches the expected pattern."), + "Unexpected error message: " + errorMessage); + } + try { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName + "//", "https://" + linkUrl); + fail("Create link did not throw an error for invalid name"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = + "\"sample//\" contains unsupported characters (‘/’ or ‘\\’). Rename and try again."; + assertEquals("500", errorCode); + assertEquals( + expected.replaceAll("\\s+", " ").trim(), errorMessage.replaceAll("\\s+", " ").trim()); + } + try { + api.createLink(appUrl, entityName, facetName, createLinkEntity, "", ""); + fail("Create link did not throw an error for empty name and url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = "Provide the missing value."; + assertEquals("409008", errorCode); + assertEquals(expected, errorMessage); + } + try { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName, "https://" + linkUrl); + fail("Create link did not throw an error for duplicate name"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals( + "An object named \"sample\" already exists. Rename the object and try again.", + errorMessage); + } + try { + for (int i = 2; i < 6; i++) { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName + i, "https://" + linkUrl); + } + System.out.println("Created 5 links in facet: " + facetName); + if (!facetName.equals("footnotes")) { + fail("More than 5 links were created in the same entity"); + } + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + if (facetName.equals("references")) { + assertEquals("Cannot upload more than 5 attachments.", errorMessage); + } else if (facetName.equals("attachments")) { + assertEquals("Cannot upload more than 4 attachments.", errorMessage); + } + } + } - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } - // String editEntityResponse = - // apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + response = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } - // for (String facetName : facet) { - // List attachments = - // apiNoRoles.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + @Test + @Order(45) + void testCreateLinkNoSDMRoles() throws IOException { + System.out.println("Test (42): Create link fails due to no SDM roles assigned"); - // if (attachments.isEmpty()) { - // fail("Could not find link in facet: " + facetName); - // } + String createLinkEntityNoSDMRoles = + apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntityNoSDMRoles.equals("Could not edit entity")) { + fail("Could not create entity"); + } - // String linkId = attachments.get(0); - // String updatedUrl = "https://www.editedexample.com"; - - // try { - // apiNoRoles.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // fail("Link got edited without SDM roles in facet: " + facetName); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - - // assertEquals("500", errorCode); - // assertEquals( - // "You do not have the required permissions to update attachments. Kindly contact the - // admin", - // errorMessage); - - // testStatus = true; - // } - // } - // api.deleteEntity(appUrl, entityName, editLinkEntity); - // if (!testStatus) { - // fail("Link got edited without SDM roles"); - // } - // } + for (String facetName : facet) { + String linkName = "sample27"; + String linkUrl = "https://example.com"; + try { + apiNoRoles.createLink( + appUrl, entityName, facetName, createLinkEntityNoSDMRoles, linkName, linkUrl); + fail("Link got created without SDM roles"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals( + "You do not have the required permissions to upload attachments. Please contact your administrator for access.", + errorMessage); + } + } - // @Test - // @Order(54) - // void testCopyLinkSuccessNewEntity() throws IOException { - // System.out.println("Test (51): Copy link from one entity to another new entity"); - // List> attachmentsByFacet = new ArrayList<>(); - // String linkUrl = "https://www.example.com"; - // for (int i = 0; i < facet.length; i++) { - // attachmentsByFacet.add(new ArrayList<>()); - // } + String response = + apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntityNoSDMRoles); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + response = api.deleteEntity(appUrl, entityName, createLinkEntityNoSDMRoles); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } - // if (copyLinkSourceEntity.equals("Could not create entity") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not create source or target entities"); - // } + @Test + @Order(46) + void testDeleteLink() throws IOException { + System.out.println("Test (43): Delete link in entity"); + List> attachments = new ArrayList<>(); - // for (int i = 0; i < facet.length; i++) { - // String linkName = "sample" + i; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet: " + facet[i]); - // } - // } + String createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + for (String facetName : facet) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet : " + facetName); + } + } - // sourceObjectIds.clear(); - // for (int i = 0; i < facet.length; i++) { - // List objectIds = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // sourceObjectIds.addAll(objectIds); - // } + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // if (sourceObjectIds.size() != facet.length) { - // fail( - // "Could not fetch object Ids for all attachments. Expected: " - // + facet.length - // + ", Found: " - // + sourceObjectIds.size()); - // } + for (String facetName : facet) { + attachments.add( + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + } - // int objectIdIndex = 0; - // for (String facetName : facet) { - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft for facet: " + facetName); - // } + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); + int index = 0; + for (String facetName : facet) { + String deleteLinkResponse = + api.deleteAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(index).get(0)); + System.out.println("Delete response for facet " + facetName + ": " + deleteLinkResponse); + if (!deleteLinkResponse.equals("Deleted")) { + fail("Could not delete created link"); + } + index += 1; + } - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); - // } + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity after copying attachments for facet " + facetName); - // } + index = 0; + attachments.clear(); + for (String facetName : facet) { + attachments.add( + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + System.out.println( + "Attachments after deletion in facet " + facetName + ": " + attachments.get(index)); + if (attachments.get(index).size() != 0) { + fail("Link wasn't deleted"); + } + index += 1; + } - // List> attachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); + String response = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); + @Test + @Order(47) + void testRenameLinkSuccess() throws IOException { + System.out.println("Test (44): Rename link in entity"); + List> attachments = new ArrayList<>(); - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch in facet " + facetName); + createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); - // System.out.println("Attachment type and URL validated for facet " + facetName); + for (String facetName : facet) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + } - // List attachments = - // attachmentsMetadata.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // for (String attachment : attachments) { - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open copied link in facet: " + facetName); - // } - // } + for (String facetName : facet) { + attachments.add( + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + } - // objectIdIndex++; - // } + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // if (!deleteTargetResponse.equals("Entity Deleted")) { - // fail("Could not delete target entity"); - // } - // } + int index = 0; + for (String facetName : facet) { + successfullyRenamedAttachments.add(attachments.get(index).get(0)); + String renameLinkResponse = + api.renameAttachment( + appUrl, + entityName, + facetName, + createLinkEntity, + attachments.get(index).get(0), + "sampleRenamed"); + if (!renameLinkResponse.equals("Renamed")) { + fail("Could not Renamed created link"); + } + index += 1; + } - // @Test - // @Order(55) - // void testCopyLinkUnsuccessfulNewEntity() throws IOException { - // System.out.println( - // "Test (52): Copy invalid type of link from one entity to another new entity"); - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + } - // if (!editResponse.equals("Entity in draft mode") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not edit source entity or create target entity"); - // } + @Test + @Order(48) + void testRenameLinkDuplicate() throws IOException { + System.out.println("Test (45): Rename link in entity fails due to duplicate error"); + List attachments = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // sourceObjectIds.add("incorrectObjectId"); + int index = 0; + for (String facetName : facet) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + } - // for (String facetName : facet) { - // try { - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); - // fail("Copy attachments did not throw an error for facet: " + facetName); - // } catch (IOException e) { - // System.out.println("Successfully caught expected error for facet: " + facetName); - // } - // } - // api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // } + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (saveResponse.equals("Could not save entity")) { + fail("Could not save entity"); + } - // @Test - // @Order(56) - // void testCopyLinkFromNewEntityToExistingEntity() throws IOException { - // System.out.println("Test (53): Copy link from a new entity to an existing target entity"); + index = 0; + List facetAttachments; + for (String facetName : facet) { + int lambdaIndex = index; + facetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .filter( + item -> + !successfullyRenamedAttachments + .get(lambdaIndex) + .equals(item.get("ID"))) // skip unwanted filename + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + index += 1; + attachments.add(facetAttachments.get(0)); + } - // List> attachmentsByFacet = new ArrayList<>(); - // String linkUrl = "https://www.example.com"; - // for (int i = 0; i < facet.length; i++) { - // attachmentsByFacet.add(new ArrayList<>()); - // } - - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyLinkSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + System.out.println("Attachments to be renamed: " + attachments); + String response = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!response.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // for (int i = 0; i < facet.length; i++) { - // String linkName = "newsample" + i; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet: " + facet[i]); - // } - // } + index = 0; + for (String facetName : facet) { + api.renameAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(index), "sampleRenamed"); + index += 1; + } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + String saveError = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + String expectedWarning = + "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"An object named \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + ObjectMapper mapper = new ObjectMapper(); + assertEquals(mapper.readTree(expectedWarning), mapper.readTree(saveError)); - // sourceObjectIds.clear(); - // for (String facetName : facet) { - // List objectIds = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // sourceObjectIds.addAll(objectIds); - // } + String deleteEntityResponse = api.deleteEntityDraft(appUrl, entityName, createLinkEntity); + if (!deleteEntityResponse.equals("Entity Draft Deleted")) { + fail("Entity draft not deleted"); + } + } - // if (sourceObjectIds.isEmpty()) { - // fail("Could not fetch object Ids for any attachments"); - // } + @Test + @Order(49) + void testRenameLinkUnsupportedCharacters() throws IOException { + System.out.println( + "Test (46): Rename link in entity fails due to unsupported characters in name"); + List> attachments = new ArrayList<>(); - // int objectIdIndex = 0; - // for (String facetName : facet) { - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft for facet: " + facetName); - // } + createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } - // List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); + String linkName = "sample2"; + String linkUrl = "https://www.example.com"; - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); - // } + for (String facetName : facet) { + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + } - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity after copying attachments for facet " + facetName); - // } + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // List> attachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); + for (String facetName : facet) { + attachments.add( + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + } - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch in facet " + facetName); + int index = 0; + for (String facetName : facet) { + api.renameAttachment( + appUrl, + entityName, + facetName, + createLinkEntity, + attachments.get(index).get(0), + "sampleRenamed//"); + index += 1; + } - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); - // System.out.println("Attachment type and URL validated for facet " + facetName); + String error = + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + String expectedError = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: references\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: footnotes\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + ObjectMapper mapper = new ObjectMapper(); + assertEquals(mapper.readTree(expectedError), mapper.readTree(error)); - // List attachments = - // attachmentsMetadata.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!deleteEntityResponse.equals("Entity Deleted")) { + fail("Entity draft not deleted"); + } + } - // for (String attachment : attachments) { - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open copied link in facet: " + facetName); - // } - // } + @Test + @Order(50) + void testEditLinkSuccess() throws IOException { + System.out.println("Test (47): Edit existing link in entity"); + List> attachmentsPerFacet = new ArrayList<>(); - // objectIdIndex++; - // } + editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (editLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } - // api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // } + for (String facetName : facet) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet: " + facetName); + } + } - // @Test - // @Order(57) - // void testCopyInvalidLinkFromNewEntityToExistingEntity() throws IOException { - // System.out.println( - // "Test (54): Copy invalid type of link from new entity to existing target entity"); - // String linkUrl = "https://www.example.com"; + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyLinkSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // for (int i = 0; i < facet.length; i++) { - // String linkName = "newsample" + i; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet: " + facet[i]); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit entities"); - // } - // for (String facetName : facet) { - // List sourceObjectIds = new ArrayList<>(); - // sourceObjectIds.add("incorrectObjectId"); - // try { - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); - // fail("Copy attachments did not throw an error for facet: " + facetName); - // } catch (IOException e) { - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // } + for (String facetName : facet) { + List attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - // @Test - // @Order(58) - // void testCopyLinkSuccessNewEntityDraft() throws IOException { - // System.out.println("Test (55): Copy link from one entity to another new entity draft mode"); - // List> attachmentsByFacet = new ArrayList<>(); - // String linkUrl = "https://www.example.com"; - // for (int i = 0; i < facet.length; i++) { - // attachmentsByFacet.add(new ArrayList<>()); - // } + if (attachments.isEmpty()) { + fail("Could not find link in facet: " + facetName); + } + attachmentsPerFacet.add(attachments); + } - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + int index = 0; + for (String facetName : facet) { + String linkId = attachmentsPerFacet.get(index).get(0); + String updatedUrl = "https://editedexample.com"; + String editLinkResponse = + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + if (!editLinkResponse.equals("Link edited successfully")) { + fail("Could not edit link in facet: " + facetName); + } + index++; + } + api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + + int verificationIndex = 0; + for (String facetName : facet) { + List attachmentsInFacet = attachmentsPerFacet.get(verificationIndex); + for (String attachmentId : attachmentsInFacet) { + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, editLinkEntity, attachmentId); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open edited link " + attachmentId + " in facet: " + facetName); + } + } + verificationIndex++; + } + } - // if (copyLinkSourceEntity.equals("Could not create entity") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not create source or target entities"); - // } + @Test + @Order(51) + void testEditLinkFailureInvalidURL() throws IOException { + System.out.println("Test (48): Edit existing link with invalid url"); + List> attachmentsPerFacet = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // for (int i = 0; i < facet.length; i++) { - // String linkName = "sample" + i; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link for facet: " + facet[i]); - // } - // } + for (String facetName : facet) { + List attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (attachments.isEmpty()) { + fail("Could not edit link in facet: " + facetName); + } + attachmentsPerFacet.add(attachments); + } - // sourceObjectIds.clear(); - // for (int i = 0; i < facet.length; i++) { - // List objectIds = - // api.fetchEntityMetadataDraft(appUrl, entityName, facet[i], - // copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // sourceObjectIds.addAll(objectIds); - // } + int index = 0; + for (String facetName : facet) { + try { + String linkId = attachmentsPerFacet.get(index).get(0); + String updatedUrl = "https://editedexample"; + index++; + String editLinkResponse = + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + System.out.println("response " + editLinkResponse); + fail("Edit link did not throw an error for invalid url in facet: " + facetName); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("400018", errorCode); + assertTrue( + errorMessage.equals("Enter a value that is within the expected pattern.") + || errorMessage.equals("Enter a value that matches the expected pattern."), + "Unexpected error message: " + errorMessage); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + } - // if (sourceObjectIds.size() != facet.length) { - // fail( - // "Could not fetch object Ids for all attachments. Expected: " - // + facet.length - // + ", Found: " - // + sourceObjectIds.size()); - // } + @Test + @Order(52) + void testEditLinkFailureEmptyURL() throws IOException { + System.out.println("Test (49): Edit existing link with an empty url"); + List> attachmentsPerFacet = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // int objectIdIndex = 0; - // for (String facetName : facet) { - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft for facet: " + facetName); - // } + for (String facetName : facet) { + List attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - // List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); + if (attachments.isEmpty()) { + fail("Could not edit link in facet: " + facetName); + } + attachmentsPerFacet.add(attachments); + } - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); - // } + int index = 0; + for (String facetName : facet) { + try { + String linkId = attachmentsPerFacet.get(index).get(0); + String updatedUrl = ""; + index++; + + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + fail("Edit link did not throw an error for empty url in facet: " + facetName); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = "Provide the missing value."; + assertEquals("409008", errorCode); + assertEquals(expected, errorMessage); + } + } + api.deleteEntity(appUrl, entityName, editLinkEntity); + } - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity after copying attachments for facet " + facetName); - // } + @Test + @Order(53) + void testEditLinkNoSDMRoles() throws IOException { + System.out.println("Test (50): Edit link fails due to no SDM roles assigned"); - // List> attachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); + Boolean testStatus = false; - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); + editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (editLinkEntity.equals("Could not create entity")) { + fail("Could not edit entity"); + } - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch in facet " + facetName); + for (String facetName : facet) { + String linkName = "sampleNoRole_" + facetName; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link in facet: " + facetName); + } + } - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); - // System.out.println("Attachment type and URL validated for facet " + facetName); + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } - // List attachments = - // attachmentsMetadata.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + String editEntityResponse = + apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } - // for (String attachment : attachments) { - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open copied link in facet: " + facetName); - // } - // } + for (String facetName : facet) { + List attachments = + apiNoRoles.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); - // objectIdIndex++; - // } + if (attachments.isEmpty()) { + fail("Could not find link in facet: " + facetName); + } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // } + String linkId = attachments.get(0); + String updatedUrl = "https://www.editedexample.com"; + + try { + apiNoRoles.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + fail("Link got edited without SDM roles in facet: " + facetName); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + + assertEquals("500", errorCode); + assertEquals( + "You do not have the required permissions to update attachments. Kindly contact the admin", + errorMessage); + + testStatus = true; + } + } + api.deleteEntity(appUrl, entityName, editLinkEntity); + if (!testStatus) { + fail("Link got edited without SDM roles"); + } + } - // @Test - // @Order(59) - // void testCopyAttachmentsSuccessNewEntityDraft() throws IOException { - // System.out.println( - // "Test (56): Copy attachments from one entity to another new entity draft mode"); - // List> attachments = new ArrayList<>(); - // for (int i = 0; i < 3; i++) { - // attachments.add(new ArrayList<>()); - // } - // copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!copyAttachmentSourceEntity.equals("Could not create entity") - // && !copyAttachmentTargetEntity.equals("Could not create entity")) { - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample1.pdf").getFile())); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(54) + void testCopyLinkSuccessNewEntity() throws IOException { + System.out.println("Test (51): Copy link from one entity to another new entity"); + List> attachmentsByFacet = new ArrayList<>(); + String linkUrl = "https://www.example.com"; + for (int i = 0; i < facet.length; i++) { + attachmentsByFacet.add(new ArrayList<>()); + } - // sourceObjectIds.clear(); - - // for (int i = 0; i < facet.length; i++) { - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facet[i], - // copyAttachmentSourceEntity, - // srvpath, - // postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.get(i).add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } - // } - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (int i = 0; i < attachments.size(); i++) { - // for (String attachment : attachments.get(i)) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadataDraft( - // appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } - // } - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (sourceObjectIds.size() == 6) { - // String copyResponse; - // int i = 0; - // for (String facetName : facet) { - // if (i != 0) { - // String editResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } - // } - // copyResponse = - // api.copyAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size()))); - // i += 2; - // if (copyResponse.equals("Attachments copied successfully")) { - // // Fetch copied attachment IDs from target draft - // List> copiedMetadataResponse = - // api.fetchEntityMetadataDraft( - // appUrl, entityName, facetName, copyAttachmentTargetEntity); - // List copiedAttachmentIds = - // copiedMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadataDraft( - // appUrl, entityName, facetName, copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not create entities"); - // } - // api.deleteEntityDraft(appUrl, entityName, copyAttachmentSourceEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); - // } + if (copyLinkSourceEntity.equals("Could not create entity") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not create source or target entities"); + } - // @Test - // @Order(60) - // void testViewChangelogForNewlyCreatedAttachment() throws IOException { - // System.out.println( - // "Test (60): View changelog for newly created attachment in all three facets"); + for (int i = 0; i < facet.length; i++) { + String linkName = "sample" + i; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet: " + facet[i]); + } + } - // for (int i = 0; i < 3; i++) { - // String facetName = facet[i]; + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // // Create a new entity for changelog test - // changelogEntityID[i] = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(changelogEntityID[i], "Failed to create changelog test entity"); - // assertNotEquals("Could not create entity", changelogEntityID[i]); + sourceObjectIds.clear(); + for (int i = 0; i < facet.length; i++) { + List objectIds = + api.fetchEntityMetadata(appUrl, entityName, facet[i], copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + sourceObjectIds.addAll(objectIds); + } - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.txt").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + if (sourceObjectIds.size() != facet.length) { + fail( + "Could not fetch object Ids for all attachments. Expected: " + + facet.length + + ", Found: " + + sourceObjectIds.size()); + } - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", changelogEntityID[i]); - // postData.put("mimeType", "text/plain"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + int objectIdIndex = 0; + for (String facetName : facet) { + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft for facet: " + facetName); + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, changelogEntityID[i], srvpath, postData, file); - - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // changelogAttachmentID[i] = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(changelogAttachmentID[i], "Attachment ID should not be null"); - // assertNotEquals("", changelogAttachmentID[i], "Attachment ID should not be empty"); - - // // Fetch changelog for the newly created attachment - // Map changelogResponse = - // api.fetchChangelog( - // appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog structure - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // "sample.txt", changelogResponse.get("filename"), "Filename should match uploaded - // file"); - // assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); - // assertEquals(1, changelogResponse.get("numItems"), "Should have 1 changelog entry"); - - // // Verify the changelog entry - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); - - // Map logEntry = changeLogs.get(0); - // assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); - // assertNotNull(logEntry.get("time"), "Time should not be null"); - // assertNotNull(logEntry.get("user"), "User should not be null"); - // assertFalse( - // logEntry.containsKey("changeDetail"), "Created operation should not have - // changeDetail"); - // } - // } + List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); - // @Test - // @Order(61) - // void testChangelogAfterModifyingNoteAndCustomProperty() throws IOException { - // System.out.println( - // "Test (61): Modify note field and custom property, then verify changelog shows created + - // 3 updated entries in all three facets"); - - // for (int i = 0; i < 3; i++) { - // String facetName = facet[i]; - - // // Update attachment with notes field (entity is already in draft mode from test 60) - // String notesValue = "Test note for changelog verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // changelogEntityID[i], - // changelogAttachmentID[i], - // updateNotesBody); - // assertEquals("Updated", updateNotesResponse, "Should successfully update notes field"); - - // // Update attachment with custom property - // Integer customProperty2Value = 12345; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // changelogEntityID[i], - // changelogAttachmentID[i], - // bodyInt); - // assertEquals( - // "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - - // // Save the entity - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // changelogEntityID[i]); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity again to fetch changelog - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // changelogEntityID[i]); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after modifications - // Map changelogResponse = - // api.fetchChangelog( - // appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog content - should have 1 created + 3 updated (note, customProperty2, and - // // internal update) - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // 4, - // changelogResponse.get("numItems"), - // "Should have 4 changelog entries (1 created + 3 updated)"); - - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(4, changeLogs.size(), "Should have exactly 4 changelog entries"); - - // // Verify first entry is 'created' - // Map createdEntry = changeLogs.get(0); - // assertEquals( - // "created", createdEntry.get("operation"), "First entry should be 'created' operation"); - - // // Verify remaining entries are 'updated' - // long updatedCount = - // changeLogs.stream().filter(log -> "updated".equals(log.get("operation"))).count(); - // assertEquals(3, updatedCount, "Should have 3 'updated' operations"); - - // // Verify that changeDetail exists in updated entries for note field - // boolean hasNoteUpdate = - // changeLogs.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .anyMatch( - // log -> { - // @SuppressWarnings("unchecked") - // Map changeDetail = - // (Map) log.get("changeDetail"); - // return changeDetail != null - // && "cmis:description".equals(changeDetail.get("field")); - // }); - // assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); - - // // Save the entity so test 62 can edit it - // String saveResponseFinal = - // api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); - // assertEquals("Saved", saveResponseFinal, "Entity should be saved successfully"); - // } - // } + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); + } - // @Test - // @Order(62) - // void testChangelogAfterRenamingAttachment() throws IOException { - // System.out.println( - // "Test (62): Rename attachment and verify changelog increases with rename entry in all - // three facets"); - - // for (int i = 0; i < 3; i++) { - // String facetName = facet[i]; - - // // Edit entity to put it in draft mode (entity was saved at end of test 61) - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // changelogEntityID[i]); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Rename the attachment - // String newFileName = "renamed_sample.txt"; - // String renameResponse = - // api.renameAttachment( - // appUrl, - // entityName, - // facetName, - // changelogEntityID[i], - // changelogAttachmentID[i], - // newFileName); - // assertEquals("Renamed", renameResponse, "Should successfully rename attachment"); - - // // Save entity after rename - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // changelogEntityID[i]); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully after rename"); - - // // Edit entity again and fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after rename - // Map changelogAfterRename = - // api.fetchChangelog( - // appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - - // assertNotNull(changelogAfterRename, "Changelog response should not be null after rename"); - - // // Verify changelog has increased (rename operation adds 1 entry for cmis:name change) - // // Expected: 1 created + 3 initial updates + 1 rename update = 5 total - // assertEquals( - // 5, changelogAfterRename.get("numItems"), "Should have 5 changelog entries after - // rename"); - - // @SuppressWarnings("unchecked") - // List> changeLogsAfterRename = - // (List>) changelogAfterRename.get("changeLogs"); - // assertEquals( - // 5, changeLogsAfterRename.size(), "Should have exactly 5 changelog entries after - // rename"); - - // // Verify updated count is 4 (3 initial + 1 from rename operation) - // long updatedCountAfterRename = - // changeLogsAfterRename.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .count(); - // assertEquals(4, updatedCountAfterRename, "Should have 4 'updated' operations after - // rename"); - - // // Verify filename change in changelog - // boolean hasFilenameUpdate = - // changeLogsAfterRename.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .anyMatch( - // log -> { - // @SuppressWarnings("unchecked") - // Map changeDetail = - // (Map) log.get("changeDetail"); - // return changeDetail != null && "cmis:name".equals(changeDetail.get("field")); - // }); - // assertTrue(hasFilenameUpdate, "Should have an update entry for filename (cmis:name)"); - - // // Cleanup - entity was saved after rename, so delete the active entity - // api.deleteEntity(appUrl, entityName, changelogEntityID[i]); - // } - // } + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity after copying attachments for facet " + facetName); + } - // @Test - // @Order(63) - // void testChangelogWithCustomPropertyEditSave() throws IOException { - // System.out.println( - // "Test (63): Create entity with custom property, save, edit and save again - verify - // changelog remains at 3 entries in all three facets"); + List> attachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); - // for (int i = 0; i < 3; i++) { - // String facetName = facet[i]; + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); - // // Create a new entity - // String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(newEntityID, "Failed to create new entity"); - // assertNotEquals("Could not create entity", newEntityID); + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch in facet " + facetName); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); + System.out.println("Attachment type and URL validated for facet " + facetName); + + List attachments = + attachmentsMetadata.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (String attachment : attachments) { + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open copied link in facet: " + facetName); + } + } - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + objectIdIndex++; + } - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", newEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + if (!deleteTargetResponse.equals("Entity Deleted")) { + fail("Could not delete target entity"); + } + } - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, - // file); + @Test + @Order(55) + void testCopyLinkUnsuccessfulNewEntity() throws IOException { + System.out.println( + "Test (52): Copy invalid type of link from one entity to another new entity"); + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // String attachmentID = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(attachmentID, "Attachment ID should not be null"); - // assertNotEquals("", attachmentID, "Attachment ID should not be empty"); - - // // Add a custom property - // Integer customPropertyValue = 99999; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customPropertyValue + "}", - // MediaType.parse("application/json")); - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, newEntityID, attachmentID, bodyInt); - // assertEquals( - // "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - - // // Save the entity - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity to fetch initial changelog - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after initial save - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog has 3 entries: 1 created + 2 updated (cmis:secondaryObjectTypeIds + - // // customProperty2) - // assertEquals( - // 3, changelogResponse.get("numItems"), "Should have 3 changelog entries initially"); - - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(3, changeLogs.size(), "Should have exactly 3 changelog entries"); - - // // Save entity again without any modifications - // saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - - // // Edit entity again and fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after second save - // Map changelogAfterSecondSave = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - - // assertNotNull( - // changelogAfterSecondSave, "Changelog response should not be null after second save"); - - // // Verify changelog still has only 3 entries (no new entries added) - // assertEquals( - // 3, - // changelogAfterSecondSave.get("numItems"), - // "Should still have only 3 changelog entries after edit-save without modifications"); - - // @SuppressWarnings("unchecked") - // List> changeLogsAfterSecondSave = - // (List>) changelogAfterSecondSave.get("changeLogs"); - // assertEquals( - // 3, - // changeLogsAfterSecondSave.size(), - // "Should still have exactly 3 changelog entries after second save"); - - // // Clean up the entity - // api.deleteEntity(appUrl, entityName, newEntityID); - // } - // } + if (!editResponse.equals("Entity in draft mode") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not edit source entity or create target entity"); + } - // @Test - // @Order(64) - // void testChangelogForSavedAttachmentWithoutModification() throws IOException { - // System.out.println( - // "Test (64): Create entity, upload attachment, save, edit and save again - verify - // changelog still has only 'created' entry in all three facets"); + sourceObjectIds.add("incorrectObjectId"); - // for (int i = 0; i < 3; i++) { - // String facetName = facet[i]; + for (String facetName : facet) { + try { + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); + fail("Copy attachments did not throw an error for facet: " + facetName); + } catch (IOException e) { + System.out.println("Successfully caught expected error for facet: " + facetName); + } + } + api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + } - // // Create a new entity - // String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(newEntityID, "Failed to create new entity"); - // assertNotEquals("Could not create entity", newEntityID); + @Test + @Order(56) + void testCopyLinkFromNewEntityToExistingEntity() throws IOException { + System.out.println("Test (53): Copy link from a new entity to an existing target entity"); - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + List> attachmentsByFacet = new ArrayList<>(); + String linkUrl = "https://www.example.com"; + for (int i = 0; i < facet.length; i++) { + attachmentsByFacet.add(new ArrayList<>()); + } - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", newEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyLinkSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, - // file); + for (int i = 0; i < facet.length; i++) { + String linkName = "newsample" + i; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet: " + facet[i]); + } + } - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // String newAttachmentID = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(newAttachmentID, "Attachment ID should not be null"); - // assertNotEquals("", newAttachmentID, "Attachment ID should not be empty"); - - // // Save the entity immediately without any modifications - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity again without making any changes to the attachment - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Save entity again without modifying the attachment - // saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - - // // Edit entity to fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog for the attachment - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, newAttachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog content - should only have 'created' entry even after edit and save - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // "sample.pdf", changelogResponse.get("filename"), "Filename should match uploaded - // file"); - // assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); - // assertEquals(1, changelogResponse.get("numItems"), "Should have only 1 changelog entry"); - - // // Verify the changelog entry - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); - - // Map logEntry = changeLogs.get(0); - // assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); - // assertNotNull(logEntry.get("time"), "Time should not be null"); - // assertNotNull(logEntry.get("user"), "User should not be null"); - // assertFalse( - // logEntry.containsKey("changeDetail"), "Created operation should not have - // changeDetail"); - - // // Clean up the new entity - // api.deleteEntity(appUrl, entityName, newEntityID); - // } - // } + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // @Test - // @Order(65) - // void testMoveAttachmentsWithSourceFacet() throws IOException { - // System.out.println( - // "Test (65): Move attachments from Source Entity to Target Entity with sourceFacet"); + sourceObjectIds.clear(); + for (String facetName : facet) { + List objectIds = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + sourceObjectIds.addAll(objectIds); + } - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + if (sourceObjectIds.isEmpty()) { + fail("Could not fetch object Ids for any attachments"); + } - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); - // files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + int objectIdIndex = 0; + for (String facetName : facet) { + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft for facet: " + facetName); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); + } - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity"); - // } + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity after copying attachments for facet " + facetName); + } - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } + List> attachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch in facet " + facetName); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); + System.out.println("Attachment type and URL validated for facet " + facetName); + + List attachments = + attachmentsMetadata.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (String attachment : attachments) { + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open copied link in facet: " + facetName); + } + } - // // Save target before move - // String saveTargetBeforeMoveTest65 = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveTest65.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveTest65); - // } + objectIdIndex++; + } - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + } - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals( - // sourceAttachmentIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all attachments after move"); - - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // 0, sourceMetadataAfterMove.size(), "Source entity should have no attachments after - // move"); - - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + @Test + @Order(57) + void testCopyInvalidLinkFromNewEntityToExistingEntity() throws IOException { + System.out.println( + "Test (54): Copy invalid type of link from new entity to existing target entity"); + String linkUrl = "https://www.example.com"; - // @Test - // @Order(66) - // public void testMoveAttachmentsToEntityWithDuplicateWithSourceFacet() throws Exception { - // System.out.println( - // "Test (66): Move attachments to entity with duplicate attachment with sourceFacet"); + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyLinkSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + for (int i = 0; i < facet.length; i++) { + String linkName = "newsample" + i; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet: " + facet[i]); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit entities"); + } + for (String facetName : facet) { + List sourceObjectIds = new ArrayList<>(); + sourceObjectIds.add("incorrectObjectId"); + try { + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); + fail("Copy attachments did not throw an error for facet: " + facetName); + } catch (IOException e) { + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + } - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + @Test + @Order(58) + void testCopyLinkSuccessNewEntityDraft() throws IOException { + System.out.println("Test (55): Copy link from one entity to another new entity draft mode"); + List> attachmentsByFacet = new ArrayList<>(); + String linkUrl = "https://www.example.com"; + for (int i = 0; i < facet.length; i++) { + attachmentsByFacet.add(new ArrayList<>()); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + if (copyLinkSourceEntity.equals("Could not create entity") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not create source or target entities"); + } - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity"); - // } + for (int i = 0; i < facet.length; i++) { + String linkName = "sample" + i; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[i], copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link for facet: " + facet[i]); + } + } - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + sourceObjectIds.clear(); + for (int i = 0; i < facet.length; i++) { + List objectIds = + api.fetchEntityMetadataDraft(appUrl, entityName, facet[i], copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + sourceObjectIds.addAll(objectIds); + } - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + if (sourceObjectIds.size() != facet.length) { + fail( + "Could not fetch object Ids for all attachments. Expected: " + + facet.length + + ", Found: " + + sourceObjectIds.size()); + } - // Map targetPostData = new HashMap<>(); - // targetPostData.put("up__ID", moveTargetEntity); - // targetPostData.put("mimeType", "application/pdf"); - // targetPostData.put("createdAt", new Date().toString()); - // targetPostData.put("createdBy", "test@test.com"); - // targetPostData.put("modifiedBy", "test@test.com"); - - // File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); - // List targetCreateResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // srvpath, - // targetPostData, - // duplicateFile); - - // if (!targetCreateResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment on target entity"); - // } + int objectIdIndex = 0; + for (String facetName : facet) { + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft for facet: " + facetName); + } - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + List subListToCopy = sourceObjectIds.subList(objectIdIndex, objectIdIndex + 1); + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, subListToCopy); - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // int targetCountBeforeMove = targetMetadataBeforeMove.size(); - - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachments for facet " + facetName + ": " + copyResponse); + } - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - - // int expectedTargetCount = targetCountBeforeMove + (sourceAttachmentIds.size() - 1); - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target should have duplicate skipped, other attachments moved"); - - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // int expectedSourceCount = - // sourceAttachmentIds.size() - (targetMetadataAfterMove.size() - targetCountBeforeMove); - // assertEquals( - // expectedSourceCount, - // sourceMetadataAfterMove.size(), - // "Source should have duplicate attachment remaining"); - - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity after copying attachments for facet " + facetName); + } - // @Test - // @Order(67) - // public void testMoveAttachmentsWithNotesAndSecondaryProperties() throws Exception { - // System.out.println( - // "Test (67): Move attachments with notes and secondary properties with sourceFacet"); + List> attachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch in facet " + facetName); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch in facet " + facetName); + System.out.println("Attachment type and URL validated for facet " + facetName); + + List attachments = + attachmentsMetadata.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (String attachment : attachments) { + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachment); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open copied link in facet: " + facetName); + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + objectIdIndex++; + } - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + } - // String notesValue = "Test note for verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, attachmentId, updateNotesBody); - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update notes for attachment: " + attachmentId); - // } - // } + @Test + @Order(59) + void testCopyAttachmentsSuccessNewEntityDraft() throws IOException { + System.out.println( + "Test (56): Copy attachments from one entity to another new entity draft mode"); + List> attachments = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + attachments.add(new ArrayList<>()); + } + copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!copyAttachmentSourceEntity.equals("Could not create entity") + && !copyAttachmentTargetEntity.equals("Could not create entity")) { + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample1.pdf").getFile())); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // Integer customProperty2Value = 54321; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, attachmentId, bodyInt); - // if (!updateCustomPropertyResponse.equals("Updated")) { - // fail("Could not update custom property for attachment: " + attachmentId); - // } - // } + sourceObjectIds.clear(); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + for (int i = 0; i < facet.length; i++) { + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, + entityName, + facet[i], + copyAttachmentSourceEntity, + srvpath, + postData, + file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.get(i).add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } + } + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (int i = 0; i < attachments.size(); i++) { + for (String attachment : attachments.get(i)) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadataDraft( + appUrl, entityName, facet[i], copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + } + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + if (sourceObjectIds.size() == 6) { + String copyResponse; + int i = 0; + for (String facetName : facet) { + if (i != 0) { + String editResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + } + copyResponse = + api.copyAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + sourceObjectIds.subList(i, Math.min(i + 2, sourceObjectIds.size()))); + i += 2; + if (copyResponse.equals("Attachments copied successfully")) { + // Fetch copied attachment IDs from target draft + List> copiedMetadataResponse = + api.fetchEntityMetadataDraft( + appUrl, entityName, facetName, copyAttachmentTargetEntity); + List copiedAttachmentIds = + copiedMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadataDraft( + appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not create entities"); + } + api.deleteEntityDraft(appUrl, entityName, copyAttachmentSourceEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); + } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + @Test + @Order(60) + void testViewChangelogForNewlyCreatedAttachment() throws IOException { + System.out.println( + "Test (60): View changelog for newly created attachment in all three facets"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + for (int i = 0; i < 3; i++) { + String facetName = facet[i]; - // // Save target before move - // String saveTargetBeforeMoveTest67 = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveTest67.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveTest67); - // } + // Create a new entity for changelog test + changelogEntityID[i] = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(changelogEntityID[i], "Failed to create changelog test entity"); + assertNotEquals("Could not create entity", changelogEntityID[i]); - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.txt").getFile()); + assertTrue(file.exists(), "Sample file should exist"); - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals( - // sourceAttachmentIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all attachments after move"); - - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, - // targetAttachmentId); - - // if (detailedMetadata.containsKey("note")) { - // assertEquals( - // notesValue, - // detailedMetadata.get("note"), - // "Notes should be preserved after move for attachment: " + targetAttachmentId); - // } else { - // fail("Notes property missing after move for attachment: " + targetAttachmentId); - // } + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", changelogEntityID[i]); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (detailedMetadata.containsKey("customProperty2")) { - // assertEquals( - // customProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Custom property should be preserved after move for attachment: " - // + targetAttachmentId); - // } else { - // fail("Custom property missing after move for attachment: " + targetAttachmentId); - // } - // } + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, changelogEntityID[i], srvpath, postData, file); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // 0, sourceMetadataAfterMove.size(), "Source entity has no attachments after move"); + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + changelogAttachmentID[i] = createResponse.get(1); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(changelogAttachmentID[i], "Attachment ID should not be null"); + assertNotEquals("", changelogAttachmentID[i], "Attachment ID should not be empty"); - // @Test - // @Order(68) - // public void testMoveAttachmentsWithoutSourceFacet() throws Exception { - // System.out.println( - // "Test (68): Move valid attachments from Source Entity to Target Entity without - // sourceFacet"); + // Fetch changelog for the newly created attachment + Map changelogResponse = + api.fetchChangelog( + appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + assertNotNull(changelogResponse, "Changelog response should not be null"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Verify changelog structure + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + "sample.txt", changelogResponse.get("filename"), "Filename should match uploaded file"); + assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); + assertEquals(1, changelogResponse.get("numItems"), "Should have 1 changelog entry"); + + // Verify the changelog entry + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); + + Map logEntry = changeLogs.get(0); + assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); + assertNotNull(logEntry.get("time"), "Time should not be null"); + assertNotNull(logEntry.get("user"), "User should not be null"); + assertFalse( + logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(61) + void testChangelogAfterModifyingNoteAndCustomProperty() throws IOException { + System.out.println( + "Test (61): Modify note field and custom property, then verify changelog shows created + 3 updated entries in all three facets"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + for (int i = 0; i < 3; i++) { + String facetName = facet[i]; - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Update attachment with notes field (entity is already in draft mode from test 60) + String notesValue = "Test note for changelog verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + changelogEntityID[i], + changelogAttachmentID[i], + updateNotesBody); + assertEquals("Updated", updateNotesResponse, "Should successfully update notes field"); + + // Update attachment with custom property + Integer customProperty2Value = 12345; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + changelogEntityID[i], + changelogAttachmentID[i], + bodyInt); + assertEquals( + "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + // Save the entity + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Edit entity again to fetch changelog + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + // Fetch changelog after modifications + Map changelogResponse = + api.fetchChangelog( + appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + assertNotNull(changelogResponse, "Changelog response should not be null"); - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals( - // moveObjectIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all moved attachments"); - - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // String readResponse = - // api.readAttachment(appUrl, entityName, facet[i], moveTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read moved attachment from target entity"); - // } - // } + // Verify changelog content - should have 1 created + 3 updated (note, customProperty2, and + // internal update) + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + 4, + changelogResponse.get("numItems"), + "Should have 4 changelog entries (1 created + 3 updated)"); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // moveObjectIds.size(), - // sourceMetadataAfterMove.size(), - // "Source entity should still have attachments in UI when sourceFacet is not specified"); - - // for (Map metadata : sourceMetadataAfterMove) { - // String objectId = (String) metadata.get("objectId"); - // assertTrue( - // moveObjectIds.contains(objectId), - // "Source entity should still show attachment with objectId: " + objectId); - // } + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(4, changeLogs.size(), "Should have exactly 4 changelog entries"); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + // Verify first entry is 'created' + Map createdEntry = changeLogs.get(0); + assertEquals( + "created", createdEntry.get("operation"), "First entry should be 'created' operation"); + + // Verify remaining entries are 'updated' + long updatedCount = + changeLogs.stream().filter(log -> "updated".equals(log.get("operation"))).count(); + assertEquals(3, updatedCount, "Should have 3 'updated' operations"); + + // Verify that changeDetail exists in updated entries for note field + boolean hasNoteUpdate = + changeLogs.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .anyMatch( + log -> { + @SuppressWarnings("unchecked") + Map changeDetail = + (Map) log.get("changeDetail"); + return changeDetail != null + && "cmis:description".equals(changeDetail.get("field")); + }); + assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); + + // Save the entity so test 62 can edit it + String saveResponseFinal = + api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Saved", saveResponseFinal, "Entity should be saved successfully"); + } + } - // @Test - // @Order(69) - // public void testMoveAttachmentsToEntityWithDuplicateWithoutSourceFacet() throws Exception { - // System.out.println( - // "Test (69): Move attachments into existing Target Entity when duplicate exists without - // sourceFacet"); + @Test + @Order(62) + void testChangelogAfterRenamingAttachment() throws IOException { + System.out.println( + "Test (62): Rename attachment and verify changelog increases with rename entry in all three facets"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + for (int i = 0; i < 3; i++) { + String facetName = facet[i]; - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Edit entity to put it in draft mode (entity was saved at end of test 61) + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Rename the attachment + String newFileName = "renamed_sample.txt"; + String renameResponse = + api.renameAttachment( + appUrl, + entityName, + facetName, + changelogEntityID[i], + changelogAttachmentID[i], + newFileName); + assertEquals("Renamed", renameResponse, "Should successfully rename attachment"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Save entity after rename + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Saved", saveResponse, "Entity should be saved successfully after rename"); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Edit entity again and fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID[i]); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + // Fetch changelog after rename + Map changelogAfterRename = + api.fetchChangelog( + appUrl, entityName, facetName, changelogEntityID[i], changelogAttachmentID[i]); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + assertNotNull(changelogAfterRename, "Changelog response should not be null after rename"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Verify changelog has increased (rename operation adds 1 entry for cmis:name change) + // Expected: 1 created + 3 initial updates + 1 rename update = 5 total + assertEquals( + 5, changelogAfterRename.get("numItems"), "Should have 5 changelog entries after rename"); - // Map targetPostData = new HashMap<>(); - // targetPostData.put("up__ID", moveTargetEntity); - // targetPostData.put("mimeType", "application/pdf"); - // targetPostData.put("createdAt", new Date().toString()); - // targetPostData.put("createdBy", "test@test.com"); - // targetPostData.put("modifiedBy", "test@test.com"); - - // List createTargetResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // srvpath, - // targetPostData, - // files.get(0)); - // if (!createTargetResponse.get(0).equals("Attachment created")) { - // fail("Could not create duplicate attachment in target entity"); - // } + @SuppressWarnings("unchecked") + List> changeLogsAfterRename = + (List>) changelogAfterRename.get("changeLogs"); + assertEquals( + 5, changeLogsAfterRename.size(), "Should have exactly 5 changelog entries after rename"); + + // Verify updated count is 4 (3 initial + 1 from rename operation) + long updatedCountAfterRename = + changeLogsAfterRename.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .count(); + assertEquals(4, updatedCountAfterRename, "Should have 4 'updated' operations after rename"); + + // Verify filename change in changelog + boolean hasFilenameUpdate = + changeLogsAfterRename.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .anyMatch( + log -> { + @SuppressWarnings("unchecked") + Map changeDetail = + (Map) log.get("changeDetail"); + return changeDetail != null && "cmis:name".equals(changeDetail.get("field")); + }); + assertTrue(hasFilenameUpdate, "Should have an update entry for filename (cmis:name)"); + + // Cleanup - entity was saved after rename, so delete the active entity + api.deleteEntity(appUrl, entityName, changelogEntityID[i]); + } + } - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity: " + saveTargetResponse); - // } + @Test + @Order(63) + void testChangelogWithCustomPropertyEditSave() throws IOException { + System.out.println( + "Test (63): Create entity with custom property, save, edit and save again - verify changelog remains at 3 entries in all three facets"); - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // int initialTargetCount = targetMetadataBeforeMove.size(); - - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + for (int i = 0; i < 3; i++) { + String facetName = facet[i]; - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + // Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(newEntityID, "Failed to create new entity"); + assertNotEquals("Could not create entity", newEntityID); - // int nonDuplicateCount = moveObjectIds.size() - 1; - // int expectedTargetCount = initialTargetCount + nonDuplicateCount; + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + assertTrue(file.exists(), "Sample file should exist"); - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target entity should have initial attachments plus non-duplicate moved attachments"); + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // assertTrue( - // targetMetadataAfterMove.size() > initialTargetCount, - // "Target should have more attachments after move (non-duplicates added)"); + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); + + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + String attachmentID = createResponse.get(1); + + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(attachmentID, "Attachment ID should not be null"); + assertNotEquals("", attachmentID, "Attachment ID should not be empty"); + + // Add a custom property + Integer customPropertyValue = 99999; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customPropertyValue + "}", + MediaType.parse("application/json")); + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, newEntityID, attachmentID, bodyInt); + assertEquals( + "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // moveObjectIds.size(), - // sourceMetadataAfterMove.size(), - // "Source entity should still have all attachments in UI when sourceFacet is not - // specified"); + // Save the entity + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - // List sourceObjectIds = new ArrayList<>(); - // for (Map metadata : sourceMetadataAfterMove) { - // sourceObjectIds.add((String) metadata.get("objectId")); - // } - // for (String objectId : moveObjectIds) { - // assertTrue( - // sourceObjectIds.contains(objectId), - // "Source entity should still show attachment with objectId: " + objectId); - // } + // Edit entity to fetch initial changelog + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + // Fetch changelog after initial save + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - // @Test - // @Order(70) - // public void testMoveAttachmentsWithNotesAndSecondaryPropertiesWithoutSourceFacet() - // throws Exception { - // System.out.println( - // "Test (70): Move attachments with notes and secondary properties without sourceFacet"); + assertNotNull(changelogResponse, "Changelog response should not be null"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Verify changelog has 3 entries: 1 created + 2 updated (cmis:secondaryObjectTypeIds + + // customProperty2) + assertEquals( + 3, changelogResponse.get("numItems"), "Should have 3 changelog entries initially"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(3, changeLogs.size(), "Should have exactly 3 changelog entries"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Save entity again without any modifications + saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Edit entity again and fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // String notesValue = "Test note for migration verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, attachmentId, updateNotesBody); - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update notes for attachment: " + attachmentId); - // } - // } + // Fetch changelog after second save + Map changelogAfterSecondSave = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - // Integer customProperty2Value = 54321; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, attachmentId, bodyInt); - // if (!updateCustomPropertyResponse.equals("Updated")) { - // fail("Could not update custom property for attachment: " + attachmentId); - // } - // } + assertNotNull( + changelogAfterSecondSave, "Changelog response should not be null after second save"); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Verify changelog still has only 3 entries (no new entries added) + assertEquals( + 3, + changelogAfterSecondSave.get("numItems"), + "Should still have only 3 changelog entries after edit-save without modifications"); - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + @SuppressWarnings("unchecked") + List> changeLogsAfterSecondSave = + (List>) changelogAfterSecondSave.get("changeLogs"); + assertEquals( + 3, + changeLogsAfterSecondSave.size(), + "Should still have exactly 3 changelog entries after second save"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + // Clean up the entity + api.deleteEntity(appUrl, entityName, newEntityID); + } + } - // List> sourceMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + @Test + @Order(64) + void testChangelogForSavedAttachmentWithoutModification() throws IOException { + System.out.println( + "Test (64): Create entity, upload attachment, save, edit and save again - verify changelog still has only 'created' entry in all three facets"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + for (int i = 0; i < 3; i++) { + String facetName = facet[i]; - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + // Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(newEntityID, "Failed to create new entity"); + assertNotEquals("Could not create entity", newEntityID); - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // int targetCountBeforeMove = targetMetadataBeforeMove.size(); - - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + assertTrue(file.exists(), "Sample file should exist"); - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // int expectedTargetCount = targetCountBeforeMove + sourceAttachmentIds.size(); - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target entity should have " + expectedTargetCount + " attachments after move"); - - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, - // targetAttachmentId); - - // if (detailedMetadata.containsKey("note")) { - // assertEquals( - // notesValue, - // detailedMetadata.get("note"), - // "Notes should be preserved after move for attachment: " + targetAttachmentId); - // } else { - // fail("Notes property missing after move for attachment: " + targetAttachmentId); - // } + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (detailedMetadata.containsKey("customProperty2")) { - // assertEquals( - // customProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Custom property should be preserved after move for attachment: " - // + targetAttachmentId); - // } else { - // fail("Custom property missing after move for attachment: " + targetAttachmentId); - // } - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // sourceCountBeforeMove, - // sourceMetadataAfterMove.size(), - // "Source entity should still have " - // + sourceCountBeforeMove - // + " attachments (without sourceFacet)"); - - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + String newAttachmentID = createResponse.get(1); - // @Test - // @Order(71) - // public void testMoveAttachmentsWithInvalidOrUndefinedSecondaryProperties() throws Exception { - // System.out.println( - // "Test (71): Move attachments with invalid or undefined secondary properties"); + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(newAttachmentID, "Attachment ID should not be null"); + assertNotEquals("", newAttachmentID, "Attachment ID should not be empty"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Save the entity immediately without any modifications + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); - // files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + // Edit entity again without making any changes to the attachment + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Save entity again without modifying the attachment + saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Edit entity to fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // String validAttachmentId = sourceAttachmentIds.get(0); - // Integer validCustomProperty2Value = 12345; - // RequestBody validPropertyBody = - // RequestBody.create( - // "{\"customProperty2\": " + validCustomProperty2Value + "}", - // MediaType.parse("application/json")); - - // String validPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, validAttachmentId, - // validPropertyBody); - // if (!validPropertyResponse.equals("Updated")) { - // fail("Could not update valid property for attachment: " + validAttachmentId); - // } + // Fetch changelog for the attachment + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, newAttachmentID); - // String invalidAttachmentId = sourceAttachmentIds.get(1); - // RequestBody invalidPropertyBody = - // RequestBody.create( - // "{\"nonExistentProperty\": \"invalid\"}", MediaType.parse("application/json")); - - // api.updateSecondaryProperty( - // appUrl, entityName, facet[i], moveSourceEntity, invalidAttachmentId, - // invalidPropertyBody); - - // String undefinedAttachmentId = sourceAttachmentIds.get(2); - // RequestBody undefinedPropertyBody = - // RequestBody.create( - // "{\"undefinedField\": \"test\", \"anotherUndefined\": 999}", - // MediaType.parse("application/json")); - - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facet[i], - // moveSourceEntity, - // undefinedAttachmentId, - // undefinedPropertyBody); - - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + assertNotNull(changelogResponse, "Changelog response should not be null"); - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + // Verify changelog content - should only have 'created' entry even after edit and save + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + "sample.pdf", changelogResponse.get("filename"), "Filename should match uploaded file"); + assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); + assertEquals(1, changelogResponse.get("numItems"), "Should have only 1 changelog entry"); + + // Verify the changelog entry + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); + + Map logEntry = changeLogs.get(0); + assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); + assertNotNull(logEntry.get("time"), "Time should not be null"); + assertNotNull(logEntry.get("user"), "User should not be null"); + assertFalse( + logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); + + // Clean up the new entity + api.deleteEntity(appUrl, entityName, newEntityID); + } + } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + @Test + @Order(65) + void testMoveAttachmentsWithSourceFacet() throws IOException { + System.out.println( + "Test (65): Move attachments from Source Entity to Target Entity with sourceFacet"); - // List> sourceMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); - // // Save target before move - // String saveTargetBeforeMoveResponseTest72 = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponseTest72.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveResponseTest72); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity"); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveTest65 = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveTest65.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveTest65); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals( + sourceAttachmentIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all attachments after move"); + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + 0, sourceMetadataAfterMove.size(), "Source entity should have no attachments after move"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(66) + public void testMoveAttachmentsToEntityWithDuplicateWithSourceFacet() throws Exception { + System.out.println( + "Test (66): Move attachments to entity with duplicate attachment with sourceFacet"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity"); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + Map targetPostData = new HashMap<>(); + targetPostData.put("up__ID", moveTargetEntity); + targetPostData.put("mimeType", "application/pdf"); + targetPostData.put("createdAt", new Date().toString()); + targetPostData.put("createdBy", "test@test.com"); + targetPostData.put("modifiedBy", "test@test.com"); + + File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); + List targetCreateResponse = + api.createAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + srvpath, + targetPostData, + duplicateFile); + + if (!targetCreateResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment on target entity"); + } + + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } + + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + int targetCountBeforeMove = targetMetadataBeforeMove.size(); + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + + int expectedTargetCount = targetCountBeforeMove + (sourceAttachmentIds.size() - 1); + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target should have duplicate skipped, other attachments moved"); + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + int expectedSourceCount = + sourceAttachmentIds.size() - (targetMetadataAfterMove.size() - targetCountBeforeMove); + assertEquals( + expectedSourceCount, + sourceMetadataAfterMove.size(), + "Source should have duplicate attachment remaining"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(67) + public void testMoveAttachmentsWithNotesAndSecondaryProperties() throws Exception { + System.out.println( + "Test (67): Move attachments with notes and secondary properties with sourceFacet"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String notesValue = "Test note for verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + + for (String attachmentId : sourceAttachmentIds) { + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, attachmentId, updateNotesBody); + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update notes for attachment: " + attachmentId); + } + } + + Integer customProperty2Value = 54321; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + + for (String attachmentId : sourceAttachmentIds) { + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, attachmentId, bodyInt); + if (!updateCustomPropertyResponse.equals("Updated")) { + fail("Could not update custom property for attachment: " + attachmentId); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveTest67 = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveTest67.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveTest67); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals( + sourceAttachmentIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all attachments after move"); + + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, targetAttachmentId); + + if (detailedMetadata.containsKey("note")) { + assertEquals( + notesValue, + detailedMetadata.get("note"), + "Notes should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Notes property missing after move for attachment: " + targetAttachmentId); + } + + if (detailedMetadata.containsKey("customProperty2")) { + assertEquals( + customProperty2Value, + detailedMetadata.get("customProperty2"), + "Custom property should be preserved after move for attachment: " + + targetAttachmentId); + } else { + fail("Custom property missing after move for attachment: " + targetAttachmentId); + } + } + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + 0, sourceMetadataAfterMove.size(), "Source entity has no attachments after move"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(68) + public void testMoveAttachmentsWithoutSourceFacet() throws Exception { + System.out.println( + "Test (68): Move valid attachments from Source Entity to Target Entity without sourceFacet"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } else { + fail("Attachment metadata does not contain objectId"); + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } + + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals( + moveObjectIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all moved attachments"); + + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + String readResponse = + api.readAttachment(appUrl, entityName, facet[i], moveTargetEntity, targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read moved attachment from target entity"); + } + } + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + moveObjectIds.size(), + sourceMetadataAfterMove.size(), + "Source entity should still have attachments in UI when sourceFacet is not specified"); + + for (Map metadata : sourceMetadataAfterMove) { + String objectId = (String) metadata.get("objectId"); + assertTrue( + moveObjectIds.contains(objectId), + "Source entity should still show attachment with objectId: " + objectId); + } + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(69) + public void testMoveAttachmentsToEntityWithDuplicateWithoutSourceFacet() throws Exception { + System.out.println( + "Test (69): Move attachments into existing Target Entity when duplicate exists without sourceFacet"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } else { + fail("Attachment metadata does not contain objectId"); + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + Map targetPostData = new HashMap<>(); + targetPostData.put("up__ID", moveTargetEntity); + targetPostData.put("mimeType", "application/pdf"); + targetPostData.put("createdAt", new Date().toString()); + targetPostData.put("createdBy", "test@test.com"); + targetPostData.put("modifiedBy", "test@test.com"); + + List createTargetResponse = + api.createAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + srvpath, + targetPostData, + files.get(0)); + if (!createTargetResponse.get(0).equals("Attachment created")) { + fail("Could not create duplicate attachment in target entity"); + } + + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity: " + saveTargetResponse); + } + + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + int initialTargetCount = targetMetadataBeforeMove.size(); + + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + + int nonDuplicateCount = moveObjectIds.size() - 1; + int expectedTargetCount = initialTargetCount + nonDuplicateCount; + + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target entity should have initial attachments plus non-duplicate moved attachments"); + + assertTrue( + targetMetadataAfterMove.size() > initialTargetCount, + "Target should have more attachments after move (non-duplicates added)"); + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + moveObjectIds.size(), + sourceMetadataAfterMove.size(), + "Source entity should still have all attachments in UI when sourceFacet is not specified"); + + List sourceObjectIds = new ArrayList<>(); + for (Map metadata : sourceMetadataAfterMove) { + sourceObjectIds.add((String) metadata.get("objectId")); + } + for (String objectId : moveObjectIds) { + assertTrue( + sourceObjectIds.contains(objectId), + "Source entity should still show attachment with objectId: " + objectId); + } + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(70) + public void testMoveAttachmentsWithNotesAndSecondaryPropertiesWithoutSourceFacet() + throws Exception { + System.out.println( + "Test (70): Move attachments with notes and secondary properties without sourceFacet"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String notesValue = "Test note for migration verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + + for (String attachmentId : sourceAttachmentIds) { + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, attachmentId, updateNotesBody); + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update notes for attachment: " + attachmentId); + } + } + + Integer customProperty2Value = 54321; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + + for (String attachmentId : sourceAttachmentIds) { + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, attachmentId, bodyInt); + if (!updateCustomPropertyResponse.equals("Updated")) { + fail("Could not update custom property for attachment: " + attachmentId); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } + + List> sourceMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } + + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + int targetCountBeforeMove = targetMetadataBeforeMove.size(); + + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + int expectedTargetCount = targetCountBeforeMove + sourceAttachmentIds.size(); + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target entity should have " + expectedTargetCount + " attachments after move"); + + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, targetAttachmentId); + + if (detailedMetadata.containsKey("note")) { + assertEquals( + notesValue, + detailedMetadata.get("note"), + "Notes should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Notes property missing after move for attachment: " + targetAttachmentId); + } + + if (detailedMetadata.containsKey("customProperty2")) { + assertEquals( + customProperty2Value, + detailedMetadata.get("customProperty2"), + "Custom property should be preserved after move for attachment: " + + targetAttachmentId); + } else { + fail("Custom property missing after move for attachment: " + targetAttachmentId); + } + } + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + sourceCountBeforeMove, + sourceMetadataAfterMove.size(), + "Source entity should still have " + + sourceCountBeforeMove + + " attachments (without sourceFacet)"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(71) + public void testMoveAttachmentsWithInvalidOrUndefinedSecondaryProperties() throws Exception { + System.out.println( + "Test (71): Move attachments with invalid or undefined secondary properties"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String validAttachmentId = sourceAttachmentIds.get(0); + Integer validCustomProperty2Value = 12345; + RequestBody validPropertyBody = + RequestBody.create( + "{\"customProperty2\": " + validCustomProperty2Value + "}", + MediaType.parse("application/json")); + + String validPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, validAttachmentId, validPropertyBody); + if (!validPropertyResponse.equals("Updated")) { + fail("Could not update valid property for attachment: " + validAttachmentId); + } + + String invalidAttachmentId = sourceAttachmentIds.get(1); + RequestBody invalidPropertyBody = + RequestBody.create( + "{\"nonExistentProperty\": \"invalid\"}", MediaType.parse("application/json")); + + api.updateSecondaryProperty( + appUrl, entityName, facet[i], moveSourceEntity, invalidAttachmentId, invalidPropertyBody); + + String undefinedAttachmentId = sourceAttachmentIds.get(2); + RequestBody undefinedPropertyBody = + RequestBody.create( + "{\"undefinedField\": \"test\", \"anotherUndefined\": 999}", + MediaType.parse("application/json")); + + api.updateSecondaryProperty( + appUrl, + entityName, + facet[i], + moveSourceEntity, + undefinedAttachmentId, + undefinedPropertyBody); + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } + + List> sourceMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveResponseTest72 = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponseTest72.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveResponseTest72); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + + assertTrue( + targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); + assertEquals( + sourceCountBeforeMove, + targetMetadataAfterMove.size(), + "All attachments should move (invalid properties are ignored)"); + + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, targetAttachmentId); + + if (detailedMetadata.containsKey("customProperty2") + && detailedMetadata.get("customProperty2") != null) { + assertEquals( + validCustomProperty2Value, + detailedMetadata.get("customProperty2"), + "Valid customProperty2 should be preserved"); + } + } + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterMove.size(), + "Source entity should have no attachments after move with sourceFacet"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(72) + public void testMoveAttachmentsFromSourceEntityInDraftMode() throws Exception { + System.out.println( + "Test (72): Move attachments from Source Entity when Source Entity is in draft mode"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + int sourceCountBeforeMove = sourceAttachmentIds.size(); + assertTrue(sourceCountBeforeMove > 0, "Source entity should have attachments before move"); + assertEquals( + files.size(), + sourceCountBeforeMove, + "Source should have " + files.size() + " attachments"); + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } + + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + + String editSourceResponse = + api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!editSourceResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity back to draft mode"); + } + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity: " + saveTargetResponse); + } + + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertTrue( + targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); + assertEquals( + sourceCountBeforeMove, + targetMetadataAfterMove.size(), + "Target should have " + sourceCountBeforeMove + " attachments after move"); + + Set targetFileNames = + targetMetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); + + for (File file : files) { + assertTrue( + targetFileNames.contains(file.getName()), + "Target should contain attachment: " + file.getName()); + } + + String saveSourceAfterMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceAfterMoveResponse.equals("Saved")) { + fail("Could not save source entity after move: " + saveSourceAfterMoveResponse); + } + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + sourceCountBeforeMove, + sourceMetadataAfterMove.size(), + "Source entity in draft mode retains attachments after move (copy behavior)"); + + Set sourceFileNamesAfterMove = + sourceMetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); + + for (File file : files) { + assertTrue( + sourceFileNamesAfterMove.contains(file.getName()), + "Source (draft) should still contain attachment: " + file.getName()); + } + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(73) + public void testEditAttachmentFileNameAndMoveToTarget() throws Exception { + System.out.println( + "Test (73): Edit attachment file name in Source Entity and move it to Target Entity"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + File originalFile = new File(classLoader.getResource("sample.txt").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, originalFile); + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment in source entity"); + } + + String attachmentId = createResponse.get(1); + assertNotNull(attachmentId, "Attachment ID should not be null"); + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + List> metadataBeforeRename = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals(1, metadataBeforeRename.size(), "Source should have 1 attachment"); + assertEquals( + "sample.txt", + metadataBeforeRename.get(0).get("fileName"), + "Original filename should be sample.txt"); + + String editSourceResponse = + api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!editSourceResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity to draft mode"); + } + + String newFileName = "testEdited.txt"; + String renameResponse = + api.renameAttachment( + appUrl, entityName, facet[i], moveSourceEntity, attachmentId, newFileName); + assertEquals("Renamed", renameResponse, "Attachment should be renamed successfully"); + + saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity after rename: " + saveSourceResponse); + } + + List> metadataAfterRename = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals(1, metadataAfterRename.size(), "Source should still have 1 attachment"); + assertEquals( + newFileName, + metadataAfterRename.get(0).get("fileName"), + "Filename should be updated to " + newFileName); + + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + String objectId = metadata.get("objectId").toString(); + moveSourceFolderId = metadata.get("folderId").toString(); + assertNotNull(objectId, "Object ID should not be null"); + assertNotNull(moveSourceFolderId, "Folder ID should not be null"); + + moveObjectIds = new ArrayList<>(); + moveObjectIds.add(objectId); + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Save target before move + String saveTargetBeforeMoveResponseTest73 = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponseTest73.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveResponseTest73); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals(1, targetMetadataAfterMove.size(), "Target should have 1 attachment after move"); + assertEquals( + newFileName, + targetMetadataAfterMove.get(0).get("fileName"), + "Target should have attachment with renamed filename: " + newFileName); + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterMove.size(), + "Source entity should have no attachments after move with sourceFacet"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(74) + public void testChainMoveAttachmentsFromSourceToTarget1ToTarget2() throws Exception { + System.out.println( + "Test (74): Move attachments from Source Entity to Target Entity 1 and then to Target Entity 2"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + int sourceCountInitial = sourceAttachmentIds.size(); + assertTrue(sourceCountInitial > 0, "Source should have attachments"); + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } + + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity 1"); + } + + // Save target1 before move + String saveTarget1BeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTarget1BeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity 1 before move"); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult1 = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult1 == null) { + fail("Move operation from source to target 1 returned null result"); + } + + List> target1MetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertTrue( + target1MetadataAfterMove.size() > 0, + "Target entity 1 should have attachments after move"); + assertEquals( + sourceCountInitial, + target1MetadataAfterMove.size(), + "Target 1 should have " + sourceCountInitial + " attachments"); + + Set target1FileNames = + target1MetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); + + for (File file : files) { + assertTrue( + target1FileNames.contains(file.getName()), + "Target 1 should contain attachment: " + file.getName()); + } + + List> sourceMetadataAfterFirstMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterFirstMove.size(), + "Source entity should have no attachments after move to target 1"); + + String moveTargetEntity2 = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity2.equals("Could not create entity")) { + fail("Could not create target entity 2"); + } + + // Save target2 before move + String saveTarget2BeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity2); + if (!saveTarget2BeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity 2 before move"); + } + + List target1AttachmentIds = new ArrayList<>(); + for (Map metadata : target1MetadataAfterMove) { + String attachmentId = metadata.get("ID").toString(); + target1AttachmentIds.add(attachmentId); + } + + moveObjectIds = new ArrayList<>(); + String target1FolderId = null; + for (String attachmentId : target1AttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (target1FolderId == null && metadata.containsKey("folderId")) { + target1FolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata from target 1: " + e.getMessage()); + } + } + + assertNotNull(target1FolderId, "Target 1 folder ID should not be null"); + + Map moveResult2 = + api.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity2, + target1FolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult2 == null) { + fail("Move operation from target 1 to target 2 returned null result"); + } + + List> target2MetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity2); + assertTrue( + target2MetadataAfterMove.size() > 0, + "Target entity 2 should have attachments after move"); + assertEquals( + sourceCountInitial, + target2MetadataAfterMove.size(), + "Target 2 should have " + sourceCountInitial + " attachments"); + + Set target2FileNames = + target2MetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); + + for (File file : files) { + assertTrue( + target2FileNames.contains(file.getName()), + "Target 2 should contain attachment: " + file.getName()); + } + + List> target1MetadataAfterSecondMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals( + 0, + target1MetadataAfterSecondMove.size(), + "Target entity 1 should have no attachments after move to target 2"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity2); + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(75) + public void testMoveAttachmentsWithoutSDMRole() throws Exception { + System.out.println("Test (75): Move attachments when user does not have SDM Role"); + + for (int i = 0; i < facet.length; i++) { + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + int sourceCountInitial = sourceAttachmentIds.size(); + assertTrue(sourceCountInitial > 0, "Source should have attachments"); + + moveObjectIds = new ArrayList<>(); + moveSourceFolderId = null; + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } + + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + + moveTargetEntity = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity with no SDM role"); + } + + // Save target before move + String saveTargetBeforeMoveResponse = + apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } + + String sourceFacet = serviceName + "." + entityName + "." + facet[i]; + String targetFacet = serviceName + "." + entityName + "." + facet[i]; + Map moveResult = null; + boolean moveOperationFailed = false; + String errorMessage = null; + + try { + moveResult = + apiNoRoles.moveAttachment( + appUrl, + entityName, + facet[i], + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + moveOperationFailed = true; + errorMessage = "Move operation returned null"; + } else if (moveResult.containsKey("error")) { + moveOperationFailed = true; + errorMessage = moveResult.get("error").toString(); + } + } catch (Exception e) { + moveOperationFailed = true; + errorMessage = e.getMessage(); + } + + assertTrue( + moveOperationFailed, "Move operation should fail when user does not have SDM role"); + assertNotNull(errorMessage, "Error message should be present when move operation fails"); + System.out.println("Move operation failed as expected. Error: " + errorMessage); + + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); + assertEquals( + sourceCountInitial, + sourceMetadataAfterMove.size(), + "Source should still have all attachments after failed move"); + + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); + assertEquals( + 0, targetMetadataAfterMove.size(), "Target should have no attachments after failed move"); + + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } + } + + @Test + @Order(76) + void testRenameAttachmentWithExtensionChange() throws IOException { + System.out.println( + "Test (76) : Rename attachment changing extension from .pdf to .txt across all facets - should return extension change warning"); + + // Step 1: Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (newEntityID.equals("Could not create entity")) { + fail("Could not create entity"); + } + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!saveResponse.equals("Saved")) { + fail("Could not save new entity: " + saveResponse); + } + + // Step 2: Upload a PDF attachment to each facet + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!"Entity in draft mode".equals(editResponse)) { + fail("Could not put entity in draft mode for PDF upload"); + } + + String[] facetAttachmentIDs = new String[facet.length]; + for (int i = 0; i < facet.length; i++) { + facetAttachmentIDs[i] = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], newEntityID, postData, file); + if (facetAttachmentIDs[i] == null) { + api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not upload sample.pdf to facet: " + facet[i]); + } + } + + // Step 3: Save the entity + String savedAfterUpload = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!savedAfterUpload.equals("Saved")) { + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not save entity after PDF upload: " + savedAfterUpload); + } + + // Step 4 & 5: Edit the entity, rename each facet's attachment changing extension .pdf -> .txt + for (int i = 0; i < facet.length; i++) { + String editDraftResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!"Entity in draft mode".equals(editDraftResponse)) { + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not put entity in draft mode for rename on facet: " + facet[i]); + } + + String renameResponse = + api.renameAttachment( + appUrl, + entityName, + facet[i], + newEntityID, + facetAttachmentIDs[i], + "renamed_document.txt"); + if (!"Renamed".equals(renameResponse)) { + api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not rename attachment on facet " + facet[i] + ": " + renameResponse); + } + + // Step 6: Save and validate the extension change warning message + String saveWithWarningResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertNotNull(saveWithWarningResponse, "Response should not be null for facet: " + facet[i]); + + String expectedMessage = + "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; + + com.fasterxml.jackson.databind.JsonNode messagesNode = + new ObjectMapper().readTree(saveWithWarningResponse); + assertTrue( + messagesNode.isArray(), + "sap-messages response should be a JSON array for facet: " + facet[i]); + + boolean foundExtensionError = false; + for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { + if (messageNode.has("message")) { + String message = messageNode.get("message").asText(); + if (message.contains("Changing the file extension is not allowed")) { + foundExtensionError = true; + assertEquals( + expectedMessage, + message, + "Extension change error message does not match for facet: " + facet[i]); + break; + } + } + } + + assertTrue( + foundExtensionError, + "Expected extension change warning not found for facet: " + + facet[i] + + ". Full response: " + + saveWithWarningResponse); + } + + // Clean up + api.deleteEntity(appUrl, entityName, newEntityID); + } + + @Test + @Order(77) + void testRenameAttachmentWithExtensionChange_BeforeSave() throws IOException { + System.out.println( + "Test (77) : Upload attachment in draft, rename changing extension before save across all facets - should return extension change warning"); + + for (int i = 0; i < facet.length; i++) { + // Step 1: Create a new entity draft (do NOT save it yet) + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (newEntityID.equals("Could not create entity")) { + fail("Could not create entity for facet: " + facet[i]); + } + + // Step 2: Upload a PDF attachment while entity is still in draft (unsaved) + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + String facetAttachmentID = + CreateandReturnFacetID( + appUrl, serviceName, entityName, facet[i], newEntityID, postData, file); + if (facetAttachmentID == null) { + api.deleteEntityDraft(appUrl, entityName, newEntityID); + fail("Could not upload sample.pdf to facet: " + facet[i]); + } + + // Step 3: Rename the attachment changing extension from .pdf to .txt — entity still not saved + String renameResponse = + api.renameAttachment( + appUrl, entityName, facet[i], newEntityID, facetAttachmentID, "renamed_document.txt"); + if (!"Renamed".equals(renameResponse)) { + api.deleteEntityDraft(appUrl, entityName, newEntityID); + fail("Could not rename attachment on facet " + facet[i] + ": " + renameResponse); + } + + // Step 4: Save — should receive extension change warning, not "Saved" + String saveWithWarningResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertNotNull(saveWithWarningResponse, "Response should not be null for facet: " + facet[i]); + + String expectedMessage = + "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; + + com.fasterxml.jackson.databind.JsonNode messagesNode = + new ObjectMapper().readTree(saveWithWarningResponse); + assertTrue( + messagesNode.isArray(), + "sap-messages response should be a JSON array for facet: " + facet[i]); + + boolean foundExtensionError = false; + for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { + if (messageNode.has("message")) { + String message = messageNode.get("message").asText(); + if (message.contains("Changing the file extension is not allowed")) { + foundExtensionError = true; + assertEquals( + expectedMessage, + message, + "Extension change error message does not match for facet: " + facet[i]); + break; + } + } + } + + assertTrue( + foundExtensionError, + "Expected extension change warning not found for facet: " + + facet[i] + + ". Full response: " + + saveWithWarningResponse); + + // Clean up + api.deleteEntity(appUrl, entityName, newEntityID); + } + } + + @Test + @Order(78) + void testReadCmisMetadataCreatedBy() throws IOException { + System.out.println("Test (78) : Read CMIS metadata and verify createdBy field"); + + // Create a self-contained entity with an attachment + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + // Now verify the CMIS createdBy property + String createdBy = + CmisDocumentHelper.getCmisProperty(testEntityID, "sample.pdf", "cmis:createdBy"); + System.out.println("cmis:createdBy value: " + createdBy); + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); + } else { + assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); + assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); + } + } + + @Test + @Order(79) + void testUploadVirusFileInScanDisabledRepo() throws IOException { + System.out.println( + "Test (79) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); + + for (int i = 0; i < facet.length; i++) { + boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity for facet: " + facet[i]); + } + String testEntityID = response; + + // Use EICAR test virus file + String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); + File file = new File(eicarFilePath); + if (!file.exists()) { + fail("EICAR virus test file not found at: " + file.getAbsolutePath()); + } + + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facet[i], testEntityID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + String testAttachmentID = createResponse.get(1); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (response.equals("Saved")) { + // Verify attachment is readable (upload succeeded despite being a virus file) + response = + api.readAttachment(appUrl, entityName, facet[i], testEntityID, testAttachmentID); + if (response.equals("OK")) { + testStatus = true; + } + } + } + + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + + if (!testStatus) { + fail( + "Virus file upload should succeed in a virus scan disabled repository for facet: " + + facet[i]); + } + } + } + + @Test + @Order(80) + void testDownloadAttachmentsAcrossMultipleFacets() throws IOException { + System.out.println( + "Test (76): Create entity, upload attachments to each facet, and verify" + + " download works across all facets"); + + // Step 1: Create entity + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String downloadTestEntityID = response; + + ClassLoader classLoader = getClass().getClassLoader(); + Map postData = new HashMap<>(); + postData.put("up__ID", downloadTestEntityID); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + postData.put("mimeType", "application/pdf"); + + // Step 2: Upload one pdf attachment to each of the 3 facets + File originalPdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + String[] facetAttachmentIDs = new String[facet.length]; + for (int i = 0; i < facet.length; i++) { + File tempFacetFile = File.createTempFile("sample_mf_facet" + i + "_", ".pdf"); + Files.copy( + originalPdfFile.toPath(), tempFacetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + List createResp = + api.createAttachment( + appUrl, entityName, facet[i], downloadTestEntityID, srvpath, postData, tempFacetFile); + tempFacetFile.delete(); + if (!createResp.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not upload pdf to facet: " + facet[i]); + return; + } + facetAttachmentIDs[i] = createResp.get(1); + } + + // Step 3: Save entity draft + response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not save entity draft: " + response); + return; + } + + // Step 4: Verify download works from each facet independently + for (int i = 0; i < facet.length; i++) { + String downloadResult = + api.downloadSelectedAttachments( + appUrl, entityName, facet[i], downloadTestEntityID, List.of(facetAttachmentIDs[i])); + JSONArray resultArray = new JSONArray(downloadResult); + assertEquals(1, resultArray.length(), "Expected 1 result from facet: " + facet[i]); + JSONObject result = resultArray.getJSONObject(0); + assertEquals( + "success", result.getString("status"), "Download should succeed for facet: " + facet[i]); + assertTrue( + result.has("content"), + "Downloaded attachment in facet " + facet[i] + " should have a content field"); + } + + // Step 5: Edit entity back to draft, upload 2 more attachments to facet[0], save, and verify + // multi-download + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); + if (!editResponse.equals("Entity in draft mode")) { + api.deleteEntity(appUrl, entityName, downloadTestEntityID); + fail("Could not edit entity back to draft mode: " + editResponse); + return; + } + + List multiIDs = new ArrayList<>(); + multiIDs.add(facetAttachmentIDs[0]); + File[] extraFiles = { + new File(classLoader.getResource("sample1.pdf").getFile()), + new File(classLoader.getResource("sample2.pdf").getFile()) + }; + for (int i = 0; i < 2; i++) { + List extraResp = + api.createAttachment( + appUrl, entityName, facet[0], downloadTestEntityID, srvpath, postData, extraFiles[i]); + if (!extraResp.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not upload extra attachment to facet: " + facet[0]); + return; + } + multiIDs.add(extraResp.get(1)); + } + + response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not save entity draft after extra uploads: " + response); + return; + } + + String multiResult = + api.downloadSelectedAttachments( + appUrl, entityName, facet[0], downloadTestEntityID, multiIDs); + JSONArray multiArray = new JSONArray(multiResult); + assertEquals(3, multiArray.length(), "Expected 3 results from multi-download in " + facet[0]); + for (int i = 0; i < multiArray.length(); i++) { + assertEquals( + "success", + multiArray.getJSONObject(i).getString("status"), + "Attachment " + (i + 1) + " in " + facet[0] + " should download successfully"); + } + + // Clean up + api.deleteEntity(appUrl, entityName, downloadTestEntityID); + } + + @Test + @Order(81) + void testDownloadButtonDisabledWhenLinkSelectedAcrossFacets() throws IOException { + System.out.println( + "Test (77): Upload pdf to one facet and link to another; verify download" + + " button enabled for pdf facet only, disabled when link-facet item selected"); + + // Step 1: Create entity + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String testEntityID = response; + + ClassLoader classLoader = getClass().getClassLoader(); + + // Step 2: Upload pdf to facet[0] (attachments) + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List createResponse = + api.createAttachment( + appUrl, entityName, facet[0], testEntityID, srvpath, postData, pdfFile); + if (!createResponse.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not upload pdf to facet: " + facet[0]); + return; + } + String pdfAttachmentID = createResponse.get(1); + + // Step 3: Create a link in facet[1] (references) + String linkResponse = + api.createLink( + appUrl, entityName, facet[1], testEntityID, "TestLink", "https://www.example.com"); + if (!linkResponse.equals("Link created successfully")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not create link in facet: " + facet[1]); + return; + } + + // Step 4: Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not save entity draft: " + response); + return; + } + + // Fetch link attachment ID from facet[1] metadata + List> facet1Attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[1], testEntityID); + String linkAttachmentID = + facet1Attachments.stream() + .filter( + a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) + .map(a -> (String) a.get("ID")) + .findFirst() + .orElse(null); + if (linkAttachmentID == null) { + api.deleteEntity(appUrl, entityName, testEntityID); + fail("Could not find link attachment in facet: " + facet[1]); + return; + } + + // Step 5: Download pdf from facet[0] - Download button should be enabled + String pdfOnlyResult = + api.downloadSelectedAttachments( + appUrl, entityName, facet[0], testEntityID, List.of(pdfAttachmentID)); + JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); + assertEquals(1, pdfOnlyArray.length(), "Expected 1 result for pdf download from " + facet[0]); + assertEquals( + "success", + pdfOnlyArray.getJSONObject(0).getString("status"), + "Download button should be enabled: pdf in " + facet[0] + " should download successfully"); + + // Step 6: Attempt to download link from facet[1] - Download button should be disabled + String linkResult = + api.downloadSelectedAttachments( + appUrl, entityName, facet[1], testEntityID, List.of(linkAttachmentID)); + JSONArray linkArray = new JSONArray(linkResult); + assertEquals( + 1, linkArray.length(), "Expected 1 result for link download attempt from " + facet[1]); + JSONObject linkItem = linkArray.getJSONObject(0); + assertEquals( + "error", + linkItem.getString("status"), + "Download button should be disabled: link in " + facet[1] + " should return error"); + assertEquals( + "Download is not supported for link attachments", + linkItem.getString("message"), + "Error message for link download in " + facet[1] + " should match"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(82) + void testDownloadAttachmentsAcrossMultipleFacetsInDraftState() throws IOException { + System.out.println( + "Test (78): Create entity in draft state, upload attachments to each" + + " facet, download before saving"); + + // Step 1: Create entity draft (do NOT save) + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String draftEntityID = response; + + ClassLoader classLoader = getClass().getClassLoader(); + Map postData = new HashMap<>(); + postData.put("up__ID", draftEntityID); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + postData.put("mimeType", "application/pdf"); + + // Step 2: Upload one pdf to each of the 3 facets (entity stays in draft state) + File origPdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + String[] draftFacetAttachmentIDs = new String[facet.length]; + for (int i = 0; i < facet.length; i++) { + File tempFacetFile = File.createTempFile("sample_mf_draft_facet" + i + "_", ".pdf"); + Files.copy(origPdfFile.toPath(), tempFacetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + List createResp = + api.createAttachment( + appUrl, entityName, facet[i], draftEntityID, srvpath, postData, tempFacetFile); + tempFacetFile.delete(); + if (!createResp.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, draftEntityID); + fail("Could not upload pdf to facet in draft state: " + facet[i]); + return; + } + draftFacetAttachmentIDs[i] = createResp.get(1); + } + + // Step 3: Verify download works from each facet in draft state + for (int i = 0; i < facet.length; i++) { + String downloadResult = + api.downloadSelectedAttachmentsDraft( + appUrl, entityName, facet[i], draftEntityID, List.of(draftFacetAttachmentIDs[i])); + JSONArray resultArray = new JSONArray(downloadResult); + assertEquals( + 1, resultArray.length(), "Expected 1 result from facet in draft state: " + facet[i]); + JSONObject result = resultArray.getJSONObject(0); + assertEquals( + "success", + result.getString("status"), + "Download should succeed in draft state for facet: " + facet[i]); + assertTrue( + result.has("content"), + "Downloaded attachment in draft facet " + facet[i] + " should have a content field"); + } + + // Clean up - entity was never saved, so delete the draft + api.deleteEntityDraft(appUrl, entityName, draftEntityID); + } + + @Test + @Order(83) + void testDownloadButtonWithPdfAndLinkAcrossFacetsInDraftState() throws IOException { + System.out.println( + "Test (79): Upload pdf to one facet and link to another, save entity, edit" + + " (draft state), verify download button enabled for pdf facet, disabled for link" + + " facet"); + + // Step 1: Create entity draft + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String testEntityID = response; + + ClassLoader classLoader = getClass().getClassLoader(); + + // Step 2: Upload pdf to facet[0] (attachments) + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + File origPdf = new File(classLoader.getResource("sample.pdf").getFile()); + File tempPdf = File.createTempFile("sample_mf_draftlink_", ".pdf"); + Files.copy(origPdf.toPath(), tempPdf.toPath(), StandardCopyOption.REPLACE_EXISTING); + List createResponse = + api.createAttachment( + appUrl, entityName, facet[0], testEntityID, srvpath, postData, tempPdf); + tempPdf.delete(); + if (!createResponse.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not upload pdf to facet: " + facet[0]); + return; + } + String pdfAttachmentID = createResponse.get(1); + + // Step 3: Create a link in facet[1] (references) + String linkResponse = + api.createLink( + appUrl, entityName, facet[1], testEntityID, "TestLink", "https://www.example.com"); + if (!linkResponse.equals("Link created successfully")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not create link in facet: " + facet[1]); + return; + } + + // Fetch link attachment ID from draft metadata of facet[1] + List> draftFacet1Attachments = + api.fetchEntityMetadataDraft(appUrl, entityName, facet[1], testEntityID); + String linkAttachmentID = + draftFacet1Attachments.stream() + .filter( + a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) + .map(a -> (String) a.get("ID")) + .findFirst() + .orElse(null); + if (linkAttachmentID == null) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not find link attachment in draft facet: " + facet[1]); + return; + } + + // Step 4: Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not save entity draft: " + response); + return; + } - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Step 5: Edit entity - puts it back into draft state + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!editResponse.equals("Entity in draft mode")) { + api.deleteEntity(appUrl, entityName, testEntityID); + fail("Could not put entity into edit/draft mode: " + editResponse); + return; + } - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - - // assertTrue( - // targetMetadataAfterMove.size() > 0, "Target entity should have attachments after - // move"); - // assertEquals( - // sourceCountBeforeMove, - // targetMetadataAfterMove.size(), - // "All attachments should move (invalid properties are ignored)"); - - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, - // targetAttachmentId); - - // if (detailedMetadata.containsKey("customProperty2") - // && detailedMetadata.get("customProperty2") != null) { - // assertEquals( - // validCustomProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Valid customProperty2 should be preserved"); - // } - // } + // Step 6: Select pdf from facet[0] in draft state - Download button should be enabled + String pdfOnlyResult = + api.downloadSelectedAttachmentsDraft( + appUrl, entityName, facet[0], testEntityID, List.of(pdfAttachmentID)); + JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); + assertEquals( + 1, + pdfOnlyArray.length(), + "Expected 1 result for pdf download from " + facet[0] + " in draft state"); + assertEquals( + "success", + pdfOnlyArray.getJSONObject(0).getString("status"), + "Download button should be enabled in draft state: pdf in " + facet[0] + " should succeed"); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterMove.size(), - // "Source entity should have no attachments after move with sourceFacet"); + // Step 7: Select link from facet[1] in draft state - Download button should be disabled + String linkOnlyResult = + api.downloadSelectedAttachmentsDraft( + appUrl, entityName, facet[1], testEntityID, List.of(linkAttachmentID)); + JSONArray linkOnlyArray = new JSONArray(linkOnlyResult); + assertEquals( + 1, + linkOnlyArray.length(), + "Expected 1 result for link download attempt from " + facet[1] + " in draft state"); + JSONObject linkItem = linkOnlyArray.getJSONObject(0); + assertEquals( + "error", + linkItem.getString("status"), + "Download button should be disabled in draft state: link in " + + facet[1] + + " should return error"); + assertEquals( + "Download is not supported for link attachments", + linkItem.getString("message"), + "Error message for link download in " + facet[1] + " in draft state should match"); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } // @Test - // @Order(72) - // public void testMoveAttachmentsFromSourceEntityInDraftMode() throws Exception { + // @Order(84) + // void testUploadAttachmentExceedingMaximumFileSize() throws IOException { // System.out.println( - // "Test (72): Move attachments from Source Entity when Source Entity is in draft mode"); + // "Test (76) : Upload attachment exceeding maximum file size in references facet"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // // Create a new entity + // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // if (response.equals("Could not create entity")) { + // fail("Could not create entity"); + // } + // String testEntityID = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); - // files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + // // Load the 150MB sample file + // ClassLoader classLoader = getClass().getClassLoader(); + // File file = new File(classLoader.getResource("sample32mb.pdf").getFile()); + // for (int i = 0; i < facet.length; i++) { // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); + // postData.put("up__ID", testEntityID); // postData.put("mimeType", "application/pdf"); // postData.put("createdAt", new Date().toString()); // postData.put("createdBy", "test@test.com"); // postData.put("modifiedBy", "test@test.com"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } - - // int sourceCountBeforeMove = sourceAttachmentIds.size(); - // assertTrue(sourceCountBeforeMove > 0, "Source entity should have attachments before move"); - // assertEquals( - // files.size(), - // sourceCountBeforeMove, - // "Source should have " + files.size() + " attachments"); - - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // List createResponse = + // api.createAttachment(appUrl, entityName, facet[i], testEntityID, srvpath, postData, + // file); + // String check = createResponse.get(0); - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } + // // Only 'references' facet has 30MB limit, others should succeed + // if (facet[i].equals("references")) { + // // The upload should fail with AttachmentSizeExceeded error + // if (!check.equals("Attachment created")) { + // try { + // JSONObject json = new JSONObject(check); + // String errorCode = json.getJSONObject("error").getString("code"); + // String errorMessage = json.getJSONObject("error").getString("message"); + // assertEquals("413", errorCode); + // assertEquals("File size exceeds the limit of 30MB.", errorMessage); + // } catch (Exception e) { + // fail("Failed to parse error response for references facet: " + e.getMessage()); // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); + // } else { + // fail("Attachment got created in references facet with file size exceeding maximum // + // limit"); + // } + // } else { + // // For attachments and footnotes, expect success + // if (!check.equals("Attachment created")) { + // fail("Attachment upload failed in " + facet[i] + " facet: " + check); // } // } + // } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } - - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); - - // String editSourceResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!editSourceResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity back to draft mode"); - // } - - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } - - // // Save target before move - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity: " + saveTargetResponse); - // } - - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } - - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertTrue( - // targetMetadataAfterMove.size() > 0, "Target entity should have attachments after - // move"); - // assertEquals( - // sourceCountBeforeMove, - // targetMetadataAfterMove.size(), - // "Target should have " + sourceCountBeforeMove + " attachments after move"); - - // Set targetFileNames = - // targetMetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // targetFileNames.contains(file.getName()), - // "Target should contain attachment: " + file.getName()); - // } - - // String saveSourceAfterMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceAfterMoveResponse.equals("Saved")) { - // fail("Could not save source entity after move: " + saveSourceAfterMoveResponse); - // } + // // delete the draft entity + // api.deleteEntityDraft(appUrl, entityName, testEntityID); + // } - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // sourceCountBeforeMove, - // sourceMetadataAfterMove.size(), - // "Source entity in draft mode retains attachments after move (copy behavior)"); - - // Set sourceFileNamesAfterMove = - // sourceMetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // sourceFileNamesAfterMove.contains(file.getName()), - // "Source (draft) should still contain attachment: " + file.getName()); - // } + @Test + @Order(85) + void testRenameToDuplicateFilename_BackendConflict_ErrorThrown() throws Exception { + System.out.println( + "Test (78) : Rename attachment to name that exists in backend — expect DI error"); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // @Test - // @Order(73) - // public void testEditAttachmentFileNameAndMoveToTarget() throws Exception { - // System.out.println( - // "Test (73): Edit attachment file name in Source Entity and move it to Target Entity"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File originalFile = new File(classLoader.getResource("sample.txt").getFile()); + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "text/plain"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, originalFile); - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment in source entity"); - // } + String conflictingName = "backend-file.pdf"; + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testEntityID); - // String attachmentId = createResponse.get(1); - // assertNotNull(attachmentId, "Attachment ID should not be null"); + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + response = + api.renameAttachment( + appUrl, entityName, facet[0], testEntityID, testAttachmentID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); - // List> metadataBeforeRename = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals(1, metadataBeforeRename.size(), "Source should have 1 attachment"); - // assertEquals( - // "sample.txt", - // metadataBeforeRename.get(0).get("fileName"), - // "Original filename should be sample.txt"); - - // String editSourceResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!editSourceResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity to draft mode"); - // } + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + assertTrue( + response.contains("already exists") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); - // String newFileName = "testEdited.txt"; - // String renameResponse = - // api.renameAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, attachmentId, newFileName); - // assertEquals("Renamed", renameResponse, "Attachment should be renamed successfully"); + api.deleteEntity(appUrl, entityName, testEntityID); + } - // saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity after rename: " + saveSourceResponse); - // } + @Test + @Order(86) + void testUploadDuplicateAttachment_DIError_RemovedFromDrafts() throws IOException { + System.out.println( + "Test (79) : Upload duplicate attachment — expect DI error and removed from drafts"); - // List> metadataAfterRename = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals(1, metadataAfterRename.size(), "Source should still have 1 attachment"); - // assertEquals( - // newFileName, - // metadataAfterRename.get(0).get("fileName"), - // "Filename should be updated to " + newFileName); - - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // String objectId = metadata.get("objectId").toString(); - // moveSourceFolderId = metadata.get("folderId").toString(); - // assertNotNull(objectId, "Object ID should not be null"); - // assertNotNull(moveSourceFolderId, "Folder ID should not be null"); - - // moveObjectIds = new ArrayList<>(); - // moveObjectIds.add(objectId); - - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Save target before move - // String saveTargetBeforeMoveResponseTest73 = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponseTest73.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveResponseTest73); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals(1, targetMetadataAfterMove.size(), "Target should have 1 attachment after - // move"); - // assertEquals( - // newFileName, - // targetMetadataAfterMove.get(0).get("fileName"), - // "Target should have attachment with renamed filename: " + newFileName); - - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterMove.size(), - // "Source entity should have no attachments after move with sourceFacet"); - - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "First upload should succeed"); - // @Test - // @Order(74) - // public void testChainMoveAttachmentsFromSourceToTarget1ToTarget2() throws Exception { - // System.out.println( - // "Test (74): Move attachments from Source Entity to Target Entity 1 and then to Target - // Entity 2"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + List duplicateResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + String errorResponse = duplicateResponse.get(0); + assertNotEquals("Attachment created", errorResponse, "Duplicate upload should fail"); + assertTrue( + errorResponse.contains("already exists") || errorResponse.contains("error"), + "Error should contain DI message. Actual: " + errorResponse); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + List> draftAttachments = + api.fetchEntityMetadataDraft(appUrl, entityName, facet[0], testEntityID); + assertEquals(1, draftAttachments.size(), "Only original attachment should remain in drafts"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + api.deleteEntity(appUrl, entityName, testEntityID); + } - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + @Test + @Order(87) + void testReadAttachment_DeletedFromBackend_NotAvailable() throws IOException { + System.out.println( + "Test (80) : Read attachment after backend deletion — verify app handles gracefully"); - // int sourceCountInitial = sourceAttachmentIds.size(); - // assertTrue(sourceCountInitial > 0, "Source should have attachments"); - - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity 1"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // // Save target1 before move - // String saveTarget1BeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTarget1BeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity 1 before move"); - // } + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult1 = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult1 == null) { - // fail("Move operation from source to target 1 returned null result"); - // } + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, "sample.pdf"); - // List> target1MetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertTrue( - // target1MetadataAfterMove.size() > 0, - // "Target entity 1 should have attachments after move"); - // assertEquals( - // sourceCountInitial, - // target1MetadataAfterMove.size(), - // "Target 1 should have " + sourceCountInitial + " attachments"); - - // Set target1FileNames = - // target1MetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // target1FileNames.contains(file.getName()), - // "Target 1 should contain attachment: " + file.getName()); - // } + response = api.readAttachment(appUrl, entityName, facet[0], testEntityID, testAttachmentID); + System.out.println(" Read response after backend deletion: " + response); - // List> sourceMetadataAfterFirstMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterFirstMove.size(), - // "Source entity should have no attachments after move to target 1"); + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertEquals(1, attachments.size(), "App should still show the attachment in its metadata"); - // String moveTargetEntity2 = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity2.equals("Could not create entity")) { - // fail("Could not create target entity 2"); - // } + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Save target2 before move - // String saveTarget2BeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity2); - // if (!saveTarget2BeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity 2 before move"); - // } + @Test + @Order(88) + void testDeleteAttachment_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println("Test (81) : Delete attachment not in repository — expect removed from UI"); - // List target1AttachmentIds = new ArrayList<>(); - // for (Map metadata : target1MetadataAfterMove) { - // String attachmentId = metadata.get("ID").toString(); - // target1AttachmentIds.add(attachmentId); - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // moveObjectIds = new ArrayList<>(); - // String target1FolderId = null; - // for (String attachmentId : target1AttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveTargetEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (target1FolderId == null && metadata.containsKey("folderId")) { - // target1FolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata from target 1: " + e.getMessage()); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertNotNull(target1FolderId, "Target 1 folder ID should not be null"); - - // Map moveResult2 = - // api.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity2, - // target1FolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult2 == null) { - // fail("Move operation from target 1 to target 2 returned null result"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // List> target2MetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity2); - // assertTrue( - // target2MetadataAfterMove.size() > 0, - // "Target entity 2 should have attachments after move"); - // assertEquals( - // sourceCountInitial, - // target2MetadataAfterMove.size(), - // "Target 2 should have " + sourceCountInitial + " attachments"); - - // Set target2FileNames = - // target2MetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // target2FileNames.contains(file.getName()), - // "Target 2 should contain attachment: " + file.getName()); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // List> target1MetadataAfterSecondMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals( - // 0, - // target1MetadataAfterSecondMove.size(), - // "Target entity 1 should have no attachments after move to target 2"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // api.deleteEntity(appUrl, entityName, moveTargetEntity2); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, "sample.pdf"); - // @Test - // @Order(75) - // public void testMoveAttachmentsWithoutSDMRole() throws Exception { - // System.out.println("Test (75): Move attachments when user does not have SDM Role"); + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // for (int i = 0; i < facet.length; i++) { - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + response = api.deleteAttachment(appUrl, entityName, facet[0], testEntityID, testAttachmentID); + assertEquals("Deleted", response, "Delete should succeed even if not in repo"); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after delete"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertEquals(0, attachments.size(), "No attachments should remain after deletion"); - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facet[i], moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + api.deleteEntity(appUrl, entityName, testEntityID); + } - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + @Test + @Order(89) + void testDeleteEntity_FolderAndContentDeletedFromRepository() throws Exception { + System.out.println( + "Test (82) : Delete entity — expect attachments no longer accessible via app after delete"); - // int sourceCountInitial = sourceAttachmentIds.size(); - // assertTrue(sourceCountInitial > 0, "Source should have attachments"); - - // moveObjectIds = new ArrayList<>(); - // moveSourceFolderId = null; - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facet[i], moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // moveTargetEntity = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity with no SDM role"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - // // Save target before move - // String saveTargetBeforeMoveResponse = - // apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // String sourceFacet = serviceName + "." + entityName + "." + facet[i]; - // String targetFacet = serviceName + "." + entityName + "." + facet[i]; - // Map moveResult = null; - // boolean moveOperationFailed = false; - // String errorMessage = null; - - // try { - // moveResult = - // apiNoRoles.moveAttachment( - // appUrl, - // entityName, - // facet[i], - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // moveOperationFailed = true; - // errorMessage = "Move operation returned null"; - // } else if (moveResult.containsKey("error")) { - // moveOperationFailed = true; - // errorMessage = moveResult.get("error").toString(); - // } - // } catch (Exception e) { - // moveOperationFailed = true; - // errorMessage = e.getMessage(); - // } + // Verify attachment is accessible before deletion + List> attachmentsBeforeDelete = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertTrue(attachmentsBeforeDelete.size() > 0, "Entity should have attachments before delete"); - // assertTrue( - // moveOperationFailed, "Move operation should fail when user does not have SDM role"); - // assertNotNull(errorMessage, "Error message should be present when move operation fails"); - // System.out.println("Move operation failed as expected. Error: " + errorMessage); - - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveSourceEntity); - // assertEquals( - // sourceCountInitial, - // sourceMetadataAfterMove.size(), - // "Source should still have all attachments after failed move"); - - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facet[i], moveTargetEntity); - // assertEquals( - // 0, targetMetadataAfterMove.size(), "Target should have no attachments after failed - // move"); - - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } - // } + response = api.deleteEntity(appUrl, entityName, testEntityID); + assertEquals("Entity Deleted", response, "Entity deletion should succeed"); - @Test - @Order(76) - void testReadCmisMetadataCreatedBy() { - System.out.println("Test (76) : Read CMIS metadata and verify createdBy field"); - String createdBy = CmisDocumentHelper.getCmisProperty(entityID, "sample.pdf", "cmis:createdBy"); - System.out.println("cmis:createdBy value: " + createdBy); - String tokenFlowFlag = System.getProperty("tokenFlow"); - if ("namedUser".equals(tokenFlowFlag)) { - assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); - } else { - assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); - assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); - } + // Verify attachments are no longer accessible via the app after entity deletion + List> attachmentsAfterDelete = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertEquals( + 0, + attachmentsAfterDelete.size(), + "Entity should have no attachments after entity deletion"); } @Test - @Order(77) - void testUploadVirusFileInScanDisabledRepo() throws IOException { - System.out.println( - "Test (77) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); + @Order(90) + void testDiscardDraft_AttachmentsAndFolderDeletedFromDI() throws Exception { + System.out.println("Test (83) : Discard draft — expect attachments and folder deleted from DI"); - for (int i = 0; i < facet.length; i++) { - boolean testStatus = false; - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity for facet: " + facet[i]); - } - String testEntityID = response; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // Use EICAR test virus file - String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); - File file = new File(eicarFilePath); - if (!file.exists()) { - fail("EICAR virus test file not found at: " + file.getAbsolutePath()); - } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); - postData.put("mimeType", "text/plain"); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - List createResponse = - api.createAttachment(appUrl, entityName, facet[i], testEntityID, srvpath, postData, file); - String check = createResponse.get(0); - if (check.equals("Attachment created")) { - String testAttachmentID = createResponse.get(1); - response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (response.equals("Saved")) { - // Verify attachment is readable (upload succeeded despite being a virus file) - response = - api.readAttachment(appUrl, entityName, facet[i], testEntityID, testAttachmentID); - if (response.equals("OK")) { - testStatus = true; - } - } - } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - // Clean up - api.deleteEntity(appUrl, entityName, testEntityID); + response = api.deleteEntityDraft(appUrl, entityName, testEntityID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); - if (!testStatus) { - fail( - "Virus file upload should succeed in a virus scan disabled repository for facet: " - + facet[i]); - } - } + List> attachmentsAfterDiscard = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertEquals( + 0, + attachmentsAfterDiscard.size(), + "Entity should have no attachments after discarding draft"); } @Test - @Order(76) - void testRenameAttachmentWithExtensionChange() throws IOException { - System.out.println( - "Test (76) : Rename attachment changing extension from .pdf to .txt across all facets - should return extension change warning"); + @Order(91) + void testDeleteAllAttachments_FolderDeletedFromDI() throws Exception { + System.out.println("Test (84) : Delete all attachments — expect folder deleted from DI"); - // Step 1: Create a new entity - String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (newEntityID.equals("Could not create entity")) { - fail("Could not create entity"); - } - String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!saveResponse.equals("Saved")) { - fail("Could not save new entity: " + saveResponse); - } + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // Step 2: Upload a PDF attachment to each facet ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", newEntityID); + postData.put("up__ID", testEntityID); postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!"Entity in draft mode".equals(editResponse)) { - fail("Could not put entity in draft mode for PDF upload"); - } - - String[] facetAttachmentIDs = new String[facet.length]; - for (int i = 0; i < facet.length; i++) { - facetAttachmentIDs[i] = - CreateandReturnFacetID( - appUrl, serviceName, entityName, facet[i], newEntityID, postData, file); - if (facetAttachmentIDs[i] == null) { - api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not upload sample.pdf to facet: " + facet[i]); - } - } - - // Step 3: Save the entity - String savedAfterUpload = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!savedAfterUpload.equals("Saved")) { - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not save entity after PDF upload: " + savedAfterUpload); - } - - // Step 4 & 5: Edit the entity, rename each facet's attachment changing extension .pdf -> .txt - for (int i = 0; i < facet.length; i++) { - String editDraftResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!"Entity in draft mode".equals(editDraftResponse)) { - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not put entity in draft mode for rename on facet: " + facet[i]); - } + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String attachID1 = createResponse.get(1); - String renameResponse = - api.renameAttachment( - appUrl, - entityName, - facet[i], - newEntityID, - facetAttachmentIDs[i], - "renamed_document.txt"); - if (!"Renamed".equals(renameResponse)) { - api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not rename attachment on facet " + facet[i] + ": " + renameResponse); - } + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // Step 6: Save and validate the extension change warning message - String saveWithWarningResponse = - api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - assertNotNull(saveWithWarningResponse, "Response should not be null for facet: " + facet[i]); + String folderName = testEntityID + "__attachments"; + ShellScriptRunner.Result folderCheck = + ShellScriptRunner.runAndCaptureAll( + CmisDocumentHelper.getCmisEnvPublic(), + "src/test/java/integration/com/sap/cds/sdm/utils/get-object-id.sh", + folderName); + assertEquals(0, folderCheck.getExitCode(), "Entity folder should exist in CMIS"); - String expectedMessage = - "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - com.fasterxml.jackson.databind.JsonNode messagesNode = - new ObjectMapper().readTree(saveWithWarningResponse); - assertTrue( - messagesNode.isArray(), - "sap-messages response should be a JSON array for facet: " + facet[i]); + response = api.deleteAttachment(appUrl, entityName, facet[0], testEntityID, attachID1); + assertEquals("Deleted", response, "Delete attachment should succeed"); - boolean foundExtensionError = false; - for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { - if (messageNode.has("message")) { - String message = messageNode.get("message").asText(); - if (message.contains("Changing the file extension is not allowed")) { - foundExtensionError = true; - assertEquals( - expectedMessage, - message, - "Extension change error message does not match for facet: " + facet[i]); - break; - } - } - } + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after deleting all attachments"); - assertTrue( - foundExtensionError, - "Expected extension change warning not found for facet: " - + facet[i] - + ". Full response: " - + saveWithWarningResponse); - } + List> attachmentsAfterDelete = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertEquals( + 0, attachmentsAfterDelete.size(), "Entity should have no attachments after deleting all"); - // Clean up - api.deleteEntity(appUrl, entityName, newEntityID); + api.deleteEntity(appUrl, entityName, testEntityID); } @Test - @Order(77) - void testRenameAttachmentWithExtensionChange_BeforeSave() throws IOException { + @Order(92) + void testCopyInvalidAttachments_IntoNewEntity_NothingCopied() throws Exception { System.out.println( - "Test (77) : Upload attachment in draft, rename changing extension before save across all facets - should return extension change warning"); + "Test (85) : Copy attachments with invalid secondary property into new entity" + + " — expect copy succeeds but invalid property not propagated"); - for (int i = 0; i < facet.length; i++) { - // Step 1: Create a new entity draft (do NOT save it yet) - String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (newEntityID.equals("Could not create entity")) { - fail("Could not create entity for facet: " + facet[i]); - } + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceEntity, "Source creation should succeed"); - // Step 2: Upload a PDF attachment while entity is still in draft (unsaved) - ClassLoader classLoader = getClass().getClassLoader(); - File file = new File(classLoader.getResource("sample.pdf").getFile()); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - Map postData = new HashMap<>(); - postData.put("up__ID", newEntityID); - postData.put("mimeType", "application/pdf"); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + List createResponse = + api.createAttachment(appUrl, entityName, facet[0], sourceEntity, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); - String facetAttachmentID = - CreateandReturnFacetID( - appUrl, serviceName, entityName, facet[i], newEntityID, postData, file); - if (facetAttachmentID == null) { - api.deleteEntityDraft(appUrl, entityName, newEntityID); - fail("Could not upload sample.pdf to facet: " + facet[i]); - } + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source save should succeed"); - // Step 3: Rename the attachment changing extension from .pdf to .txt — entity still not saved - String renameResponse = - api.renameAttachment( - appUrl, entityName, facet[i], newEntityID, facetAttachmentID, "renamed_document.txt"); - if (!"Renamed".equals(renameResponse)) { - api.deleteEntityDraft(appUrl, entityName, newEntityID); - fail("Could not rename attachment on facet " + facet[i] + ": " + renameResponse); - } + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID, "invalidTestValue"); - // Step 4: Save — should receive extension change warning, not "Saved" - String saveWithWarningResponse = - api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - assertNotNull(saveWithWarningResponse, "Response should not be null for facet: " + facet[i]); + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID); + assertNotNull(sourceMetadata.get("objectId"), "Source should have objectId"); + String sourceObjectId = sourceMetadata.get("objectId").toString(); - String expectedMessage = - "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); - com.fasterxml.jackson.databind.JsonNode messagesNode = - new ObjectMapper().readTree(saveWithWarningResponse); - assertTrue( - messagesNode.isArray(), - "sap-messages response should be a JSON array for facet: " + facet[i]); + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); - boolean foundExtensionError = false; - for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { - if (messageNode.has("message")) { - String message = messageNode.get("message").asText(); - if (message.contains("Changing the file extension is not allowed")) { - foundExtensionError = true; - assertEquals( - expectedMessage, - message, - "Extension change error message does not match for facet: " + facet[i]); - break; - } - } - } + String copyResponse = + api.copyAttachment(appUrl, entityName, facet[0], targetEntity, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); - assertTrue( - foundExtensionError, - "Expected extension change warning not found for facet: " - + facet[i] - + ". Full response: " - + saveWithWarningResponse); + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Save should succeed — copy does not propagate invalid props"); + + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetEntity, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); - // Clean up - api.deleteEntity(appUrl, entityName, newEntityID); - } + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); } @Test - @Order(78) - void testDownloadAttachmentsAcrossMultipleFacets() throws IOException { + @Order(93) + void testCopyInvalidAttachments_IntoExistingEntity_NothingCopied() throws Exception { System.out.println( - "Test (76): Create entity, upload attachments to each facet, and verify" - + " download works across all facets"); + "Test (86) : Copy attachments with invalid secondary property into existing entity" + + " — expect copy succeeds, invalid property not propagated"); - // Step 1: Create entity - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String downloadTestEntityID = response; + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceEntity, "Source creation should succeed"); ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", downloadTestEntityID); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - postData.put("mimeType", "application/pdf"); - // Step 2: Upload one pdf attachment to each of the 3 facets - File originalPdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - String[] facetAttachmentIDs = new String[facet.length]; - for (int i = 0; i < facet.length; i++) { - File tempFacetFile = File.createTempFile("sample_mf_facet" + i + "_", ".pdf"); - Files.copy( - originalPdfFile.toPath(), tempFacetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - List createResp = - api.createAttachment( - appUrl, entityName, facet[i], downloadTestEntityID, srvpath, postData, tempFacetFile); - tempFacetFile.delete(); - if (!createResp.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not upload pdf to facet: " + facet[i]); - return; - } - facetAttachmentIDs[i] = createResp.get(1); - } + List createResponse = + api.createAttachment( + appUrl, entityName, facet[0], sourceEntity, srvpath, postData, filePdf); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); - // Step 3: Save entity draft - response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not save entity draft: " + response); - return; - } + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source save should succeed"); - // Step 4: Verify download works from each facet independently - for (int i = 0; i < facet.length; i++) { - String downloadResult = - api.downloadSelectedAttachments( - appUrl, entityName, facet[i], downloadTestEntityID, List.of(facetAttachmentIDs[i])); - JSONArray resultArray = new JSONArray(downloadResult); - assertEquals(1, resultArray.length(), "Expected 1 result from facet: " + facet[i]); - JSONObject result = resultArray.getJSONObject(0); - assertEquals( - "success", result.getString("status"), "Download should succeed for facet: " + facet[i]); - assertTrue( - result.has("content"), - "Downloaded attachment in facet " + facet[i] + " should have a content field"); - } + api.updateInvalidSecondaryProperty( + appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID, "invalidTestValue"); - // Step 5: Edit entity back to draft, upload 2 more attachments to facet[0], save, and verify - // multi-download - String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); - if (!editResponse.equals("Entity in draft mode")) { - api.deleteEntity(appUrl, entityName, downloadTestEntityID); - fail("Could not edit entity back to draft mode: " + editResponse); - return; - } + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID); + String sourceObjectId = sourceMetadata.get("objectId").toString(); - List multiIDs = new ArrayList<>(); - multiIDs.add(facetAttachmentIDs[0]); - File[] extraFiles = { - new File(classLoader.getResource("sample1.pdf").getFile()), - new File(classLoader.getResource("sample2.pdf").getFile()) - }; - for (int i = 0; i < 2; i++) { - List extraResp = - api.createAttachment( - appUrl, entityName, facet[0], downloadTestEntityID, srvpath, postData, extraFiles[i]); - if (!extraResp.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not upload extra attachment to facet: " + facet[0]); - return; - } - multiIDs.add(extraResp.get(1)); - } + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); - response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not save entity draft after extra uploads: " + response); - return; - } + File file1Pdf = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postDataTarget = new HashMap<>(); + postDataTarget.put("up__ID", targetEntity); + postDataTarget.put("mimeType", "application/pdf"); + postDataTarget.put("createdAt", new Date().toString()); + postDataTarget.put("createdBy", "test@test.com"); + postDataTarget.put("modifiedBy", "test@test.com"); - String multiResult = - api.downloadSelectedAttachments( - appUrl, entityName, facet[0], downloadTestEntityID, multiIDs); - JSONArray multiArray = new JSONArray(multiResult); - assertEquals(3, multiArray.length(), "Expected 3 results from multi-download in " + facet[0]); - for (int i = 0; i < multiArray.length(); i++) { - assertEquals( - "success", - multiArray.getJSONObject(i).getString("status"), - "Attachment " + (i + 1) + " in " + facet[0] + " should download successfully"); - } + List targetCreateResponse = + api.createAttachment( + appUrl, entityName, facet[0], targetEntity, srvpath, postDataTarget, file1Pdf); + assertEquals("Attachment created", targetCreateResponse.get(0)); - // Clean up - api.deleteEntity(appUrl, entityName, downloadTestEntityID); + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target save should succeed"); + + response = api.editEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Entity in draft mode", response, "Target should enter draft mode"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, entityName, facet[0], targetEntity, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Save should succeed"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], targetEntity); + assertEquals(2, targetAttachments.size(), "Target should have 2 attachments"); + + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetEntity, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); + + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); } @Test - @Order(79) - void testDownloadButtonDisabledWhenLinkSelectedAcrossFacets() throws IOException { + @Order(94) + void testCopyEditedFileName_FromOneEntityToAnother() throws Exception { System.out.println( - "Test (77): Upload pdf to one facet and link to another; verify download" - + " button enabled for pdf facet only, disabled when link-facet item selected"); + "Test (87) : Copy attachment with edited filename — expect target shows edited name"); - // Step 1: Create entity - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String testEntityID = response; + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceEntity, "Source creation should succeed"); ClassLoader classLoader = getClass().getClassLoader(); - - // Step 2: Upload pdf to facet[0] (attachments) + File file = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); + postData.put("up__ID", sourceEntity); postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); List createResponse = - api.createAttachment( - appUrl, entityName, facet[0], testEntityID, srvpath, postData, pdfFile); - if (!createResponse.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not upload pdf to facet: " + facet[0]); - return; - } - String pdfAttachmentID = createResponse.get(1); + api.createAttachment(appUrl, entityName, facet[0], sourceEntity, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0)); + String sourceAttachmentID = createResponse.get(1); - // Step 3: Create a link in facet[1] (references) - String linkResponse = - api.createLink( - appUrl, entityName, facet[1], testEntityID, "TestLink", "https://www.example.com"); - if (!linkResponse.equals("Link created successfully")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not create link in facet: " + facet[1]); - return; + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source save should succeed"); + + String editedFileName = "sampleEdited.pdf"; + response = api.editEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Entity in draft mode", response); + + response = + api.renameAttachment( + appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID, editedFileName); + assertEquals("Renamed", response, "Rename should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source save after rename should succeed"); + + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facet[0], sourceEntity, sourceAttachmentID); + assertEquals(editedFileName, sourceMetadata.get("fileName")); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, entityName, facet[0], targetEntity, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target save should succeed"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], targetEntity); + assertFalse(targetAttachments.isEmpty(), "Target should have attachments"); + + boolean foundEditedFile = false; + for (Map attachment : targetAttachments) { + if (editedFileName.equals(attachment.get("fileName"))) { + foundEditedFile = true; + break; + } } + assertTrue(foundEditedFile, "Target should have attachment with edited filename"); + + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); + } + + @Test + @Order(95) + void testLinkAttachment_CreatedByIsUserNotClientId() throws Exception { + System.out.println( + "Test (88) : Create link and verify createdBy is the user, not the clientID"); + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "testLink"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); - // Step 4: Save entity response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not save entity draft: " + response); - return; - } + assertEquals("Saved", response, "Entity save should succeed"); - // Fetch link attachment ID from facet[1] metadata - List> facet1Attachments = - api.fetchEntityMetadata(appUrl, entityName, facet[1], testEntityID); - String linkAttachmentID = - facet1Attachments.stream() - .filter( - a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) - .map(a -> (String) a.get("ID")) - .findFirst() - .orElse(null); - if (linkAttachmentID == null) { - api.deleteEntity(appUrl, entityName, testEntityID); - fail("Could not find link attachment in facet: " + facet[1]); - return; + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have at least one attachment (link)"); + + String linkID = (String) attachments.get(0).get("ID"); + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[0], testEntityID, linkID); + String createdBy = (String) metadata.get("createdBy"); + String modifiedBy = (String) metadata.get("modifiedBy"); + + assertNotNull(createdBy, "createdBy should not be null"); + assertNotNull(modifiedBy, "modifiedBy should not be null"); + + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals(username, createdBy, "createdBy should be the user"); + assertEquals(username, modifiedBy, "modifiedBy should be the user"); + } else { + assertFalse(createdBy.isEmpty(), "createdBy should not be empty"); + assertFalse(modifiedBy.isEmpty(), "modifiedBy should not be empty"); } - // Step 5: Download pdf from facet[0] - Download button should be enabled - String pdfOnlyResult = - api.downloadSelectedAttachments( - appUrl, entityName, facet[0], testEntityID, List.of(pdfAttachmentID)); - JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); - assertEquals(1, pdfOnlyArray.length(), "Expected 1 result for pdf download from " + facet[0]); - assertEquals( - "success", - pdfOnlyArray.getJSONObject(0).getString("status"), - "Download button should be enabled: pdf in " + facet[0] + " should download successfully"); + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(96) + void testDeleteLink_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println("Test (89) : Delete link not in repository — expect removed from UI"); + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "linkToDelete"; + String linkUrl = "https://www.example.com/delete-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, linkName); + + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + response = api.deleteAttachment(appUrl, entityName, facet[0], testEntityID, linkID); + assertEquals("Deleted", response, "Delete link should succeed in the UI"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after deleting link"); + + List> remainingAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertTrue(remainingAttachments.isEmpty(), "No attachments should remain"); + + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(97) + void testRenameLinkToDuplicateName_BackendConflict_ErrorThrown() throws Exception { + System.out.println("Test (90) : Rename link to duplicate name — expect error"); + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "originalLink"; + String linkUrl = "https://www.example.com/original"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + String conflictingName = "backendLink"; + ClassLoader classLoader = getClass().getClassLoader(); + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testEntityID); + + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + response = + api.renameAttachment(appUrl, entityName, facet[0], testEntityID, linkID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + assertTrue( + response.contains("already exist") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); + + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(98) + void testRenameLink_WhitespaceOnly_WarningThrown() throws Exception { + System.out.println("Test (91) : Rename link with whitespace-only name — expect warning"); + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "linkToRename"; + String linkUrl = "https://www.example.com/rename-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + String whitespaceOnlyName = " "; + response = + api.renameAttachment( + appUrl, entityName, facet[0], testEntityID, linkID, whitespaceOnlyName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertNotEquals("Saved", response, "Save should not succeed with whitespace-only filename"); + assertTrue( + response.contains("cannot be empty") || response.contains("could not be updated"), + "Warning should indicate filename issue. Actual: " + response); + + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(99) + void testDiscardDraftEditedLink_RevertsToOriginalUrl() throws Exception { + System.out.println("Test (92) : Edit link URL, discard draft — expect revert to original URL"); + + String originalUrl = "https://abc.com"; + String editedUrl = "https://xyz.com"; + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "discardTestLink"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], testEntityID, linkName, originalUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + + Map metadataBefore = + api.fetchMetadata(appUrl, entityName, facet[0], testEntityID, linkID); + assertEquals(originalUrl, metadataBefore.get("linkUrl"), "Link should have original URL"); + + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + String editResponse = + api.editLink(appUrl, entityName, facet[0], testEntityID, linkID, editedUrl); + assertEquals("Link edited successfully", editResponse, "Link edit should succeed in draft"); - // Step 6: Attempt to download link from facet[1] - Download button should be disabled - String linkResult = - api.downloadSelectedAttachments( - appUrl, entityName, facet[1], testEntityID, List.of(linkAttachmentID)); - JSONArray linkArray = new JSONArray(linkResult); - assertEquals( - 1, linkArray.length(), "Expected 1 result for link download attempt from " + facet[1]); - JSONObject linkItem = linkArray.getJSONObject(0); - assertEquals( - "error", - linkItem.getString("status"), - "Download button should be disabled: link in " + facet[1] + " should return error"); + response = api.deleteEntityDraft(appUrl, entityName, testEntityID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); + + Map metadataAfterDiscard = + api.fetchMetadata(appUrl, entityName, facet[0], testEntityID, linkID); assertEquals( - "Download is not supported for link attachments", - linkItem.getString("message"), - "Error message for link download in " + facet[1] + " should match"); + originalUrl, metadataAfterDiscard.get("linkUrl"), "Link URL should revert to original"); - // Clean up api.deleteEntity(appUrl, entityName, testEntityID); } @Test - @Order(80) - void testDownloadAttachmentsAcrossMultipleFacetsInDraftState() throws IOException { - System.out.println( - "Test (78): Create entity in draft state, upload attachments to each" - + " facet, download before saving"); + @Order(100) + void testMoveAttachments_FromSdmFolder_ToTargetEntity() throws Exception { + System.out.println("Test (93) : Move attachments from SDM folder to target entity"); - // Step 1: Create entity draft (do NOT save) - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String draftEntityID = response; + String folderName = "move-test-folder-" + System.currentTimeMillis(); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); ClassLoader classLoader = getClass().getClassLoader(); - Map postData = new HashMap<>(); - postData.put("up__ID", draftEntityID); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); - postData.put("mimeType", "application/pdf"); - - // Step 2: Upload one pdf to each of the 3 facets (entity stays in draft state) - File origPdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - String[] draftFacetAttachmentIDs = new String[facet.length]; - for (int i = 0; i < facet.length; i++) { - File tempFacetFile = File.createTempFile("sample_mf_draft_facet" + i + "_", ".pdf"); - Files.copy(origPdfFile.toPath(), tempFacetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - List createResp = - api.createAttachment( - appUrl, entityName, facet[i], draftEntityID, srvpath, postData, tempFacetFile); - tempFacetFile.delete(); - if (!createResp.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, draftEntityID); - fail("Could not upload pdf to facet in draft state: " + facet[i]); - return; - } - draftFacetAttachmentIDs[i] = createResp.get(1); - } - - // Step 3: Verify download works from each facet in draft state - for (int i = 0; i < facet.length; i++) { - String downloadResult = - api.downloadSelectedAttachmentsDraft( - appUrl, entityName, facet[i], draftEntityID, List.of(draftFacetAttachmentIDs[i])); - JSONArray resultArray = new JSONArray(downloadResult); - assertEquals( - 1, resultArray.length(), "Expected 1 result from facet in draft state: " + facet[i]); - JSONObject result = resultArray.getJSONObject(0); - assertEquals( - "success", - result.getString("status"), - "Download should succeed in draft state for facet: " + facet[i]); - assertTrue( - result.has("content"), - "Downloaded attachment in draft facet " + facet[i] + " should have a content field"); + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); + + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created"); + assertNotNull(docId2, "Second document should be created"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); + String response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target save should succeed"); + + String targetFacet = serviceName + "." + entityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[0], + targetEntity, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], targetEntity); + assertEquals(2, targetAttachments.size(), "Target should have 2 attachments after move"); + + for (Map attachment : targetAttachments) { + String attachmentId = (String) attachment.get("ID"); + String readResponse = + api.readAttachment(appUrl, entityName, facet[0], targetEntity, attachmentId); + assertEquals("OK", readResponse, "Moved attachment should be readable"); } - // Clean up - entity was never saved, so delete the draft - api.deleteEntityDraft(appUrl, entityName, draftEntityID); + api.deleteEntity(appUrl, entityName, targetEntity); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); } @Test - @Order(81) - void testDownloadButtonWithPdfAndLinkAcrossFacetsInDraftState() throws IOException { + @Order(101) + void testMoveAttachments_FromSdmFolder_DuplicateInTarget_Skipped() throws Exception { System.out.println( - "Test (79): Upload pdf to one facet and link to another, save entity, edit" - + " (draft state), verify download button enabled for pdf facet, disabled for link" - + " facet"); + "Test (94) : Move from SDM folder with duplicate in target — expect duplicate skipped"); - // Step 1: Create entity draft - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String testEntityID = response; + String folderName = "move-dup-test-folder-" + System.currentTimeMillis(); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); ClassLoader classLoader = getClass().getClassLoader(); + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); - // Step 2: Upload pdf to facet[0] (attachments) + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created"); + assertNotNull(docId2, "Second document should be created"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); + + File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); + postData.put("up__ID", targetEntity); postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - File origPdf = new File(classLoader.getResource("sample.pdf").getFile()); - File tempPdf = File.createTempFile("sample_mf_draftlink_", ".pdf"); - Files.copy(origPdf.toPath(), tempPdf.toPath(), StandardCopyOption.REPLACE_EXISTING); List createResponse = api.createAttachment( - appUrl, entityName, facet[0], testEntityID, srvpath, postData, tempPdf); - tempPdf.delete(); - if (!createResponse.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not upload pdf to facet: " + facet[0]); - return; - } - String pdfAttachmentID = createResponse.get(1); - - // Step 3: Create a link in facet[1] (references) - String linkResponse = - api.createLink( - appUrl, entityName, facet[1], testEntityID, "TestLink", "https://www.example.com"); - if (!linkResponse.equals("Link created successfully")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not create link in facet: " + facet[1]); - return; - } - - // Fetch link attachment ID from draft metadata of facet[1] - List> draftFacet1Attachments = - api.fetchEntityMetadataDraft(appUrl, entityName, facet[1], testEntityID); - String linkAttachmentID = - draftFacet1Attachments.stream() - .filter( - a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) - .map(a -> (String) a.get("ID")) - .findFirst() - .orElse(null); - if (linkAttachmentID == null) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not find link attachment in draft facet: " + facet[1]); - return; - } - - // Step 4: Save entity - response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not save entity draft: " + response); - return; - } - - // Step 5: Edit entity - puts it back into draft state - String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!editResponse.equals("Entity in draft mode")) { - api.deleteEntity(appUrl, entityName, testEntityID); - fail("Could not put entity into edit/draft mode: " + editResponse); - return; - } - - // Step 6: Select pdf from facet[0] in draft state - Download button should be enabled - String pdfOnlyResult = - api.downloadSelectedAttachmentsDraft( - appUrl, entityName, facet[0], testEntityID, List.of(pdfAttachmentID)); - JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); - assertEquals( - 1, - pdfOnlyArray.length(), - "Expected 1 result for pdf download from " + facet[0] + " in draft state"); + appUrl, entityName, facet[0], targetEntity, srvpath, postData, duplicateFile); + assertEquals("Attachment created", createResponse.get(0)); + + String response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target save should succeed"); + + String targetFacet = serviceName + "." + entityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[0], + targetEntity, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], targetEntity); assertEquals( - "success", - pdfOnlyArray.getJSONObject(0).getString("status"), - "Download button should be enabled in draft state: pdf in " + facet[0] + " should succeed"); + 2, targetAttachments.size(), "Target should have 2 attachments (1 orig + 1 moved)"); - // Step 7: Select link from facet[1] in draft state - Download button should be disabled - String linkOnlyResult = - api.downloadSelectedAttachmentsDraft( - appUrl, entityName, facet[1], testEntityID, List.of(linkAttachmentID)); - JSONArray linkOnlyArray = new JSONArray(linkOnlyResult); - assertEquals( - 1, - linkOnlyArray.length(), - "Expected 1 result for link download attempt from " + facet[1] + " in draft state"); - JSONObject linkItem = linkOnlyArray.getJSONObject(0); - assertEquals( - "error", - linkItem.getString("status"), - "Download button should be disabled in draft state: link in " - + facet[1] - + " should return error"); - assertEquals( - "Download is not supported for link attachments", - linkItem.getString("message"), - "Error message for link download in " + facet[1] + " in draft state should match"); + List fileNames = + targetAttachments.stream() + .map(a -> (String) a.get("fileName")) + .collect(Collectors.toList()); + assertTrue(fileNames.contains("sample.pdf"), "Target should have sample.pdf"); + assertTrue(fileNames.contains("sample1.pdf"), "Target should have sample1.pdf (moved)"); - // Clean up - api.deleteEntity(appUrl, entityName, testEntityID); + api.deleteEntity(appUrl, entityName, targetEntity); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); } - // @Test - // @Order(78) - // void testUploadAttachmentExceedingMaximumFileSize() throws IOException { - // System.out.println( - // "Test (76) : Upload attachment exceeding maximum file size in references facet"); - - // // Create a new entity - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response.equals("Could not create entity")) { - // fail("Could not create entity"); - // } - // String testEntityID = response; + @Test + @Order(102) + void testMoveAttachments_FromSdmFolder_WithSecondaryProperties_Preserved() throws Exception { + System.out.println( + "Test (95) : Move from SDM folder with secondary properties — expect preserved"); - // // Load the 150MB sample file - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample32mb.pdf").getFile()); + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", sourceEntity, "Source creation should succeed"); - // for (int i = 0; i < facet.length; i++) { - // Map postData = new HashMap<>(); - // postData.put("up__ID", testEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); - // List createResponse = - // api.createAttachment(appUrl, entityName, facet[i], testEntityID, srvpath, postData, - // file); - // String check = createResponse.get(0); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Only 'references' facet has 30MB limit, others should succeed - // if (facet[i].equals("references")) { - // // The upload should fail with AttachmentSizeExceeded error - // if (!check.equals("Attachment created")) { - // try { - // JSONObject json = new JSONObject(check); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("413", errorCode); - // assertEquals("File size exceeds the limit of 30MB.", errorMessage); - // } catch (Exception e) { - // fail("Failed to parse error response for references facet: " + e.getMessage()); - // } - // } else { - // fail("Attachment got created in references facet with file size exceeding maximum // - // limit"); - // } - // } else { - // // For attachments and footnotes, expect success - // if (!check.equals("Attachment created")) { - // fail("Attachment upload failed in " + facet[i] + " facet: " + check); - // } - // } - // } + List createResponse1 = + api.createAttachment( + appUrl, entityName, facet[0], sourceEntity, srvpath, postData, filePdf); + assertEquals("Attachment created", createResponse1.get(0)); + String attachId1 = createResponse1.get(1); + + String linkName = "testMoveLink"; + String linkUrl = "https://www.example.com/move-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facet[0], sourceEntity, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + String notesValue = "Move test note"; + MediaType mediaType = MediaType.parse("application/json"); + RequestBody notesBody = RequestBody.create(mediaType, "{\"note\": \"" + notesValue + "\"}"); + String updateNotes1 = + api.updateSecondaryProperty( + appUrl, entityName, facet[0], sourceEntity, attachId1, notesBody); + assertEquals("Updated", updateNotes1, "Notes update should succeed"); + + Integer customIntValue = 42; + RequestBody intBody = + RequestBody.create(mediaType, "{\"customProperty2\": " + customIntValue + "}"); + String updateInt1 = + api.updateSecondaryProperty(appUrl, entityName, facet[0], sourceEntity, attachId1, intBody); + assertEquals("Updated", updateInt1, "Custom property update should succeed"); + + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source save should succeed"); + + List objectIdsToMove = new ArrayList<>(); + String sourceFolderIdLocal = null; + + List> sourceAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], sourceEntity); + for (Map attachment : sourceAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[0], sourceEntity, attId); + if (metadata.get("objectId") != null) { + objectIdsToMove.add(metadata.get("objectId").toString()); + } + if (sourceFolderIdLocal == null && metadata.get("folderId") != null) { + sourceFolderIdLocal = metadata.get("folderId").toString(); + } + } + assertNotNull(sourceFolderIdLocal, "Source folder ID should be found"); + assertFalse(objectIdsToMove.isEmpty(), "Should have objectIds to move"); + + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", targetEntity, "Target creation should succeed"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target save should succeed"); + + String targetFacet = serviceName + "." + entityName + "." + facet[0]; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facet[0], + targetEntity, + sourceFolderIdLocal, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facet[0], targetEntity); + assertEquals( + sourceAttachments.size(), targetAttachments.size(), "Target should have all attachments"); + + boolean foundWithNotes = false; + boolean foundLink = false; + for (Map attachment : targetAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, entityName, facet[0], targetEntity, attId); + + if (notesValue.equals(metadata.get("note"))) { + foundWithNotes = true; + assertEquals( + customIntValue, metadata.get("customProperty2"), "Custom property should be preserved"); + } + if (linkUrl.equals(metadata.get("linkUrl"))) { + foundLink = true; + } + } + assertTrue( + foundWithNotes, "Attachment with notes and secondary properties should be preserved"); + assertTrue(foundLink, "Link should be moved successfully"); - // // delete the draft entity - // api.deleteEntityDraft(appUrl, entityName, testEntityID); - // } + api.deleteEntity(appUrl, entityName, targetEntity); + api.deleteEntity(appUrl, entityName, sourceEntity); + } } diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_SingleFacet.java b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_SingleFacet.java index ce69f08d..1f4962c8 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_SingleFacet.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_SingleFacet.java @@ -2,13 +2,20 @@ import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import integration.com.sap.cds.sdm.utils.CmisDocumentHelper; +import integration.com.sap.cds.sdm.utils.ShellScriptRunner; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; import okhttp3.*; +import okio.ByteString; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.*; @@ -273,7034 +280,8429 @@ void testUploadSingleAttachmentPDF() throws IOException { } } - // @Test - // @Order(4) - // void testUploadSingleAttachmentTXT() throws IOException { - // System.out.println("Test (4) : Upload txt"); - // Boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.txt").getFile()); + @Test + @Order(4) + void testUploadSingleAttachmentTXT() throws IOException { + System.out.println("Test (4) : Upload txt"); + Boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/txt"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/txt"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Entity in draft mode") { - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // attachmentID2 = createResponse.get(1); - // response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID, - // attachmentID2); - // if (response.equals("OK")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // response = api.readAttachment(appUrl, entityName, facetName, entityID, - // attachmentID2); - // if (response.equals("OK")) { - // testStatus = true; - // } - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not upload sample.txt"); - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Entity in draft mode") { + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + attachmentID2 = createResponse.get(1); + response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID, attachmentID2); + if (response.equals("OK")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + response = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID2); + if (response.equals("OK")) { + testStatus = true; + } + } + } + } + } + if (!testStatus) { + fail("Could not upload sample.txt"); + } + } - // @Test - // @Order(5) - // void testUploadSingleAttachmentEXE() throws IOException { - // System.out.println("Test (5) : Upload exe"); - // Boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.exe").getFile()); + @Test + @Order(5) + void testUploadSingleAttachmentEXE() throws IOException { + System.out.println("Test (5) : Upload exe"); + Boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.exe").getFile()); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/exe"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/exe"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Entity in draft mode") { - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // attachmentID3 = createResponse.get(1); - // response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID, - // attachmentID3); - // if (response.equals("OK")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // response = api.readAttachment(appUrl, entityName, facetName, entityID, - // attachmentID3); - // if (response.equals("OK")) { - // testStatus = true; - // } - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not create sample.exe"); - // } - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Entity in draft mode") { + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + attachmentID3 = createResponse.get(1); + response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID, attachmentID3); + if (response.equals("OK")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + response = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID3); + if (response.equals("OK")) { + testStatus = true; + } + } + } + } + } + if (!testStatus) { + fail("Could not create sample.exe"); + } + } - // @Test - // @Order(6) - // void testUploadAttachmentWithoutSDMRole() throws IOException { - // System.out.println("Test (6) : Upload attachment with no SDM role"); - // Boolean testStatus = false; - // String response = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID4 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID4); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // apiNoRoles.createAttachment( - // appUrl, entityName, facetName, entityID4, srvpath, postData, tempFile); - // String check = createResponse.get(0); - // String expectedString = - // "{\"error\":{\"code\":\"500\",\"message\":\"You do not have the required permissions to - // upload attachments. Please contact your administrator for access.\"}}"; - // if (check.equals(expectedString)) { - // testStatus = true; - // } - // } - // if (!testStatus) { - // fail("Attachment created without SDM role"); - // } - // } + @Test + @Order(6) + void testUploadAttachmentWithoutSDMRole() throws IOException { + System.out.println("Test (6) : Upload attachment with no SDM role"); + Boolean testStatus = false; + String response = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID4 = response; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID4); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(7) - // void testUploadSingleAttachmentPDFDuplicate() throws IOException { - // System.out.println("Test (7) : Upload duplicate pdf"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Boolean testStatus = false; + List createResponse = + apiNoRoles.createAttachment( + appUrl, entityName, facetName, entityID4, srvpath, postData, tempFile); + String check = createResponse.get(0); + String expectedString = + "{\"error\":{\"code\":\"500\",\"message\":\"You do not have the required permissions to upload attachments. Please contact your administrator for access.\"}}"; + if (check.equals(expectedString)) { + testStatus = true; + } + } + if (!testStatus) { + fail("Attachment created without SDM role"); + } + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(7) + void testUploadSingleAttachmentPDFDuplicate() throws IOException { + System.out.println("Test (7) : Upload duplicate pdf"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Entity in draft mode") { - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // testStatus = false; - // } else { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // String expectedJson = - // "{\"error\":{\"code\":\"500\",\"message\":\"An object named \\\"sample.pdf\\\" - // already exists. Rename the object and try again.\"}}"; - // ObjectMapper objectMapper = new ObjectMapper(); - // JsonNode actualJsonNode = objectMapper.readTree(check); - // JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); - // if (expectedJsonNode.equals(actualJsonNode)) { - // testStatus = true; - // } - // } - // } - // } - // if (!testStatus) { - // fail("Attachment created"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(8) - // void testUploadSingleAttachmentPDFDuplicateDifferentEntity() throws IOException { - // System.out.println("Test (8) : Upload duplicate pdf in different entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID2 = response; - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // if (response == "Saved") { - // response = api.checkEntity(appUrl, entityName, entityID2); - // if (response.equals("Entity exists")) { - // testStatus = true; - // } - // } - // } - // if (!testStatus) { - // fail("Could not create entity"); - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Entity in draft mode") { + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + testStatus = false; + } else { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + String expectedJson = + "{\"error\":{\"code\":\"500\",\"message\":\"An object named \\\"sample.pdf\\\" already exists. Rename the object and try again.\"}}"; + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode actualJsonNode = objectMapper.readTree(check); + JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); + if (expectedJsonNode.equals(actualJsonNode)) { + testStatus = true; + } + } + } + } + if (!testStatus) { + fail("Attachment created"); + } + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); + @Test + @Order(8) + void testUploadSingleAttachmentPDFDuplicateDifferentEntity() throws IOException { + System.out.println("Test (8) : Upload duplicate pdf in different entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID2 = response; + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); + if (response == "Saved") { + response = api.checkEntity(appUrl, entityName, entityID2); + if (response.equals("Entity exists")) { + testStatus = true; + } + } + } + if (!testStatus) { + fail("Could not create entity"); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID2); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // response = api.editEntityDraft(appUrl, entityName, srvpath, entityID2); - // if (response == "Entity in draft mode") { - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID2, srvpath, postData, - // file); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // attachmentID4 = createResponse.get(1); - // response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID2, - // attachmentID4); - // if (response.equals("OK")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // if (response.equals("Saved")) { - // response = api.readAttachment(appUrl, entityName, facetName, entityID2, - // attachmentID4); - - // if (response.equals("OK")) { - // testStatus = true; - // } - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not upload sample.pdf " + response); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", entityID2); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(9) - // void testCreateAttachmentWithRestrictedCharacterInFilename() throws IOException { - // System.out.println("Test (9): Create attachment with restricted character in filename"); + response = api.editEntityDraft(appUrl, entityName, srvpath, entityID2); + if (response == "Entity in draft mode") { + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID2, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + attachmentID4 = createResponse.get(1); + response = api.readAttachmentDraft(appUrl, entityName, facetName, entityID2, attachmentID4); + if (response.equals("OK")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); + if (response.equals("Saved")) { + response = api.readAttachment(appUrl, entityName, facetName, entityID2, attachmentID4); - // boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); + if (response.equals("OK")) { + testStatus = true; + } + } + } + } + } + if (!testStatus) { + fail("Could not upload sample.pdf " + response); + } + } - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + @Test + @Order(9) + void testCreateAttachmentWithRestrictedCharacterInFilename() throws IOException { + System.out.println("Test (9): Create attachment with restricted character in filename"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Entity in draft mode")) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, entityID, srvpath, postData, tempFile); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // attachmentID6 = createResponse.get(1); - - // String restrictedFilename = "a/\\bc.pdf"; - // response = - // api.renameAttachment( - // appUrl, entityName, facetName, entityID, attachmentID6, restrictedFilename); - - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // if (response.equals(expected)) { - // api.renameAttachment( - // appUrl, entityName, facetName, entityID, attachmentID6, "sample3.pdf"); - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if ("Saved".equals(response)) testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // } - // if (!testStatus) { - // fail("Attachment created with restricted character in filename"); - // } - // } + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - // @Test - // @Order(10) - // void testDraftUpdateWithFileUploadDeleteAndCreate() throws IOException { - // System.out.println("Test (10): Upload attachments, delete one and create entity"); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - - // entityID5 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID5); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // List createResponse1 = - // api.createAttachment(appUrl, entityName, facetName, entityID5, srvpath, postData1, - // file); - // if (createResponse1.get(0).equals("Attachment created")) { - // attachmentID7 = createResponse1.get(1); - // } + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Entity in draft mode")) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, entityID, srvpath, postData, tempFile); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + attachmentID6 = createResponse.get(1); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData2 = new HashMap<>(); - // postData2.put("up__ID", entityID5); - // postData2.put("mimeType", "application/txt"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // List createResponse2 = - // api.createAttachment(appUrl, entityName, facetName, entityID5, srvpath, postData2, - // file); - // if (createResponse2.get(0).equals("Attachment created")) { - // attachmentID8 = createResponse2.get(1); - // } - // response = api.deleteAttachment(appUrl, entityName, facetName, entityID5, attachmentID8); - // if (response.equals("Deleted")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); + String restrictedFilename = "a/\\bc.pdf"; + response = + api.renameAttachment( + appUrl, entityName, facetName, entityID, attachmentID6, restrictedFilename); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } - // } - // if (!testStatus) { - // fail("Failed to create entity after deleting one attachment"); - // } - // } + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"a/\\bc.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + if (response.equals(expected)) { + api.renameAttachment( + appUrl, entityName, facetName, entityID, attachmentID6, "sample3.pdf"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if ("Saved".equals(response)) testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + } + if (!testStatus) { + fail("Attachment created with restricted character in filename"); + } + } - // @Test - // @Order(11) - // void testUpdateEntityDraft() throws IOException { - // System.out.println("Test (11): Update entity in draft"); - // boolean testStatus = false; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); + @Test + @Order(10) + void testDraftUpdateWithFileUploadDeleteAndCreate() throws IOException { + System.out.println("Test (10): Upload attachments, delete one and create entity"); - // File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); - // Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID5); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + entityID5 = response; + ClassLoader classLoader = getClass().getClassLoader(); + + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID5); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment(appUrl, entityName, facetName, entityID5, srvpath, postData1, file); + if (createResponse1.get(0).equals("Attachment created")) { + attachmentID7 = createResponse1.get(1); + } - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID5); - // if (response.equals("Entity in draft mode")) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, entityID5, srvpath, postData, tempFile); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } - // } - // if (!testStatus) { - // fail("update entity draft with uploading attachment failed"); - // } - // api.deleteEntity(appUrl, entityName, entityID5); - // } + file = new File(classLoader.getResource("sample.txt").getFile()); + Map postData2 = new HashMap<>(); + postData2.put("up__ID", entityID5); + postData2.put("mimeType", "application/txt"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + List createResponse2 = + api.createAttachment(appUrl, entityName, facetName, entityID5, srvpath, postData2, file); + if (createResponse2.get(0).equals("Attachment created")) { + attachmentID8 = createResponse2.get(1); + } + response = api.deleteAttachment(appUrl, entityName, facetName, entityID5, attachmentID8); + if (response.equals("Deleted")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); - // @Test - // @Order(12) - // void testRenameSingleAttachment() { - // System.out.println("Test (12) : Rename single attachment"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String name = "sample123"; - // if (response == "Entity in draft mode") { - // response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, - // name); - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment was not renamed"); - // } - // } + if (response.equals("Saved")) { + testStatus = true; + } + } + } + if (!testStatus) { + fail("Failed to create entity after deleting one attachment"); + } + } - // @Test - // @Order(13) - // void testRenameAttachmentWithUnsupportedCharacter() { - // System.out.println("Test (13) : Rename single attachment with unsupported characters"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String name = "invalid/name"; - // if (response == "Entity in draft mode") { - // response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, - // name); - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"invalid/name\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // if (response.equals(expected)) { - // api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, - // "sample123"); - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if ("Saved".equals(response)) testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment was renamed with unsupported characters"); - // } - // } + @Test + @Order(11) + void testUpdateEntityDraft() throws IOException { + System.out.println("Test (11): Update entity in draft"); + boolean testStatus = false; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.pdf")).getFile()); - // @Test - // @Order(14) - // void testRenameMultipleAttachments() { - // System.out.println("Test (14) : Rename multiple attachments"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String name1 = "sample1234"; - // String name2 = "sample12345"; - // if (response == "Entity in draft mode") { - // String response1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID2, name1); - // String response2 = - // api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, name2); - // if (response1.equals("Renamed") && response2.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment was not renamed"); - // } - // } + File tempFile = new File(System.getProperty("java.io.tmpdir"), "sample3.pdf"); + Files.copy(file.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID5); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID5); + if (response.equals("Entity in draft mode")) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, entityID5, srvpath, postData, tempFile); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID5); + if (response.equals("Saved")) { + testStatus = true; + } + } + } + if (!testStatus) { + fail("update entity draft with uploading attachment failed"); + } + api.deleteEntity(appUrl, entityName, entityID5); + } + + @Test + @Order(12) + void testRenameSingleAttachment() { + System.out.println("Test (12) : Rename single attachment"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String name = "sample123"; + if (response == "Entity in draft mode") { + response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, name); + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment was not renamed"); + } + } + + @Test + @Order(13) + void testRenameAttachmentWithUnsupportedCharacter() { + System.out.println("Test (13) : Rename single attachment with unsupported characters"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String name = "invalid/name"; + if (response == "Entity in draft mode") { + response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, name); + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"invalid/name\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + if (response.equals(expected)) { + api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, "sample123"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if ("Saved".equals(response)) testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment was renamed with unsupported characters"); + } + } + + @Test + @Order(14) + void testRenameMultipleAttachments() { + System.out.println("Test (14) : Rename multiple attachments"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String name1 = "sample1234"; + String name2 = "sample12345"; + if (response == "Entity in draft mode") { + String response1 = + api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID2, name1); + String response2 = + api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, name2); + if (response1.equals("Renamed") && response2.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment was not renamed"); + } + } + + @Test + @Order(15) + void testRenameSingleAttachmentDuplicate() { + System.out.println("Test (15) : Rename single attachment duplicate"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + String name = "sample123"; + String name2 = "sample123456"; + if (response == "Entity in draft mode") { + response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, name); + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sample123\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + if (response.equals(expected)) { + response = + api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, name2); + if (response.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response.equals("Saved")) { + testStatus = true; + } + } + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment was renamed"); + } + } + + @Test + @Order(16) + void testRenameMultipleAttachmentsWithOneUnsupportedCharacter() { + System.out.println( + "Test (16) : Rename multiple attachments where one name has unsupported characters"); + Boolean testStatus = false; + + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + + if (response.equals("Entity in draft mode")) { + String validName1 = "valid_attachment1.pdf"; + String invalidName2 = "invalid/attachment2.pdf"; + + String renameResponse1 = + api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, validName1); + String renameResponse2 = + api.renameAttachment( + appUrl, entityName, facetName, entityID, attachmentID2, invalidName2); + + if (renameResponse1.equals("Renamed") && renameResponse2.equals("Renamed")) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"invalid/attachment2.pdf\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + if (response.equals(expected)) { + api.renameAttachment( + appUrl, entityName, facetName, entityID, attachmentID2, "sample1234"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if ("Saved".equals(response)) testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + + if (!testStatus) { + fail("Multiple renames should have failed due to one unsupported characters"); + } + } + + @Test + @Order(17) + void testRenameSingleAttachmentWithoutSDMRole() throws IOException { + System.out.println("Test (17) : Rename attachments where user don't have SDM Roles"); + boolean testStatus = false; + String apiResponse = apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, entityID); + String name = "sample123"; // Renaming the attachment + if (apiResponse == "Entity in draft mode") { + apiResponse = + apiNoRoles.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, name); + if (apiResponse.equals("Renamed")) { + apiResponse = apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); + String expected = + "[{\"code\":\"\",\"message\":\"Could not update the following files. \\n" + + // + "\\n" + + // + "\\t\\u2022 valid_attachment1.pdf\\n" + + // + "\\n" + + // + "You do not have the required permissions to update attachments. Kindly contact the admin\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (apiResponse.equals(expected)) { + testStatus = true; + } + } else { + apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); + } + } + if (!testStatus) { + fail("Attachment got renamed without SDM roles."); + } + } + + @Test + @Order(18) + void testRenameToValidateNames() throws IOException { + System.out.println("Test (18) : Rename attachments to validate names"); + boolean testStatus = false, successCount = true; + String generatedID = ""; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID3 = response; + String[] filetoUpload = {"sample.pdf", "sample.txt", "sample.exe", "sample2.pdf"}; + String[] names = {"Restricted/Character", " ", "duplicateName.pdf", "duplicateName.pdf"}; + + ClassLoader classLoader = getClass().getClassLoader(); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + for (int i = 0; i < filetoUpload.length; i++) { + File file = new File(classLoader.getResource(filetoUpload[i]).getFile()); + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, file); + generatedID = createResponse.get(1); + response = + api.renameAttachment(appUrl, entityName, facetName, entityID3, generatedID, names[i]); + successCount &= "Renamed".equals(response); + } + if (successCount) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + String expected = + "{\"error\":{\"code\":\"400\",\"message\":\"The object name cannot be empty or consist entirely of space characters. Enter a value.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"Restricted/Character\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"duplicateName.pdf\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; + if (response.equals(expected)) { + response = api.deleteEntityDraft(appUrl, entityName, entityID3); + if (response.equals("Entity Draft Deleted")) testStatus = true; + } + } + if (!testStatus) fail("Could not create entity"); + } else { + fail("Could not create entity"); + return; + } + } + + @Test + @Order(19) + void testDeleteSingleAttachment() throws IOException { + System.out.println("Test (19) : Delete single attachment"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Entity in draft mode") { + response = api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID1); + if (response == "Deleted") { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Saved") { + response = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID1); + if (response.equals("Could not read Attachment")) { + testStatus = true; + } + } + } + } + if (!testStatus) { + fail("Could not read Attachment"); + } + } + + @Test + @Order(20) + void testDeleteMultipleAttachments() throws IOException { + System.out.println("Test (20) : Delete multiple attachments"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Entity in draft mode") { + String response1 = + api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID2); + String response2 = + api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID3); + if (response1 == "Deleted" && response2 == "Deleted") { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); + if (response == "Saved") { + response1 = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID2); + response2 = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID3); + if (response1.equals("Could not read Attachment") + && response2.equals("Could not read Attachment")) { + testStatus = true; + } + } + } + } + if (!testStatus) { + fail("Could not delete attachment"); + } + } + + @Test + @Order(21) + void testUploadBlockedMimeType() throws IOException { + System.out.println("Test (21): Upload blocked mimeType .rtf"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!"Could not create entity".equals(response)) { + entityID2 = response; + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(Objects.requireNonNull(classLoader.getResource("sample.rtf")).getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID2); + postData.put("mimeType", "application/rtf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID2, srvpath, postData, file); + String actualResponse = createResponse.get(0); + String expectedJson = + "{\"error\":{\"code\":\"500\",\"message\":\"This file type is not allowed in this repository. Contact your administrator for assistance.\"}}"; + + if (expectedJson.equals(actualResponse)) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); + if ("Saved".equals(response)) { + testStatus = true; + } + } else { + api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); + } + } + if (!testStatus) { + fail("Attachment got uploaded with blocked .rtf MIME type"); + } + } + + @Test + @Order(22) + void testDeleteEntity() { + System.out.println("Test (22) : Delete entity"); + Boolean testStatus = false; + String response = api.deleteEntity(appUrl, entityName, entityID); + String response2 = api.deleteEntity(appUrl, entityName, entityID2); + if (response == "Entity Deleted" && response2 == "Entity Deleted") { + testStatus = true; + } + if (!testStatus) { + fail("Could not delete entity"); + } + } + + @Test + @Order(23) + void testUpdateValidSecondaryProperty_beforeEntityIsSaved_singleAttachment() throws IOException { + System.out.println("Test (23): Rename & Update secondary property before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID3 = response; + System.out.println("Entity created"); + System.out.println("Creating attachment"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + attachmentID1 = createResponse.get(1); + System.out.println("Attachment created"); + String name1 = "sample1234.pdf"; + String secondaryPropertyString = "sample12345"; + Integer secondaryPropertyInt = 1234; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + System.out.println("Renaming and updating secondary properties for attachment"); + String response1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + if (response1 == "Renamed" + && updateSecondaryPropertyResponse1 == "Updated" + && updateSecondaryPropertyResponse2 == "Updated" + && updateSecondaryPropertyResponse3 == "Updated" + && updateSecondaryPropertyResponse4 == "Updated") { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation --- + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals(name1, cmisName, "CMIS should reflect renamed filename"); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set in CMIS"); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match in CMIS"); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool, "DocumentInfoRecordBoolean should be true in CMIS"); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set in CMIS"); + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachment"); + } + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(24) + void testUpdateValidSecondaryProperty_afterEntityIsSaved_singleAttachment() { + System.out.println("Test (24): Rename & Update secondary property after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response == "Entity in draft mode") { + String name1 = "sample.pdf"; + String secondaryPropertyString = "sample"; + Integer secondaryPropertyInt = 12; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + System.out.println("Renaming and updating secondary properties for attachment"); + String response1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + if (response1 == "Renamed" + && updateSecondaryPropertyResponse1 == "Updated" + && updateSecondaryPropertyResponse2 == "Updated" + && updateSecondaryPropertyResponse3 == "Updated" + && updateSecondaryPropertyResponse4 == "Updated") { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation --- + String cmisName = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals(name1, cmisName, "CMIS should reflect renamed filename"); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordString"); + assertNotNull(cmisString, "DocumentInfoRecordString should be set in CMIS"); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt), + cmisInt, + "DocumentInfoRecordInt should match in CMIS"); + String cmisBool = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool, "DocumentInfoRecordBoolean should be true in CMIS"); + String cmisDate = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate, "DocumentInfoRecordDate should be set in CMIS"); + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachment"); + } + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + if (!testStatus) { + fail("Could not update secondary property after entity is saved"); + } + } + + @Test + @Order(25) + void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_singleAttachment() + throws IOException { + System.out.println( + "Test (25): Rename & Update invalid secondary property before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!"Could not create entity".equals(response)) { + entityID3 = response; + System.out.println("Entity created"); + System.out.println("Creating attachment"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID3); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, file); + String check = createResponse.get(0); + if ("Attachment created".equals(check)) { + attachmentID1 = createResponse.get(1); + System.out.println("Attachment created"); + String name1 = "sample1234.pdf"; + + // Dropdown values for secondaryPropertyString + String[] dropdownValues = {"A", "B", "C"}; + // Select one dropdown value (e.g., "A") + String secondaryPropertyString = dropdownValues[0]; + + Integer secondaryPropertyInt = 1234; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + String invalidProperty = "testid"; + + System.out.println("Renaming and updating invalid secondary properties for attachment"); + String response1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + + // Update secondary properties for String using dropdown selected value as object with code + + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown1); + + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + + // Update invalid secondary property + String updateSecondaryPropertyResponse5 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, invalidProperty); + + if ("Renamed".equals(response1) + && "Updated".equals(updateSecondaryPropertyResponse1) + && "Updated".equals(updateSecondaryPropertyResponse2) + && "Updated".equals(updateSecondaryPropertyResponse3) + && "Updated".equals(updateSecondaryPropertyResponse4) + && "Updated".equals(updateSecondaryPropertyResponse5)) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + Map attachmentMetadata = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); + assertEquals("sample.pdf", attachmentMetadata.get("fileName")); + assertNull(attachmentMetadata.get("customProperty3")); + assertNull(attachmentMetadata.get("customProperty4")); + assertNull(attachmentMetadata.get("customProperty1_code")); + assertNull(attachmentMetadata.get("customProperty2")); + assertNull(attachmentMetadata.get("customProperty6")); + assertNull(attachmentMetadata.get("customProperty5")); + + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation: no changes should persist in DI --- + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals("sample.pdf", cmisName, "Filename should NOT be changed in CMIS"); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist in CMIS"); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordString"); + assertNull( + cmisString, "Valid props should not persist when invalid props cause rejection"); + String cmisInt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordInt"); + assertNull( + cmisInt, "Valid props should not persist when invalid props cause rejection"); + testStatus = true; + System.out.println( + "Rename & update secondary properties for attachment is unsuccessfull"); + } + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(26) + void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_singleAttachment() throws IOException { + System.out.println( + "Test (26): Rename & Update invalid secondary property after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response == "Entity in draft mode") { + String name1 = "sample.pdf"; + String secondaryPropertyString = "A"; + Integer secondaryPropertyInt = 12; + LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); + String invalidProperty = "testidinvalid"; + System.out.println("Renaming and updating invalid secondary properties for attachment"); + String response1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); + String updateSecondaryPropertyResponse3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + // Update invalid secondary property + String updateSecondaryPropertyResponse5 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, invalidProperty); + if (response1 == "Renamed" + && updateSecondaryPropertyResponse1 == "Updated" + && updateSecondaryPropertyResponse2 == "Updated" + && updateSecondaryPropertyResponse3 == "Updated" + && updateSecondaryPropertyResponse4 == "Updated" + && updateSecondaryPropertyResponse5 == "Updated") { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + Map attachmentMetadata = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); + assertEquals("sample.pdf", attachmentMetadata.get("fileName")); + assertNull(attachmentMetadata.get("customProperty3")); + assertNull(attachmentMetadata.get("customProperty4")); + assertNull(attachmentMetadata.get("customProperty1_code")); + assertNull(attachmentMetadata.get("customProperty2")); + assertNull(attachmentMetadata.get("customProperty6")); + assertNull(attachmentMetadata.get("customProperty5")); + + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation: no changes should persist in DI --- + String cmisName = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals("sample.pdf", cmisName, "Filename should NOT be changed in CMIS"); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist in CMIS"); + String cmisString = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.pdf", "Working:DocumentInfoRecordString"); + assertNull( + cmisString, "Valid props should not persist when invalid props cause rejection"); + testStatus = true; + System.out.println( + "Rename & update secondary properties for attachment is unsuccessfull"); + } + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(27) + void testUpdateValidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (27): Rename & Update valid secondary properties for multiple attachments before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID3 = response; + + System.out.println("Entity created"); + + System.out.println("Creating attachment PDF"); + ClassLoader classLoader = getClass().getClassLoader(); + + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID3); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData1, file); + if (createResponse1.get(0).equals("Attachment created")) { + attachmentID1 = createResponse1.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment TXT"); + file = new File(classLoader.getResource("sample.txt").getFile()); + Map postData2 = new HashMap<>(); + postData2.put("up__ID", entityID3); + postData2.put("mimeType", "application/txt"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + List createResponse2 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData2, file); + if (createResponse2.get(0).equals("Attachment created")) { + attachmentID2 = createResponse2.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment EXE"); + file = new File(classLoader.getResource("sample.exe").getFile()); + Map postData3 = new HashMap<>(); + postData3.put("up__ID", entityID3); + postData3.put("mimeType", "application/exe"); + postData3.put("createdAt", new Date().toString()); + postData3.put("createdBy", "test@test.com"); + postData3.put("modifiedBy", "test@test.com"); + + List createResponse3 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData3, file); + if (createResponse3.get(0).equals("Attachment created")) { + attachmentID3 = createResponse3.get(1); + System.out.println("Attachment created"); + } + + String check1 = createResponse1.get(0); + String check2 = createResponse2.get(0); + String check3 = createResponse3.get(0); + if (check1.equals("Attachment created") + && check2.equals("Attachment created") + && check3.equals("Attachment created")) { + Boolean attachment1Updated = false; + Boolean attachment2Updated = false; + Boolean attachment3Updated = false; + + String name1 = "sample1234.pdf"; + Integer secondaryPropertyInt1 = 1234; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + System.out.println("Renaming and updating secondary properties for attachment PDF"); + String responsePDF1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponsePDF1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponsePDF2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponsePDF3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponsePDF4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + if (responsePDF1 == "Renamed" + && updateSecondaryPropertyResponsePDF1 == "Updated" + && updateSecondaryPropertyResponsePDF2 == "Updated" + && updateSecondaryPropertyResponsePDF3 == "Updated" + && updateSecondaryPropertyResponsePDF4 == "Updated") { + System.out.println("Renamed & updated Secondary properties for attachment PDF"); + attachment1Updated = true; + } + + System.out.println("Updating secondary properties for attachment TXT"); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); + if (updateSecondaryPropertyResponseTXT1 == "Updated") { + System.out.println("Updated Secondary properties for attachment TXT"); + attachment2Updated = true; + } + Integer secondaryPropertyInt3 = 1234; + LocalDateTime secondaryPropertyDateTime3 = LocalDateTime.now(); + System.out.println("Updating secondary properties for attachment EXE"); + // Update secondary properties for String + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); + // Update secondary properties for Integer + RequestBody bodyInt3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); + // Update secondary properties for DateTime + RequestBody bodyDateTime3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime3 + "\"\n}")); + String updateSecondaryPropertyResponseEXE3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDateTime3); + + if (updateSecondaryPropertyResponseEXE1 == "Updated" + && updateSecondaryPropertyResponseEXE2 == "Updated" + && updateSecondaryPropertyResponseEXE3 == "Updated") { + System.out.println("Updated Secondary properties for attachment EXE"); + attachment3Updated = true; + } + + if (attachment1Updated && attachment2Updated && attachment3Updated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation per attachment --- + // Attachment 1 (PDF, renamed to sample1234.pdf) + String cmisName1 = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals(name1, cmisName1, "PDF should be renamed in CMIS"); + String cmisInt1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisInt1, + "PDF DocumentInfoRecordInt should match in CMIS"); + String cmisBool1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool1, "PDF DocumentInfoRecordBoolean should be true"); + String cmisDate1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate1, "PDF DocumentInfoRecordDate should be set"); + + // Attachment 2 (TXT, only Boolean set) + String cmisBool2 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool2, "TXT DocumentInfoRecordBoolean should be true"); + + // Attachment 3 (EXE, String + Int + DateTime) + String cmisString3 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull(cmisString3, "EXE DocumentInfoRecordString should be set"); + String cmisInt3 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt3), + cmisInt3, + "EXE DocumentInfoRecordInt should match"); + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachments"); + } + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(28) + void testUpdateValidSecondaryProperty_afterEntityIsSaved_multipleAttachments() { + System.out.println( + "Test (28): Rename & Update valid secondary properties for multiple attachments after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response == "Entity in draft mode") { + Boolean attachment1Updated = false; + Boolean attachment2Updated = false; + Boolean attachment3Updated = false; + + String name1 = "sample1.pdf"; + Integer secondaryPropertyInt1 = 12; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + System.out.println("Renaming and updating secondary properties for attachment PDF"); + String responsePDF1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue1 = integrationTestUtils.getDropDownValue(); + String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); + String updateSecondaryPropertyResponsePDF1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown1); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponsePDF2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponsePDF3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponsePDF4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + + if (responsePDF1 == "Renamed" + && updateSecondaryPropertyResponsePDF1 == "Updated" + && updateSecondaryPropertyResponsePDF2 == "Updated" + && updateSecondaryPropertyResponsePDF3 == "Updated" + && updateSecondaryPropertyResponsePDF4 == "Updated") { + System.out.println("Renamed & updated Secondary properties for attachment PDF"); + attachment1Updated = true; + } + + System.out.println("Updating secondary properties for attachment TXT"); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); + if (updateSecondaryPropertyResponseTXT1 == "Updated") { + System.out.println("Updated Secondary properties for attachment TXT"); + attachment2Updated = true; + } + + Integer secondaryPropertyInt3 = 123; + LocalDateTime secondaryPropertyDateTime3 = LocalDateTime.now(); + System.out.println("Updating secondary properties for attachment EXE"); + // Update secondary properties for String + String dropdownValue2 = integrationTestUtils.getDropDownValue(); + String jsonDropdown2 = "{ \"customProperty1_code\" : \"" + dropdownValue2 + "\" }"; + RequestBody bodyDropdown2 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown2); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown2); + // Update secondary properties for Integer + RequestBody bodyInt3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); + // Update secondary properties for DateTime + RequestBody bodyDateTime3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime3 + "\"\n}")); + String updateSecondaryPropertyResponseEXE3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDateTime3); + + if (updateSecondaryPropertyResponseEXE1 == "Updated" + && updateSecondaryPropertyResponseEXE2 == "Updated" + && updateSecondaryPropertyResponseEXE3 == "Updated") { + System.out.println("Updated Secondary properties for attachment EXE"); + attachment3Updated = true; + } + + if (attachment1Updated && attachment2Updated && attachment3Updated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response.equals("Saved")) { + System.out.println("Entity saved"); + // --- CMIS backend validation per attachment --- + // Attachment 1 (PDF, renamed to sample1.pdf) + String cmisName1 = CmisDocumentHelper.getCmisProperty(entityID3, name1, "cmis:name"); + assertEquals(name1, cmisName1, "PDF should be renamed in CMIS"); + String cmisInt1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt1), + cmisInt1, + "PDF DocumentInfoRecordInt should match in CMIS"); + String cmisBool1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool1, "PDF DocumentInfoRecordBoolean should be true"); + String cmisDate1 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, name1, "Working:DocumentInfoRecordDate"); + assertNotNull(cmisDate1, "PDF DocumentInfoRecordDate should be set"); + + // Attachment 2 (TXT, only Boolean set) + String cmisBool2 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBool2, "TXT DocumentInfoRecordBoolean should be true"); + + // Attachment 3 (EXE, String + Int) + String cmisString3 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull(cmisString3, "EXE DocumentInfoRecordString should be set"); + String cmisInt3 = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt3), + cmisInt3, + "EXE DocumentInfoRecordInt should match"); + testStatus = true; + System.out.println("Renamed & updated Secondary properties for attachments"); + } + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + if (!testStatus) { + fail("Could not update secondary property after entity is saved"); + } + } + + @Test + @Order(29) + void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (29): Rename & Update invalid and valid secondary properties for multiple attachments before entity is saved"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID3 = response; + + System.out.println("Entity created"); + + System.out.println("Creating attachment PDF"); + ClassLoader classLoader = getClass().getClassLoader(); + + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID3); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData1, file); + if (createResponse1.get(0).equals("Attachment created")) { + attachmentID1 = createResponse1.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment TXT"); + file = new File(classLoader.getResource("sample.txt").getFile()); + Map postData2 = new HashMap<>(); + postData2.put("up__ID", entityID3); + postData2.put("mimeType", "application/txt"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + List createResponse2 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData2, file); + if (createResponse2.get(0).equals("Attachment created")) { + attachmentID2 = createResponse2.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment EXE"); + file = new File(classLoader.getResource("sample.exe").getFile()); + Map postData3 = new HashMap<>(); + postData3.put("up__ID", entityID3); + postData3.put("mimeType", "application/exe"); + postData3.put("createdAt", new Date().toString()); + postData3.put("createdBy", "test@test.com"); + postData3.put("modifiedBy", "test@test.com"); + + List createResponse3 = + api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData3, file); + if (createResponse3.get(0).equals("Attachment created")) { + attachmentID3 = createResponse3.get(1); + System.out.println("Attachment created"); + } + + String check1 = createResponse1.get(0); + String check2 = createResponse2.get(0); + String check3 = createResponse3.get(0); + if (check1.equals("Attachment created") + && check2.equals("Attachment created") + && check3.equals("Attachment created")) { + Boolean attachment1Updated = false; + Boolean attachment2Updated = false; + Boolean attachment3Updated = false; + + String name1 = "sample1234.pdf"; + Integer secondaryPropertyInt1 = 1234; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + String invalidPropertyPDF = "testidinvalidPDF"; + System.out.println("Renaming and updating invalid secondary properties for attachment PDF"); + String responsePDF1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponsePDF1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyint = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponsePDF2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyint); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponsePDF3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponsePDF4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + // Update invalid secondary property + String updateSecondaryPropertyResponsePDF5 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, invalidPropertyPDF); + if (responsePDF1 == "Renamed" + && updateSecondaryPropertyResponsePDF1 == "Updated" + && updateSecondaryPropertyResponsePDF2 == "Updated" + && updateSecondaryPropertyResponsePDF3 == "Updated" + && updateSecondaryPropertyResponsePDF4 == "Updated" + && updateSecondaryPropertyResponsePDF5 == "Updated") { + attachment1Updated = true; + } + + System.out.println("Updating valid secondary properties for attachment TXT"); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); + if (updateSecondaryPropertyResponseTXT1 == "Updated") { + System.out.println("Updated Secondary properties for attachment TXT"); + attachment2Updated = true; + } + + Integer secondaryPropertyInt3 = 1234; + System.out.println("Updating valid secondary properties for attachment EXE"); + + // Update secondary properties for String + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); + // Update secondary properties for Integer + RequestBody bodyInt3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); + + if (updateSecondaryPropertyResponseEXE1 == "Updated" + && updateSecondaryPropertyResponseEXE2 == "Updated") { + System.out.println("Updated Secondary properties for attachment EXE"); + attachment3Updated = true; + } + + if (attachment1Updated && attachment2Updated && attachment3Updated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + Map attachmentMetadataPDF = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); + assertEquals("sample.pdf", attachmentMetadataPDF.get("fileName")); + assertNull(attachmentMetadataPDF.get("customProperty3")); + assertNull(attachmentMetadataPDF.get("customProperty4")); + assertNull(attachmentMetadataPDF.get("customProperty1_code")); + assertNull(attachmentMetadataPDF.get("customProperty2")); + assertNull(attachmentMetadataPDF.get("customProperty6")); + assertNull(attachmentMetadataPDF.get("customProperty5")); + + Map attachmentMetadataTXT = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID2); + assertEquals("sample.txt", attachmentMetadataTXT.get("fileName")); + assertNull(attachmentMetadataTXT.get("customProperty3")); + assertNull(attachmentMetadataTXT.get("customProperty4")); + assertNull(attachmentMetadataTXT.get("customProperty1_code")); + assertNull(attachmentMetadataTXT.get("customProperty2")); + assertTrue((Boolean) attachmentMetadataTXT.get("customProperty6")); + assertNull(attachmentMetadataTXT.get("customProperty5")); + + Map attachmentMetadataEXE = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID3); + assertEquals("sample.exe", attachmentMetadataEXE.get("fileName")); + assertNull(attachmentMetadataEXE.get("customProperty3")); + assertNull(attachmentMetadataEXE.get("customProperty4")); + assertEquals(dropdownValue, attachmentMetadataEXE.get("customProperty1_code")); + assertEquals(1234, attachmentMetadataEXE.get("customProperty2")); + + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation --- + // Attachment 1 (PDF, invalid prop) — should NOT have changes in CMIS + String cmisNamePdf = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals("sample.pdf", cmisNamePdf, "PDF filename should NOT be changed in CMIS"); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist in CMIS for PDF"); + + // Attachment 2 (TXT, valid prop) — Boolean should be set + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals("true", cmisBoolTxt, "TXT DocumentInfoRecordBoolean should be true"); + + // Attachment 3 (EXE, valid props) — String + Int should be set + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull(cmisStringExe, "EXE DocumentInfoRecordString should be set"); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt3), + cmisIntExe, + "EXE DocumentInfoRecordInt should match"); + testStatus = true; + System.out.println( + "Rename & update unsuccessfull for invalid Secondary properties and successfull for valid property attachments"); + } + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(30) + void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_multipleAttachments() + throws IOException { + System.out.println( + "Test (30): Rename & Update invalid and valid secondary properties for multiple attachments after entity is saved"); + System.out.println("Editing entity"); + Boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); + if (response == "Entity in draft mode") { + Boolean attachment1Updated = false; + Boolean attachment2Updated = false; + Boolean attachment3Updated = false; + + String name1 = "sample.pdf"; + Integer secondaryPropertyInt1 = 12; + LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); + String invalidPropertyPDF = "testidinvalidPDF"; + System.out.println("Renaming and updating invalid secondary properties for attachment PDF"); + String responsePDF1 = + api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); + // Update secondary properties for String + String dropdownValue = integrationTestUtils.getDropDownValue(); + String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; + RequestBody bodyDropdown = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponsePDF1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); + // Update secondary properties for Integer + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); + String updateSecondaryPropertyResponsePDF2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); + // Update secondary properties for DateTime + RequestBody bodyDateTime = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); + String updateSecondaryPropertyResponsePDF3 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); + // Update secondary properties for Boolean + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponsePDF4 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); + // Update invalid secondary property + String updateSecondaryPropertyResponsePDF5 = + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID1, invalidPropertyPDF); + if (responsePDF1 == "Renamed" + && updateSecondaryPropertyResponsePDF1 == "Updated" + && updateSecondaryPropertyResponsePDF2 == "Updated" + && updateSecondaryPropertyResponsePDF3 == "Updated" + && updateSecondaryPropertyResponsePDF4 == "Updated" + && updateSecondaryPropertyResponsePDF5 == "Updated") { + attachment1Updated = true; + } + + System.out.println("Updating valid secondary properties for attachment TXT"); + // Update secondary properties for Boolean + RequestBody bodyBool = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + false + "\n}")); + String updateSecondaryPropertyResponseTXT1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); + if (updateSecondaryPropertyResponseTXT1 == "Updated") { + System.out.println("Updated Secondary properties for attachment TXT"); + attachment2Updated = true; + } + + Integer secondaryPropertyInt3 = 12; + System.out.println("Updating valid secondary properties for attachment EXE"); + + // Update secondary properties for String + RequestBody bodyDropdown1 = + RequestBody.create(MediaType.parse("application/json"), jsonDropdown); + String updateSecondaryPropertyResponseEXE1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); + // Update secondary properties for Integer + RequestBody bodyInt3 = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8( + "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); + String updateSecondaryPropertyResponseEXE2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); + + if (updateSecondaryPropertyResponseEXE1 == "Updated" + && updateSecondaryPropertyResponseEXE2 == "Updated") { + System.out.println("Updated Secondary properties for attachment EXE"); + attachment3Updated = true; + } + + if (attachment1Updated && attachment2Updated && attachment3Updated) { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); + Map attachmentMetadataPDF = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); + assertEquals("sample.pdf", attachmentMetadataPDF.get("fileName")); + assertNull(attachmentMetadataPDF.get("customProperty3")); + assertNull(attachmentMetadataPDF.get("customProperty4")); + assertNull(attachmentMetadataPDF.get("customProperty1_code")); + assertNull(attachmentMetadataPDF.get("customProperty2")); + assertNull(attachmentMetadataPDF.get("customProperty6")); + assertNull(attachmentMetadataPDF.get("customProperty5")); + + Map attachmentMetadataTXT = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID2); + assertEquals("sample.txt", attachmentMetadataTXT.get("fileName")); + assertNull(attachmentMetadataTXT.get("customProperty3")); + assertNull(attachmentMetadataTXT.get("customProperty4")); + assertNull(attachmentMetadataTXT.get("customProperty1_code")); + assertNull(attachmentMetadataTXT.get("customProperty2")); + assertFalse((Boolean) attachmentMetadataTXT.get("customProperty6")); + assertNull(attachmentMetadataTXT.get("customProperty5")); + + Map attachmentMetadataEXE = + api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID3); + assertEquals("sample.exe", attachmentMetadataEXE.get("fileName")); + assertNull(attachmentMetadataEXE.get("customProperty3")); + assertNull(attachmentMetadataEXE.get("customProperty4")); + assertEquals(dropdownValue, attachmentMetadataEXE.get("customProperty1_code")); + assertEquals(12, attachmentMetadataEXE.get("customProperty2")); + + String expectedResponse = + "[{\"code\":\"\",\"message\":\"The following secondary properties are not supported.\\n" + + // + "\\n" + + // + "\\t\\u2022 id1\\n" + + // + "\\n" + + // + "Please contact your administrator for assistance with any necessary adjustments.\\n" + + // + "\\n" + + // + "Table: attachments\\n" + + // + "Page: IntegrationTestEntity\",\"numericSeverity\":3}]"; + if (response.equals(expectedResponse)) { + System.out.println("Entity saved"); + // --- CMIS backend validation --- + // Attachment 1 (PDF, invalid prop) — should NOT have changes in CMIS + String cmisNamePdf = + CmisDocumentHelper.getCmisProperty(entityID3, "sample.pdf", "cmis:name"); + assertEquals("sample.pdf", cmisNamePdf, "PDF filename should NOT be changed in CMIS"); + String cmisId1 = + CmisDocumentHelper.getCmisPropertyOrNull(entityID3, "sample.pdf", "abc:myId1"); + assertNull(cmisId1, "Invalid property abc:myId1 should not exist in CMIS for PDF"); + + // Attachment 2 (TXT, valid prop) — Boolean should be set + String cmisBoolTxt = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.txt", "Working:DocumentInfoRecordBoolean"); + assertEquals("false", cmisBoolTxt, "TXT DocumentInfoRecordBoolean should be false"); + + // Attachment 3 (EXE, valid props) — String + Int should be set + String cmisStringExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordString"); + assertNotNull(cmisStringExe, "EXE DocumentInfoRecordString should be set"); + String cmisIntExe = + CmisDocumentHelper.getCmisPropertyOrNull( + entityID3, "sample.exe", "Working:DocumentInfoRecordInt"); + assertEquals( + String.valueOf(secondaryPropertyInt3), + cmisIntExe, + "EXE DocumentInfoRecordInt should match"); + testStatus = true; + System.out.println( + "Rename & update unsuccessfull for invalid Secondary properties and successfull for valid property attachments"); + } + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } + } + } + if (!testStatus) { + fail("Could not update secondary property before entity is saved"); + } + } + + @Test + @Order(31) + void testNAttachments_NewEntity() throws IOException { + System.out.println( + "Test (31): Creating new entity and checking only max 4 attachments are allowed to be uploaded"); + System.out.println("Creating entity"); + Boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response != "Could not create entity") { + entityID4 = response; + + System.out.println("Entity created"); + + System.out.println("Creating attachment PDF"); + ClassLoader classLoader = getClass().getClassLoader(); + + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID4); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse1 = + api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData1, file); + if (createResponse1.get(0).equals("Attachment created")) { + attachmentID1 = createResponse1.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment TXT"); + file = new File(classLoader.getResource("sample.txt").getFile()); + Map postData2 = new HashMap<>(); + postData2.put("up__ID", entityID4); + postData2.put("mimeType", "application/txt"); + postData2.put("createdAt", new Date().toString()); + postData2.put("createdBy", "test@test.com"); + postData2.put("modifiedBy", "test@test.com"); + + List createResponse2 = + api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData2, file); + if (createResponse2.get(0).equals("Attachment created")) { + attachmentID2 = createResponse2.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating attachment EXE"); + file = new File(classLoader.getResource("sample.exe").getFile()); + Map postData3 = new HashMap<>(); + postData3.put("up__ID", entityID4); + postData3.put("mimeType", "application/exe"); + postData3.put("createdAt", new Date().toString()); + postData3.put("createdBy", "test@test.com"); + postData3.put("modifiedBy", "test@test.com"); + + List createResponse3 = + api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, file); + if (createResponse3.get(0).equals("Attachment created")) { + attachmentID3 = createResponse3.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating second attachment pdf"); + file = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postData4 = new HashMap<>(); + postData4.put("up__ID", entityID4); + postData4.put("mimeType", "application/pdf"); + postData4.put("createdAt", new Date().toString()); + postData4.put("createdBy", "test@test.com"); + postData4.put("modifiedBy", "test@test.com"); + + List createResponse4 = + api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, file); + if (createResponse4.get(0).equals("Attachment created")) { + attachmentID4 = createResponse4.get(1); + System.out.println("Attachment created"); + } + + System.out.println("Creating third attachment pdf"); + file = new File(classLoader.getResource("sample2.pdf").getFile()); + Map postData5 = new HashMap<>(); + postData5.put("up__ID", entityID4); + postData5.put("mimeType", "application/pdf"); + postData5.put("createdAt", new Date().toString()); + postData5.put("createdBy", "test@test.com"); + postData5.put("modifiedBy", "test@test.com"); + + List createResponse5 = + api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, file); + if (createResponse5.get(0).equals("Only 4 attachments allowed.")) { + testStatus = true; + attachmentID5 = createResponse5.get(1); + System.out.println("Expected error received: Only 4 attachments allowed."); + } + String check = createResponse5.get(0); + if (check.equals("Attachment created")) { + testStatus = false; + } else { + response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID4); + if (response.equals("Saved")) { + String expectedJson = + "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 attachments.\"}}"; + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode actualJsonNode = objectMapper.readTree(check); + JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); + if (expectedJsonNode.equals(actualJsonNode)) { + testStatus = true; + } + } + } + } + if (!testStatus) { + fail("Attachment was created"); + } + } + + @Test + @Order(32) + void testUploadNAttachments() throws IOException { + System.out.println("Test (32): Upload maximum 4 attachments in an exsisting entity"); + + ClassLoader classLoader = getClass().getClassLoader(); + File originalFile = new File(classLoader.getResource("sample.exe").getFile()); + + boolean testStatus = false; + String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID4); + System.out.println("response: " + response); + + if ("Entity in draft mode".equals(response)) { + for (int i = 1; i <= 5; i++) { + // Ensure only one file is uploaded at a time and complete before next + File tempFile = File.createTempFile("sample_" + i + "_", ".exe"); + Files.copy(originalFile.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + Map postData = new HashMap<>(); + postData.put("up__ID", entityID4); + postData.put("mimeType", "application/exe"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, entityID4, srvpath, postData, tempFile); + + String resultMessage = createResponse.get(0); + System.out.println("Result message for attachment " + i + ": " + resultMessage); + + String expectedResponse = + "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 attachments.\"}}"; + if (resultMessage.equals(expectedResponse)) { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode actualJsonNode = objectMapper.readTree(resultMessage); + JsonNode expectedJsonNode = objectMapper.readTree(expectedResponse); + if (expectedJsonNode.equals(actualJsonNode)) { + testStatus = true; + } + } else { + testStatus = false; + } + tempFile.delete(); + } + if (!testStatus) { + fail("5th attachment did not trigger the expected error."); + } + // Delete the newly created entity + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID4); + if (deleteEntityResponse != "Entity Deleted") { + fail("Could not delete entity"); + } else { + System.out.println("Successfully deleted the test entity4"); + } + } + } + + @Test + @Order(33) + void testDiscardDraftWithoutAttachments() { + System.out.println("Test (33) : Discard draft without adding attachments"); + + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + } + + response = api.deleteEntityDraft(appUrl, entityName, response); + if (!response.equals("Entity Draft Deleted")) { + fail("Draft was not discarded properly"); + } + } + + @Test + @Order(34) + void testDiscardDraftWithAttachments() throws IOException { + System.out.println("Test (34) : Discard draft with attachments"); + boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!response.equals("Could not create entity")) { + entityID7 = response; + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + + Map postData1 = new HashMap<>(); + postData1.put("up__ID", entityID7); + postData1.put("mimeType", "application/pdf"); + postData1.put("createdAt", new Date().toString()); + postData1.put("createdBy", "test@test.com"); + postData1.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, entityID7, srvpath, postData1, file); + if (createResponse.get(0).equals("Attachment created")) { + attachmentID1 = createResponse.get(1); + } + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + response = api.deleteEntityDraft(appUrl, entityName, entityID7); + } + if (response.equals("Entity Draft Deleted")) { + testStatus = true; + } + } + if (!testStatus) { + fail("Draft was not discarded properly"); + } + } + + @Test + @Order(35) + void testCopyAttachmentsSuccessNewEntity() throws IOException { + System.out.println("Test (35): Copy attachments from one entity to another new entity"); + List attachments = new ArrayList<>(); + copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!copyAttachmentSourceEntity.equals("Could not create entity") + && !copyAttachmentTargetEntity.equals("Could not create entity")) { + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample1.pdf").getFile())); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (String attachment : attachments) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadata( + appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } + + if (sourceObjectIds.size() == 2) { + String copyResponse; + copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); + if (copyResponse.equals("Attachments copied successfully")) { + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not create entities"); + } + } + + @Test + @Order(36) + void testCopyAttachmentsUnsuccessfulNewEntity() throws IOException { + System.out.println("Test (36): Copy attachments from one entity to another new entity"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + copyAttachmentTargetEntityEmpty = + api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (editResponse1.equals("Entity in draft mode") + && !copyAttachmentTargetEntityEmpty.equals("Could not create entity")) { + sourceObjectIds.add("incorrectObjectId"); + if (sourceObjectIds.size() == 3) { + try { + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntityEmpty, sourceObjectIds); + fail("Copy attachments did not throw an error"); + } catch (IOException e) { + String saveEntityResponse1 = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String saveEntityResponse2 = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntityEmpty); + String deleteResponse = + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntityEmpty); + if (!saveEntityResponse1.equals("Saved") + || !saveEntityResponse2.equals("Saved") + || !deleteResponse.equals("Entity Deleted")) { + fail("Could not save entities"); + } + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } + + @Test + @Order(37) + void testCopyAttachmentWithNotesField() throws IOException { + System.out.println( + "Test (37): Create entity with attachment containing notes, copy to new entity and verify notes field"); + Boolean testStatus = false; + // Create source entity + copyCustomSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyCustomSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + // Create and upload attachment to source entity + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment"); + } + + String sourceAttachmentId = createResponse.get(1); + + // Update attachment with notes field + String notesValue = "This is a test note for copy attachment verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateBody = RequestBody.create(jsonPayload, mediaType); + + String updateResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, updateBody); + + if (!updateResponse.equals("Updated")) { + fail("Could not update attachment notes field"); + } + + // Save source entity + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity"); + } + + // Fetch attachment metadata to get objectId + Map sourceAttachmentMetadata = + api.fetchMetadata( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId"); + } + + // Store objectId in array + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + if (sourceObjectIds.isEmpty()) { + sourceObjectIds.add(sourceObjectId); + } else { + sourceObjectIds.set(0, sourceObjectId); + } + + String sourceNoteValue = + sourceAttachmentMetadata.get("note") != null + ? sourceAttachmentMetadata.get("note").toString() + : null; + + if (!notesValue.equals(sourceNoteValue)) { + fail( + "Notes field was not properly set in source attachment. Expected: " + + notesValue + + ", Got: " + + sourceNoteValue); + } + + // Create target entity + copyCustomTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyCustomTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } + + // Copy attachment to target entity + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(0)); // Use objectId from array + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity: " + copyResponse); + } + + // Save target entity + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity"); + } + + // Fetch target entity attachments metadata + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + + if (targetAttachmentsMetadata.isEmpty()) { + fail("No attachments found in target entity"); + } + + // Verify the copied attachment has the same notes value + Map copiedAttachmentMetadata = targetAttachmentsMetadata.get(0); + String copiedNoteValue = + copiedAttachmentMetadata.get("note") != null + ? copiedAttachmentMetadata.get("note").toString() + : null; + + if (!notesValue.equals(copiedNoteValue)) { + fail( + "Notes field was not properly copied. Expected: " + + notesValue + + ", Got: " + + copiedNoteValue); + } + + // Verify attachment content can be read from target entity + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + + if (readResponse.equals("OK")) { + testStatus = true; + } + if (!testStatus) { + fail("Could not verify that notes field was copied from source to target attachment"); + } + } + + @Test + @Order(38) + void testCopyAttachmentWithSecondaryPropertiesField() throws IOException { + System.out.println( + "Test (38): Verify that secondary properties are preserved when copying attachments between entities"); + Boolean testStatus = false; + + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample1.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment"); + } + + String sourceAttachmentId = createResponse.get(1); + + // Update attachment with secondary properties + // DocumentInfoRecordBoolean : Set to true + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyBoolean); + + if (!updateSecondaryPropertyResponse1.equals("Updated")) { + fail( + "Could not update attachment DocumentInfoRecordBoolean field. Response: " + + updateSecondaryPropertyResponse1); + } + + // customProperty2 : Set to 12345 + Integer customProperty2Value = 12345; + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); + + if (!updateSecondaryPropertyResponse2.equals("Updated")) { + fail( + "Could not update attachment customProperty2 field. Response: " + + updateSecondaryPropertyResponse2); + } + + // Save source entity + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity. Response: " + saveSourceResponse); + } + + // Fetch attachment metadata to get objectId and verify secondary properties + Map sourceAttachmentMetadata = + api.fetchMetadata( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId"); + } + + // Store objectId in array for reuse + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + if (sourceObjectIds.size() < 2) { + sourceObjectIds.add(sourceObjectId); + } else { + sourceObjectIds.set(1, sourceObjectId); + } + + // Verify all secondary properties in source attachment + Boolean sourceCustomProperty6 = + sourceAttachmentMetadata.get("customProperty6") != null + ? (Boolean) sourceAttachmentMetadata.get("customProperty6") + : null; + Integer sourceCustomProperty2 = + sourceAttachmentMetadata.get("customProperty2") != null + ? (Integer) sourceAttachmentMetadata.get("customProperty2") + : null; + + if (sourceCustomProperty6 == null || !sourceCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly set in source attachment. Expected: true, Got: " + + sourceCustomProperty6); + } + + if (!customProperty2Value.equals(sourceCustomProperty2)) { + fail( + "customProperty2 was not properly set in source attachment. Expected: " + + customProperty2Value + + ", Got: " + + sourceCustomProperty2); + } + + String editTargetResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!editTargetResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity"); + } + + // Copy attachment to target entity + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(1)); // Use objectId from array + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity: " + copyResponse); + } + + // Save target entity + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity"); + } + + // Fetch target entity attachments metadata + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + + if (targetAttachmentsMetadata.isEmpty()) { + fail("No attachments found in target entity"); + } + + // Verify the copied attachment has the same secondary properties + // Find the attachment we just copied by matching the filename + Map copiedAttachmentMetadata = + targetAttachmentsMetadata.stream() + .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); + + if (copiedAttachmentMetadata == null) { + fail("Could not find the copied attachment with file in target entity"); + } + + Boolean copiedCustomProperty6 = + copiedAttachmentMetadata.get("customProperty6") != null + ? (Boolean) copiedAttachmentMetadata.get("customProperty6") + : null; + Integer copiedCustomProperty2 = + copiedAttachmentMetadata.get("customProperty2") != null + ? (Integer) copiedAttachmentMetadata.get("customProperty2") + : null; + + // Verify DocumentInfoRecordBoolean + if (copiedCustomProperty6 == null || !copiedCustomProperty6) { + fail( + "DocumentInfoRecordBoolean as not properly copied. Expected: true, Got: " + + copiedCustomProperty6); + } + + // Verify customProperty2 + if (!customProperty2Value.equals(copiedCustomProperty2)) { + fail( + "customProperty2 was not properly copied. Expected: " + + customProperty2Value + + ", Got: " + + copiedCustomProperty2); + } + + // Verify attachment content can be read from target entity + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + + if (readResponse.equals("OK")) { + testStatus = true; + } + if (!testStatus) { + fail( + "Could not verify that all secondary properties were copied from source to target attachment"); + } + } + + @Test + @Order(39) + void testCopyAttachmentWithNotesAndSecondaryPropertiesField() throws IOException { + System.out.println( + "Test (39): Verify that both notes field and secondary properties are preserved during attachment copy"); + Boolean testStatus = false; + + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity"); + } + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample2.pdf").getFile()); + + Map postData = new HashMap<>(); + postData.put("up__ID", copyCustomSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment"); + } + + String sourceAttachmentId = createResponse.get(1); + + // Update attachment with notes field + String notesValue = "This attachment has both notes and secondary properties for testing"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + copyCustomSourceEntity, + sourceAttachmentId, + updateNotesBody); + + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update attachment notes field"); + } + + // Update attachment with secondary properties + // DocumentInfoRecordBoolean : Set to true + RequestBody bodyBoolean = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); + String updateSecondaryPropertyResponse1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyBoolean); + + if (!updateSecondaryPropertyResponse1.equals("Updated")) { + fail( + "Could not update attachment DocumentInfoRecordBoolean (customProperty6) field. Response: " + + updateSecondaryPropertyResponse1); + } + + // customProperty2 : Set to 99999 + Integer customProperty2Value = 99999; + RequestBody bodyInt = + RequestBody.create( + MediaType.parse("application/json"), + ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); + String updateSecondaryPropertyResponse2 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); + + if (!updateSecondaryPropertyResponse2.equals("Updated")) { + fail( + "Could not update attachment customProperty2 field. Response: " + + updateSecondaryPropertyResponse2); + } + + // Save source entity + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity. Response: " + saveSourceResponse); + } + + // Fetch attachment metadata to get objectId and verify notes and secondary properties + Map sourceAttachmentMetadata = + api.fetchMetadata( + appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + + if (!sourceAttachmentMetadata.containsKey("objectId")) { + fail("Source attachment metadata does not contain objectId"); + } + + String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); + if (sourceObjectIds.size() < 3) { + sourceObjectIds.add(sourceObjectId); + } else { + sourceObjectIds.set(2, sourceObjectId); + } + + String sourceNoteValue = + sourceAttachmentMetadata.get("note") != null + ? sourceAttachmentMetadata.get("note").toString() + : null; + + if (!notesValue.equals(sourceNoteValue)) { + fail( + "Notes field was not properly set in source attachment. Expected: " + + notesValue + + ", Got: " + + sourceNoteValue); + } + + Boolean sourceCustomProperty6 = + sourceAttachmentMetadata.get("customProperty6") != null + ? (Boolean) sourceAttachmentMetadata.get("customProperty6") + : null; + Integer sourceCustomProperty2 = + sourceAttachmentMetadata.get("customProperty2") != null + ? (Integer) sourceAttachmentMetadata.get("customProperty2") + : null; + + if (sourceCustomProperty6 == null || !sourceCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly set in source attachment. Expected: true, Got: " + + sourceCustomProperty6); + } + + if (!customProperty2Value.equals(sourceCustomProperty2)) { + fail( + "customProperty2 was not properly set in source attachment. Expected: " + + customProperty2Value + + ", Got: " + + sourceCustomProperty2); + } + + String editTargetResponse = + api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!editTargetResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity"); + } + + // Copy attachment to target entity + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectIds.get(2)); // Use objectId from array + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, objectIdsToCopy); + + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy attachment to target entity: " + copyResponse); + } + + // Save target entity + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity"); + } + + // Fetch target entity attachments metadata + List> targetAttachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + + if (targetAttachmentsMetadata.isEmpty()) { + fail("No attachments found in target entity"); + } + + // Verify the copied attachment has the same notes and secondary properties + // Find the attachment we just copied by matching the filename + Map copiedAttachmentMetadata = + targetAttachmentsMetadata.stream() + .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) + .findFirst() + .orElse(null); + + if (copiedAttachmentMetadata == null) { + fail("Could not find the copied attachment with fil in target entity"); + } + + // Verify notes field was copied + String copiedNoteValue = + copiedAttachmentMetadata.get("note") != null + ? copiedAttachmentMetadata.get("note").toString() + : null; + + if (!notesValue.equals(copiedNoteValue)) { + fail( + "Notes field was not properly copied. Expected: " + + notesValue + + ", Got: " + + copiedNoteValue); + } + + // Verify secondary properties were copied + Boolean copiedCustomProperty6 = + copiedAttachmentMetadata.get("customProperty6") != null + ? (Boolean) copiedAttachmentMetadata.get("customProperty6") + : null; + Integer copiedCustomProperty2 = + copiedAttachmentMetadata.get("customProperty2") != null + ? (Integer) copiedAttachmentMetadata.get("customProperty2") + : null; + + // Verify DocumentInfoRecordBoolean + if (copiedCustomProperty6 == null || !copiedCustomProperty6) { + fail( + "DocumentInfoRecordBoolean was not properly copied. Expected: true, Got: " + + copiedCustomProperty6); + } + + // Verify customProperty2 + if (!customProperty2Value.equals(copiedCustomProperty2)) { + fail( + "customProperty2 was not properly copied. Expected: " + + customProperty2Value + + ", Got: " + + copiedCustomProperty2); + } + + // Verify attachment content can be read from target entity + String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); + String readResponse = + api.readAttachment( + appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + + if (readResponse.equals("OK")) { + testStatus = true; + } + if (!testStatus) { + fail( + "Could not verify that notes field and all secondary properties were copied from source to target attachment"); + } + api.deleteEntity(appUrl, entityName, copyCustomSourceEntity); + api.deleteEntity(appUrl, entityName, copyCustomTargetEntity); + } + + @Test + @Order(40) + void testCopyAttachmentsSuccessExistingEntity() throws IOException { + System.out.println("Test (40): Copy attachments from one entity to another existing entity"); + List attachments = new ArrayList<>(); + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + File file1 = new File(classLoader.getResource("sample.pdf").getFile()); + File file2 = new File(classLoader.getResource("sample1.pdf").getFile()); + File tempFile1 = new File(System.getProperty("java.io.tmpdir"), "sample_copy_existing_1.pdf"); + Files.copy(file1.toPath(), tempFile1.toPath(), StandardCopyOption.REPLACE_EXISTING); + File tempFile2 = new File(System.getProperty("java.io.tmpdir"), "sample_copy_existing_2.pdf"); + Files.copy(file2.toPath(), tempFile2.toPath(), StandardCopyOption.REPLACE_EXISTING); + files.add(tempFile1); + files.add(tempFile2); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String editResponse2 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (editResponse1.equals("Entity in draft mode") + && editResponse2.equals("Entity in draft mode")) { + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (String attachment : attachments) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadata( + appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + + sourceObjectIds.clear(); + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } + + if (sourceObjectIds.size() == 2) { + String copyResponse; + copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); + if (copyResponse.equals("Attachments copied successfully")) { + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + if (targetAttachmentIds.size() == 4) { + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } + // api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); + // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } + + @Test + @Order(41) + void testCopyAttachmentsUnsuccessfulExistingEntity() throws IOException { + System.out.println( + "Test (41): Copy attachments from one entity to another existing entity - unsuccessful"); + String editResponse1 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + String editResponse2 = + api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (editResponse1.equals("Entity in draft mode") + && editResponse2.equals("Entity in draft mode")) { + sourceObjectIds.add("incorrectObjectId"); + if (sourceObjectIds.size() == 3) { + try { + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); + fail("Copy attachments did not throw an error"); + } catch (IOException e) { + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not edit entities"); + } + } + + @Test + @Order(42) + void testCreateLinkSuccess() throws IOException { + System.out.println("Test (42): Create link in entity"); + List attachments = new ArrayList<>(); + createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!createLinkEntity.equals("Could not create entity")) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse1 = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + String createLinkResponse2 = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName + "1", linkUrl); + if (createLinkResponse1.equals("Link created successfully") + && createLinkResponse2.equals("Link created successfully")) { + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (saveEntityResponse.equals("Saved")) { + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String openAttachmentResponse; + for (String attachment : attachments) { + openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, createLinkEntity, attachment); + System.out.println("openAttachmentResponse: " + openAttachmentResponse); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open created link"); + } + } + } else { + fail("Could not save entity"); + } + } else { + fail("Could not create link"); + } + } else { + fail("Could not create entity"); + } + } + + @Test + @Order(43) + void testCreateLinkDifferentEntity() throws IOException { + System.out.println("Test (43): Create link with same name in different entity"); + String createLinkDifferentEntity = + api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!createLinkDifferentEntity.equals("Could not edit entity")) { + String linkName = "sample"; + String linkUrl = "https://example.com"; + String createResponse = + api.createLink( + appUrl, entityName, facetName, createLinkDifferentEntity, linkName, linkUrl); + if (!createResponse.equals("Link created successfully")) { + fail("Could not create link in different entity with same name"); + } + String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkDifferentEntity); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } + response = api.deleteEntity(appUrl, entityName, createLinkDifferentEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } else { + fail("Could not edit entity"); + } + } + + @Test + @Order(44) + void testCreateLinkFailure() throws IOException { + System.out.println("Test (44): Create link fails due to invalid URL and name"); + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Could not edit entity")) { + String linkName = "sample"; + String linkUrl = "example.com"; + try { + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + fail("Create link did not throw an error for invalid url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("400018", errorCode); + assertTrue( + errorMessage.equals("Enter a value that is within the expected pattern.") + || errorMessage.equals("Enter a value that matches the expected pattern."), + "Unexpected error message: " + errorMessage); + } + try { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName + "//", "https://" + linkUrl); + fail("Create link did not throw an error for invalid name"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = + "\"sample//\" contains unsupported characters (‘/’ or ‘\\’). Rename and try again."; + assertEquals("500", errorCode); + assertEquals( + expected.replaceAll("\\s+", " ").trim(), errorMessage.replaceAll("\\s+", " ").trim()); + } + try { + api.createLink(appUrl, entityName, facetName, createLinkEntity, "", ""); + fail("Create link did not throw an error for empty name and url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = "Provide the missing value."; + assertEquals("409008", errorCode); + assertEquals(expected, errorMessage); + } + try { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName, "https://" + linkUrl); + fail("Create link did not throw an error for duplicate name"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals( + "An object named \"sample\" already exists. Rename the object and try again.", + errorMessage); + } + try { + for (int i = 2; i < 5; i++) { + api.createLink( + appUrl, entityName, facetName, createLinkEntity, linkName + i, "https://" + linkUrl); + } + fail("More than 5 links were created in the same entity"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals("Cannot upload more than 4 attachments.", errorMessage); + } + String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } + response = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } else { + fail("Could not edit entity"); + } + } + + @Test + @Order(45) + void testCreateLinkNoSDMRoles() throws IOException { + System.out.println("Test (45): Create link fails due to no SDM roles assigned"); + String createLinkEntityNoSDMRoles = + apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!createLinkEntityNoSDMRoles.equals("Could not edit entity")) { + String linkName = "sample27"; + String linkUrl = "https://example.com"; + try { + apiNoRoles.createLink( + appUrl, entityName, facetName, createLinkEntityNoSDMRoles, linkName, linkUrl); + fail("Link got created without SDM roles"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals( + "You do not have the required permissions to upload attachments. Please contact your administrator for access.", + errorMessage); + } + String response = + apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntityNoSDMRoles); + if (!response.equals("Saved")) { + fail("Could not save entity"); + } + response = api.deleteEntity(appUrl, entityName, createLinkEntityNoSDMRoles); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } else { + fail("Could not edit entity"); + } + } + + @Test + @Order(46) + void testDeleteLink() throws IOException { + System.out.println("Test (46): Delete link in entity"); + List attachments = new ArrayList<>(); + String createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!createLinkEntity.equals("Could not create entity")) { + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (createLinkResponse.equals("Link created successfully")) { + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (saveEntityResponse.equals("Saved")) { + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String editEntityResponse = + api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + String deleteLinkResponse = + api.deleteAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(0)); + if (!deleteLinkResponse.equals("Deleted")) { + fail("Could not delete created link"); + } else { + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (attachments.size() != 0) { + fail("Link wasn't deleted"); + } + String response = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!response.equals("Entity Deleted")) { + fail("Could not delete entity"); + } + } + } else { + fail("Could not save entity"); + } + } else { + fail("Could not create link"); + } + } else { + fail("Could not create entity"); + } + } + + @Test + @Order(47) + void testRenameLinkSuccess() throws IOException { + System.out.println("Test (47): Rename link in entity"); + List attachments = new ArrayList<>(); + + createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (createLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } + + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + + attachmentID9 = attachments.get(0); + String renameLinkResponse = + api.renameAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(0), "sampleRenamed"); + if (!renameLinkResponse.equals("Renamed")) fail("Could not Renamed created link"); + + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + } + + @Test + @Order(48) + void testRenameLinkDuplicate() throws IOException { + System.out.println("Test (48): Rename link in entity fails due to duplicate error"); + List attachments = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + + editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + .filter(item -> !attachmentID9.equals(item.get("ID"))) // skip unwanted filename + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + attachmentID10 = attachments.get(0); + api.renameAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(0), "sampleRenamed"); + + String saveError = + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + String expectedWarning = + "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sampleRenamed\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + ObjectMapper mapper = new ObjectMapper(); + assertEquals(mapper.readTree(expectedWarning), mapper.readTree(saveError)); + + String deleteEntityResponse = api.deleteEntityDraft(appUrl, entityName, createLinkEntity); + if (!deleteEntityResponse.equals("Entity Draft Deleted")) { + fail("Entity draft not deleted"); + } + } + + @Test + @Order(49) + void testRenameLinkUnsupportedCharacters() throws IOException { + System.out.println( + "Test (49): Rename link in entity fails due to unsupported characters in name"); + List attachments = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + + String linkName = "sample2"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() + // .filter(item -> "sample2".equals(item.get("filename"))) // skip unwanted filename + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + System.out.println("attachments: " + attachments); + + editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + + api.renameAttachment( + appUrl, entityName, facetName, createLinkEntity, attachments.get(0), "sampleRenamed//"); + String warning = + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); + String expectedWarning = + "{\"error\":{\"code\":\"400\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\"}}"; + ObjectMapper mapper = new ObjectMapper(); + assertEquals(mapper.readTree(expectedWarning), mapper.readTree(warning)); + + String deleteEntityResponse = api.deleteEntity(appUrl, entityName, createLinkEntity); + if (!deleteEntityResponse.equals("Entity Deleted")) { + fail("Entity draft not deleted"); + } + } + + @Test + @Order(50) + void testEditLinkSuccess() throws IOException { + System.out.println("Test (50): Edit existing link in entity"); + + List attachments = new ArrayList<>(); + editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (editLinkEntity.equals("Could not create entity")) { + fail("Could not create entity"); + } + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link"); + } + + String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (attachments.isEmpty()) { + fail("Could not edit link"); + } + String linkId = attachments.get(0); + String updatedUrl = "https://editedexample.com"; + String editLinkResponse = + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + if (!editLinkResponse.equals("Link edited successfully")) { + fail("Could not edit link"); + } + saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!saveEntityResponse.equals("Saved")) { + fail("Could not save entity"); + } + String openAttachmentResponse; + for (String attachment : attachments) { + openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, editLinkEntity, attachment); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open created link"); + } + } + } + + @Test + @Order(51) + void testEditLinkFailureInvalidURL() throws IOException { + System.out.println("Test (51): Edit existing link with invalid url"); + Boolean testStatus = false; + List attachments = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (attachments.isEmpty()) { + fail("Could not edit link"); + } + String linkId = attachments.get(0); + String updatedUrl = "https://editedexample"; + try { + + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + fail("Create link did not throw an error for invalid url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("400018", errorCode); + assertTrue( + errorMessage.equals("Enter a value that is within the expected pattern.") + || errorMessage.equals("Enter a value that matches the expected pattern."), + "Unexpected error message: " + errorMessage); + + testStatus = true; + } + api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!testStatus) { + fail("Could not edit link with an invalid URL"); + } + } + + @Test + @Order(52) + void testEditLinkFailureEmptyURL() throws IOException { + System.out.println("Test (52): Edit existing link with an empty url"); + Boolean testStatus = false; + List attachments = new ArrayList<>(); + + String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (attachments.isEmpty()) { + fail("Could not edit link"); + } + String linkId = attachments.get(0); + String updatedUrl = ""; + try { + api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + fail("edit link did not throw an error for empty url"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + String expected = "Provide the missing value."; + assertEquals("409008", errorCode); + assertEquals(expected, errorMessage); + testStatus = true; + } + api.deleteEntityDraft(appUrl, entityName, editLinkEntity); + if (!testStatus) { + fail("Could not edit link with an empty URL"); + } + } + + @Test + @Order(53) + void testEditLinkNoSDMRoles() throws IOException { + System.out.println("Test (53): Edit link fails due to no SDM roles assigned"); + + Boolean testStatus = false; + List attachments = new ArrayList<>(); + + String editEntityResponse = + apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); + if (!editEntityResponse.equals("Entity in draft mode")) { + fail("Could not edit entity"); + } + attachments = + apiNoRoles.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (attachments.isEmpty()) { + fail("Could not edit link"); + } + String linkId = attachments.get(0); + String updatedUrl = "https://www.example1.com"; + try { + apiNoRoles.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); + fail("Link got edited without SDM roles in facet: \" + facetName"); + } catch (IOException e) { + String message = e.getMessage(); + int jsonStart = message.indexOf("{"); + String jsonPart = message.substring(jsonStart); + JSONObject json = new JSONObject(jsonPart); + String errorCode = json.getJSONObject("error").getString("code"); + String errorMessage = json.getJSONObject("error").getString("message"); + assertEquals("500", errorCode); + assertEquals( + "You do not have the required permissions to update attachments. Kindly contact the admin", + errorMessage); + testStatus = true; + } + apiNoRoles.deleteEntity(appUrl, entityName, createLinkEntity); + if (!testStatus) { + fail("Link got edited without SDM roles"); + } + api.deleteEntity(appUrl, entityName, editLinkEntity); + } + + @Test + @Order(54) + void testCopyLinkSuccessNewEntity() throws IOException { + System.out.println("Test (54): Copy link from one entity to another new entity"); + List> attachmentsMetadata = new ArrayList<>(); + + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + + if (copyLinkSourceEntity.equals("Could not create entity") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not create source or target entity"); + } + + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link in source entity"); + } + + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + + List sourceObjectIds = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (sourceObjectIds.isEmpty()) { + fail("Could not fetch object Id for link"); + } + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy link: " + copyResponse); + } + + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveResponse.equals("Saved")) { + fail("Could not save target entity after copying link"); + } + + attachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); + + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch. Expected '" + + expectedType + + "' but got '" + + receivedType + + "'."); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); + + System.out.println("Attachment type and URL validated successfully."); + + String attachmentId = (String) copiedAttachment.get("ID"); + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open the attachment"); + } + + String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + if (!deleteSourceResponse.equals("Entity Deleted") + || !deleteTargetResponse.equals("Entity Deleted")) { + fail("could not delete source or target entity"); + } + } + + @Test + @Order(55) + void testCopyLinkUnsuccessfulNewEntity() throws IOException { + System.out.println( + "Test (55): Copy invalid type of link from one entity to another new entity"); + + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + + if (copyLinkSourceEntity.equals("Could not create entity") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not create source or target entity"); + } + + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + List invalidObjectIds = Collections.singletonList("incorrectObjectId"); + + try { + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, invalidObjectIds); + fail("Copy attachments did not throw error for invalid ID"); + } catch (IOException e) { + System.out.println("Caught expected error: " + e.getMessage()); + } + + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveResponse.equals("Saved")) { + fail("Could not save target entity after unsuccessful copy"); + } + + String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + if (!deleteSourceResponse.equals("Entity Deleted")) { + fail("Could not delete source entity"); + } + } + + @Test + @Order(56) + void testCopyLinkFromNewEntityToExistingEntity() throws IOException { + System.out.println("Test (56): Copy link from a new entity to an existing target entity"); + List> attachmentsMetadata = new ArrayList<>(); + + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyLinkSourceEntity.equals("Could not create entity")) { + fail("Could not create new source entity"); + } + + String linkName = "Sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link in new source entity"); + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save new source entity"); + } + + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + + List sourceObjectIds = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (sourceObjectIds.isEmpty()) { + fail("Could not fetch objectId from new source entity"); + } + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy link from new source entity to existing target entity: " + copyResponse); + } + + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity after copying link"); + } + + attachmentsMetadata = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); + + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch. Expected '" + + expectedType + + "' but got '" + + receivedType + + "'."); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); + + System.out.println("Attachment type and URL validated successfully."); + + String attachmentId = (String) copiedAttachment.get("ID"); + assertNotNull(attachmentId, "Could not find 'ID' in the copied attachment metadata."); + + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open the attachment"); + } + + String deleteResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + if (!deleteResponse.equals("Entity Deleted")) { + fail("Could not delete new source entity"); + } + } + + @Test + @Order(57) + void testCopyInvalidLinkFromNewEntityToExistingEntity() throws IOException { + System.out.println( + "Test (57): Copy invalid type of link from new entity to existing target entity"); + + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (copyLinkSourceEntity.equals("Could not create entity")) { + fail("Could not create new source entity"); + } + + String linkName = "Sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link in new source entity"); + } + + String saveSourceResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save new source entity"); + } + + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!editResponse.equals("Entity in draft mode")) { + fail("Could not edit target entity draft"); + } + + List invalidObjectIds = Collections.singletonList("invalidObjectId123"); + + try { + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, invalidObjectIds); + fail("Copy did not throw error for invalid link ID"); + } catch (IOException e) { + System.out.println("Caught expected error while copying invalid link: " + e.getMessage()); + } + + // No need to wait for upload completion as copy failed, but ensure clean state + String saveTargetResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity after unsuccessful copy"); + } + + String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); + String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + if (!deleteSourceResponse.equals("Entity Deleted") + || !deleteTargetResponse.equals("Entity Deleted")) { + fail("Could not delete new source entity or target entity"); + } + } + + @Test + @Order(58) + void testCopyLinkSuccessNewEntityDraft() throws IOException { + System.out.println("Test (58): Copy link from one entity to another new entity draft mode"); + + copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + + if (copyLinkSourceEntity.equals("Could not create entity") + || copyLinkTargetEntity.equals("Could not create entity")) { + fail("Could not create source or target entity"); + } + + String linkName = "sample"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); + if (!createLinkResponse.equals("Link created successfully")) { + fail("Could not create link in source entity"); + } + + List sourceObjectIds = + api.fetchEntityMetadataDraft(appUrl, entityName, facetName, copyLinkSourceEntity).stream() + .map(item -> (String) item.get("objectId")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (sourceObjectIds.isEmpty()) { + fail("Could not fetch object Id for link"); + } + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); + if (!copyResponse.equals("Attachments copied successfully")) { + fail("Could not copy link: " + copyResponse); + } + + List> attachmentsMetadata = new ArrayList<>(); + attachmentsMetadata = + api.fetchEntityMetadataDraft(appUrl, entityName, facetName, copyLinkTargetEntity); + Map copiedAttachment = attachmentsMetadata.get(0); + String receivedType = (String) copiedAttachment.get("type"); + String receivedUrl = (String) copiedAttachment.get("linkUrl"); + + String expectedType = "sap-icon://internet-browser"; + assertTrue( + expectedType.equalsIgnoreCase(receivedType), + "Attachment type mismatch. Expected '" + + expectedType + + "' but got '" + + receivedType + + "'."); + + assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); + + System.out.println("Attachment type and URL validated successfully."); + + String attachmentId = (String) copiedAttachment.get("ID"); + assertNotNull(attachmentId, "Could not find 'ID' in the copied attachment metadata."); + + String openAttachmentResponse = + api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); + if (!openAttachmentResponse.equals("Attachment opened successfully")) { + fail("Could not open the attachment"); + } - // @Test - // @Order(15) - // void testRenameSingleAttachmentDuplicate() { - // System.out.println("Test (15) : Rename single attachment duplicate"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String name = "sample123"; - // String name2 = "sample123456"; - // if (response == "Entity in draft mode") { - // response = api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, - // name); - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sample123\\\" already - // exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // if (response.equals(expected)) { - // response = - // api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID3, - // name2); - // if (response.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response.equals("Saved")) { - // testStatus = true; - // } - // } - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment was renamed"); - // } - // } + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + if (!saveResponse.equals("Saved")) { + fail("Could not save target entity after copying link"); + } + api.deleteEntityDraft(appUrl, entityName, copyLinkSourceEntity); + api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); + } - // @Test - // @Order(16) - // void testRenameMultipleAttachmentsWithOneUnsupportedCharacter() { - // System.out.println( - // "Test (16) : Rename multiple attachments where one name has unsupported characters"); - // Boolean testStatus = false; - - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - - // if (response.equals("Entity in draft mode")) { - // String validName1 = "valid_attachment1.pdf"; - // String invalidName2 = "invalid/attachment2.pdf"; - - // String renameResponse1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, - // validName1); - // String renameResponse2 = - // api.renameAttachment( - // appUrl, entityName, facetName, entityID, attachmentID2, invalidName2); - - // if (renameResponse1.equals("Renamed") && renameResponse2.equals("Renamed")) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"invalid/attachment2.pdf\\\" contains - // unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // if (response.equals(expected)) { - // api.renameAttachment( - // appUrl, entityName, facetName, entityID, attachmentID2, "sample1234"); - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if ("Saved".equals(response)) testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } + @Test + @Order(59) + void testCopyAttachmentsSuccessNewEntityDraft() throws IOException { + System.out.println( + "Test (59): Copy attachments from one entity to another new entity draft mode"); + List attachments = new ArrayList<>(); + copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (!copyAttachmentSourceEntity.equals("Could not create entity") + && !copyAttachmentTargetEntity.equals("Could not create entity")) { + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample1.pdf").getFile())); + Map postData = new HashMap<>(); + postData.put("up__ID", entityID7); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + sourceObjectIds.clear(); + + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + attachments.add(createResponse.get(1)); + } else { + fail("Could not create attachment"); + } + } - // if (!testStatus) { - // fail("Multiple renames should have failed due to one unsupported characters"); - // } - // } + List> attachmentsMetadata = new ArrayList<>(); + Map fetchAttachmentMetadataResponse; + for (String attachment : attachments) { + try { + fetchAttachmentMetadataResponse = + api.fetchMetadataDraft( + appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); + attachmentsMetadata.add(fetchAttachmentMetadataResponse); + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } + for (Map metadata : attachmentsMetadata) { + if (metadata.containsKey("objectId")) { + sourceObjectIds.add(metadata.get("objectId").toString()); + } else { + fail("Attachment metadata does not contain objectId"); + } + } - // @Test - // @Order(17) - // void testRenameSingleAttachmentWithoutSDMRole() throws IOException { - // System.out.println("Test (17) : Rename attachments where user don't have SDM Roles"); - // boolean testStatus = false; - // String apiResponse = apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, entityID); - // String name = "sample123"; // Renaming the attachment - // if (apiResponse == "Entity in draft mode") { - // apiResponse = - // apiNoRoles.renameAttachment(appUrl, entityName, facetName, entityID, attachmentID1, - // name); - // if (apiResponse.equals("Renamed")) { - // apiResponse = apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // String expected = - // "[{\"code\":\"\",\"message\":\"Could not update the following files. \\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 valid_attachment1.pdf\\n" - // + // - // "\\n" - // + // - // "You do not have the required permissions to update attachments. Kindly contact - // the admin\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (apiResponse.equals(expected)) { - // testStatus = true; - // } - // } else { - // apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // } - // } - // if (!testStatus) { - // fail("Attachment got renamed without SDM roles."); - // } - // } + if (sourceObjectIds.size() == 2) { + String copyResponse; + copyResponse = + api.copyAttachment( + appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); + if (copyResponse.equals("Attachments copied successfully")) { + String saveEntityResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); + if (saveEntityResponse.equals("Saved")) { + List> fetchEntityMetadataResponse; + fetchEntityMetadataResponse = + api.fetchEntityMetadata(appUrl, entityName, facetName, copyAttachmentTargetEntity); + targetAttachmentIds = + fetchEntityMetadataResponse.stream() + .map(item -> (String) item.get("ID")) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + String readResponse; + for (String targetAttachmentId : targetAttachmentIds) { + readResponse = + api.readAttachment( + appUrl, + entityName, + facetName, + copyAttachmentTargetEntity, + targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read copied attachment"); + } + } + } else { + fail("Could not save entity after copying attachments: " + saveEntityResponse); + } + } else { + fail("Could not copy attachments: " + copyResponse); + } + } else { + fail("Could not fetch objects Ids for all attachments"); + } + } else { + fail("Could not create entities"); + } + api.deleteEntityDraft(appUrl, entityName, copyAttachmentSourceEntity); + api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); + } - // @Test - // @Order(18) - // void testRenameToValidateNames() throws IOException { - // System.out.println("Test (18) : Rename attachments to validate names"); - // boolean testStatus = false, successCount = true; - // String generatedID = ""; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID3 = response; - // String[] filetoUpload = {"sample.pdf", "sample.txt", "sample.exe", "sample2.pdf"}; - // String[] names = {"Restricted/Character", " ", "duplicateName.pdf", - // "duplicateName.pdf"}; - - // ClassLoader classLoader = getClass().getClassLoader(); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // for (int i = 0; i < filetoUpload.length; i++) { - // File file = new File(classLoader.getResource(filetoUpload[i]).getFile()); - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, - // file); - // generatedID = createResponse.get(1); - // response = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, generatedID, - // names[i]); - // successCount &= "Renamed".equals(response); - // } - // if (successCount) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // String expected = - // "{\"error\":{\"code\":\"400\",\"message\":\"The object name cannot be empty or - // consist entirely of space characters. Enter a value.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\",\"details\":[{\"code\":\"\",\"message\":\"\\\"Restricted/Character\\\" contains unsupported characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4},{\"code\":\"\",\"message\":\"An object named \\\"duplicateName.pdf\\\" already exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"@Common.numericSeverity\":4}]}}"; - // if (response.equals(expected)) { - // response = api.deleteEntityDraft(appUrl, entityName, entityID3); - // if (response.equals("Entity Draft Deleted")) testStatus = true; - // } - // } - // if (!testStatus) fail("Could not create entity"); - // } else { - // fail("Could not create entity"); - // return; - // } - // } + @Test + @Order(60) + void testViewChangelogForNewlyCreatedAttachment() throws IOException { + System.out.println("Test (60): View changelog for newly created attachment"); - // @Test - // @Order(19) - // void testDeleteSingleAttachment() throws IOException { - // System.out.println("Test (19) : Delete single attachment"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Entity in draft mode") { - // response = api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID1); - // if (response == "Deleted") { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Saved") { - // response = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID1); - // if (response.equals("Could not read Attachment")) { - // testStatus = true; - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not read Attachment"); - // } - // } + // Create a new entity for changelog test + changelogEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(changelogEntityID, "Failed to create changelog test entity"); + assertNotEquals("Could not create entity", changelogEntityID); - // @Test - // @Order(20) - // void testDeleteMultipleAttachments() throws IOException { - // System.out.println("Test (20) : Delete multiple attachments"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Entity in draft mode") { - // String response1 = - // api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID2); - // String response2 = - // api.deleteAttachment(appUrl, entityName, facetName, entityID, attachmentID3); - // if (response1 == "Deleted" && response2 == "Deleted") { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID); - // if (response == "Saved") { - // response1 = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID2); - // response2 = api.readAttachment(appUrl, entityName, facetName, entityID, attachmentID3); - // if (response1.equals("Could not read Attachment") - // && response2.equals("Could not read Attachment")) { - // testStatus = true; - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not delete attachment"); - // } - // } + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.txt").getFile()); + assertTrue(file.exists(), "Sample file should exist"); - // @Test - // @Order(21) - // void testUploadBlockedMimeType() throws IOException { - // System.out.println("Test (21): Upload blocked mimeType .rtf"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!"Could not create entity".equals(response)) { - // entityID2 = response; - - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new - // File(Objects.requireNonNull(classLoader.getResource("sample.rtf")).getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID2); - // postData.put("mimeType", "application/rtf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID2, srvpath, postData, - // file); - // String actualResponse = createResponse.get(0); - // String expectedJson = - // "{\"error\":{\"code\":\"500\",\"message\":\"This file type is not allowed in this - // repository. Contact your administrator for assistance.\"}}"; - - // if (expectedJson.equals(actualResponse)) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // if ("Saved".equals(response)) { - // testStatus = true; - // } - // } else { - // api.saveEntityDraft(appUrl, entityName, srvpath, entityID2); - // } - // } - // if (!testStatus) { - // fail("Attachment got uploaded with blocked .rtf MIME type"); - // } - // } + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", changelogEntityID); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(22) - // void testDeleteEntity() { - // System.out.println("Test (22) : Delete entity"); - // Boolean testStatus = false; - // String response = api.deleteEntity(appUrl, entityName, entityID); - // String response2 = api.deleteEntity(appUrl, entityName, entityID2); - // if (response == "Entity Deleted" && response2 == "Entity Deleted") { - // testStatus = true; - // } - // if (!testStatus) { - // fail("Could not delete entity"); - // } - // } + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, changelogEntityID, srvpath, postData, file); - // @Test - // @Order(23) - // void testUpdateValidSecondaryProperty_beforeEntityIsSaved_singleAttachment() throws IOException - // { - // System.out.println("Test (23): Rename & Update secondary property before entity is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID3 = response; - // System.out.println("Entity created"); - // System.out.println("Creating attachment"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, - // file); - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // attachmentID1 = createResponse.get(1); - // System.out.println("Attachment created"); - // String name1 = "sample1234.pdf"; - // String secondaryPropertyString = "sample12345"; - // Integer secondaryPropertyInt = 1234; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // System.out.println("Renaming and updating secondary properties for attachment"); - // String response1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // if (response1 == "Renamed" - // && updateSecondaryPropertyResponse1 == "Updated" - // && updateSecondaryPropertyResponse2 == "Updated" - // && updateSecondaryPropertyResponse3 == "Updated" - // && updateSecondaryPropertyResponse4 == "Updated") { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachment"); - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + changelogAttachmentID = createResponse.get(1); - // @Test - // @Order(24) - // void testUpdateValidSecondaryProperty_afterEntityIsSaved_singleAttachment() { - // System.out.println("Test (24): Rename & Update secondary property after entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response == "Entity in draft mode") { - // String name1 = "sample.pdf"; - // String secondaryPropertyString = "sample"; - // Integer secondaryPropertyInt = 12; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // System.out.println("Renaming and updating secondary properties for attachment"); - // String response1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // if (response1 == "Renamed" - // && updateSecondaryPropertyResponse1 == "Updated" - // && updateSecondaryPropertyResponse2 == "Updated" - // && updateSecondaryPropertyResponse3 == "Updated" - // && updateSecondaryPropertyResponse4 == "Updated") { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachment"); - // } - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property after entity is saved"); - // } - // } + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(changelogAttachmentID, "Attachment ID should not be null"); + assertNotEquals("", changelogAttachmentID, "Attachment ID should not be empty"); - // @Test - // @Order(25) - // void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_singleAttachment() - // throws IOException { - // System.out.println( - // "Test (25): Rename & Update invalid secondary property before entity is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!"Could not create entity".equals(response)) { - // entityID3 = response; - // System.out.println("Entity created"); - // System.out.println("Creating attachment"); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID3); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData, - // file); - // String check = createResponse.get(0); - // if ("Attachment created".equals(check)) { - // attachmentID1 = createResponse.get(1); - // System.out.println("Attachment created"); - // String name1 = "sample1234.pdf"; - - // // Dropdown values for secondaryPropertyString - // String[] dropdownValues = {"A", "B", "C"}; - // // Select one dropdown value (e.g., "A") - // String secondaryPropertyString = dropdownValues[0]; - - // Integer secondaryPropertyInt = 1234; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // String invalidProperty = "testid"; - - // System.out.println("Renaming and updating invalid secondary properties for attachment"); - // String response1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - - // // Update secondary properties for String using dropdown selected value as object with - // code - - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown1); - - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - - // // Update invalid secondary property - // String updateSecondaryPropertyResponse5 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, invalidProperty); - - // if ("Renamed".equals(response1) - // && "Updated".equals(updateSecondaryPropertyResponse1) - // && "Updated".equals(updateSecondaryPropertyResponse2) - // && "Updated".equals(updateSecondaryPropertyResponse3) - // && "Updated".equals(updateSecondaryPropertyResponse4) - // && "Updated".equals(updateSecondaryPropertyResponse5)) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // Map attachmentMetadata = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); - // assertEquals("sample.pdf", attachmentMetadata.get("fileName")); - // assertNull(attachmentMetadata.get("customProperty3")); - // assertNull(attachmentMetadata.get("customProperty4")); - // assertNull(attachmentMetadata.get("customProperty1_code")); - // assertNull(attachmentMetadata.get("customProperty2")); - // assertNull(attachmentMetadata.get("customProperty6")); - // assertNull(attachmentMetadata.get("customProperty5")); - - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update secondary properties for attachment is unsuccessfull"); - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + // Fetch changelog for the newly created attachment + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID); - // @Test - // @Order(26) - // void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_singleAttachment() throws - // IOException { - // System.out.println( - // "Test (26): Rename & Update invalid secondary property after entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response == "Entity in draft mode") { - // String name1 = "sample.pdf"; - // String secondaryPropertyString = "A"; - // Integer secondaryPropertyInt = 12; - // LocalDateTime secondaryPropertyDateTime = LocalDateTime.now(); - // String invalidProperty = "testidinvalid"; - // System.out.println("Renaming and updating invalid secondary properties for attachment"); - // String response1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime + "\"\n}")); - // String updateSecondaryPropertyResponse3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // // Update invalid secondary property - // String updateSecondaryPropertyResponse5 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, invalidProperty); - // if (response1 == "Renamed" - // && updateSecondaryPropertyResponse1 == "Updated" - // && updateSecondaryPropertyResponse2 == "Updated" - // && updateSecondaryPropertyResponse3 == "Updated" - // && updateSecondaryPropertyResponse4 == "Updated" - // && updateSecondaryPropertyResponse5 == "Updated") { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // Map attachmentMetadata = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); - // assertEquals("sample.pdf", attachmentMetadata.get("fileName")); - // assertNull(attachmentMetadata.get("customProperty3")); - // assertNull(attachmentMetadata.get("customProperty4")); - // assertNull(attachmentMetadata.get("customProperty1_code")); - // assertNull(attachmentMetadata.get("customProperty2")); - // assertNull(attachmentMetadata.get("customProperty6")); - // assertNull(attachmentMetadata.get("customProperty5")); - - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update secondary properties for attachment is unsuccessfull"); - // } - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + assertNotNull(changelogResponse, "Changelog response should not be null"); - // @Test - // @Order(27) - // void testUpdateValidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (27): Rename & Update valid secondary properties for multiple attachments before - // entity is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID3 = response; - - // System.out.println("Entity created"); - - // System.out.println("Creating attachment PDF"); - // ClassLoader classLoader = getClass().getClassLoader(); - - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID3); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // List createResponse1 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData1, - // file); - // if (createResponse1.get(0).equals("Attachment created")) { - // attachmentID1 = createResponse1.get(1); - // System.out.println("Attachment created"); - // } + // Verify changelog structure + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + "sample.txt", changelogResponse.get("filename"), "Filename should match uploaded file"); + assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); + assertEquals(1, changelogResponse.get("numItems"), "Should have 1 changelog entry"); + + // Verify the changelog entry + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); + + Map logEntry = changeLogs.get(0); + assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); + assertNotNull(logEntry.get("time"), "Time should not be null"); + assertNotNull(logEntry.get("user"), "User should not be null"); + assertFalse( + logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); + } - // System.out.println("Creating attachment TXT"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData2 = new HashMap<>(); - // postData2.put("up__ID", entityID3); - // postData2.put("mimeType", "application/txt"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // List createResponse2 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData2, - // file); - // if (createResponse2.get(0).equals("Attachment created")) { - // attachmentID2 = createResponse2.get(1); - // System.out.println("Attachment created"); - // } + @Test + @Order(61) + void testChangelogAfterModifyingNoteAndCustomProperty() throws IOException { + System.out.println( + "Test (61): Modify note field and custom property, then verify changelog shows created + 3 updated entries"); - // System.out.println("Creating attachment EXE"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // Map postData3 = new HashMap<>(); - // postData3.put("up__ID", entityID3); - // postData3.put("mimeType", "application/exe"); - // postData3.put("createdAt", new Date().toString()); - // postData3.put("createdBy", "test@test.com"); - // postData3.put("modifiedBy", "test@test.com"); - - // List createResponse3 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData3, - // file); - // if (createResponse3.get(0).equals("Attachment created")) { - // attachmentID3 = createResponse3.get(1); - // System.out.println("Attachment created"); - // } + // Update attachment with notes field (entity is already in draft mode from test 60) + String notesValue = "Test note for changelog verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - // String check1 = createResponse1.get(0); - // String check2 = createResponse2.get(0); - // String check3 = createResponse3.get(0); - // if (check1.equals("Attachment created") - // && check2.equals("Attachment created") - // && check3.equals("Attachment created")) { - // Boolean attachment1Updated = false; - // Boolean attachment2Updated = false; - // Boolean attachment3Updated = false; - - // String name1 = "sample1234.pdf"; - // Integer secondaryPropertyInt1 = 1234; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // System.out.println("Renaming and updating secondary properties for attachment PDF"); - // String responsePDF1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponsePDF1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponsePDF2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponsePDF3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponsePDF4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // if (responsePDF1 == "Renamed" - // && updateSecondaryPropertyResponsePDF1 == "Updated" - // && updateSecondaryPropertyResponsePDF2 == "Updated" - // && updateSecondaryPropertyResponsePDF3 == "Updated" - // && updateSecondaryPropertyResponsePDF4 == "Updated") { - // System.out.println("Renamed & updated Secondary properties for attachment PDF"); - // attachment1Updated = true; - // } - - // System.out.println("Updating secondary properties for attachment TXT"); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); - // if (updateSecondaryPropertyResponseTXT1 == "Updated") { - // System.out.println("Updated Secondary properties for attachment TXT"); - // attachment2Updated = true; - // } - // Integer secondaryPropertyInt3 = 1234; - // LocalDateTime secondaryPropertyDateTime3 = LocalDateTime.now(); - // System.out.println("Updating secondary properties for attachment EXE"); - // // Update secondary properties for String - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); - // // Update secondary properties for Integer - // RequestBody bodyInt3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime3 + "\"\n}")); - // String updateSecondaryPropertyResponseEXE3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDateTime3); - - // if (updateSecondaryPropertyResponseEXE1 == "Updated" - // && updateSecondaryPropertyResponseEXE2 == "Updated" - // && updateSecondaryPropertyResponseEXE3 == "Updated") { - // System.out.println("Updated Secondary properties for attachment EXE"); - // attachment3Updated = true; - // } - - // if (attachment1Updated && attachment2Updated && attachment3Updated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachments"); - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + changelogEntityID, + changelogAttachmentID, + updateNotesBody); + assertEquals("Updated", updateNotesResponse, "Should successfully update notes field"); + + // Update attachment with custom property + Integer customProperty2Value = 12345; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID, bodyInt); + assertEquals( + "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - // @Test - // @Order(28) - // void testUpdateValidSecondaryProperty_afterEntityIsSaved_multipleAttachments() { - // System.out.println( - // "Test (28): Rename & Update valid secondary properties for multiple attachments after - // entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response == "Entity in draft mode") { - // Boolean attachment1Updated = false; - // Boolean attachment2Updated = false; - // Boolean attachment3Updated = false; - - // String name1 = "sample1.pdf"; - // Integer secondaryPropertyInt1 = 12; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // System.out.println("Renaming and updating secondary properties for attachment PDF"); - // String responsePDF1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue1 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown1 = "{ \"customProperty1_code\" : \"" + dropdownValue1 + "\" }"; - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown1); - // String updateSecondaryPropertyResponsePDF1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown1); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponsePDF2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponsePDF3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponsePDF4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - - // if (responsePDF1 == "Renamed" - // && updateSecondaryPropertyResponsePDF1 == "Updated" - // && updateSecondaryPropertyResponsePDF2 == "Updated" - // && updateSecondaryPropertyResponsePDF3 == "Updated" - // && updateSecondaryPropertyResponsePDF4 == "Updated") { - // System.out.println("Renamed & updated Secondary properties for attachment PDF"); - // attachment1Updated = true; - // } + // Save the entity + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - // System.out.println("Updating secondary properties for attachment TXT"); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); - // if (updateSecondaryPropertyResponseTXT1 == "Updated") { - // System.out.println("Updated Secondary properties for attachment TXT"); - // attachment2Updated = true; - // } + // Edit entity again to fetch changelog + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // Integer secondaryPropertyInt3 = 123; - // LocalDateTime secondaryPropertyDateTime3 = LocalDateTime.now(); - // System.out.println("Updating secondary properties for attachment EXE"); - // // Update secondary properties for String - // String dropdownValue2 = integrationTestUtils.getDropDownValue(); - // String jsonDropdown2 = "{ \"customProperty1_code\" : \"" + dropdownValue2 + "\" }"; - // RequestBody bodyDropdown2 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown2); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown2); - // // Update secondary properties for Integer - // RequestBody bodyInt3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime3 + "\"\n}")); - // String updateSecondaryPropertyResponseEXE3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDateTime3); - - // if (updateSecondaryPropertyResponseEXE1 == "Updated" - // && updateSecondaryPropertyResponseEXE2 == "Updated" - // && updateSecondaryPropertyResponseEXE3 == "Updated") { - // System.out.println("Updated Secondary properties for attachment EXE"); - // attachment3Updated = true; - // } + // Fetch changelog after modifications + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID); - // if (attachment1Updated && attachment2Updated && attachment3Updated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response.equals("Saved")) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println("Renamed & updated Secondary properties for attachments"); - // } - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property after entity is saved"); - // } - // } + assertNotNull(changelogResponse, "Changelog response should not be null"); - // @Test - // @Order(29) - // void testUpdateInvalidSecondaryProperty_beforeEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (29): Rename & Update invalid and valid secondary properties for multiple - // attachments before entity is saved"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID3 = response; - - // System.out.println("Entity created"); - - // System.out.println("Creating attachment PDF"); - // ClassLoader classLoader = getClass().getClassLoader(); - - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID3); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // List createResponse1 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData1, - // file); - // if (createResponse1.get(0).equals("Attachment created")) { - // attachmentID1 = createResponse1.get(1); - // System.out.println("Attachment created"); - // } + // Verify changelog content - should have 1 created + 3 updated (note, customProperty2, and + // internal update) + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + 4, + changelogResponse.get("numItems"), + "Should have 4 changelog entries (1 created + 3 updated)"); - // System.out.println("Creating attachment TXT"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData2 = new HashMap<>(); - // postData2.put("up__ID", entityID3); - // postData2.put("mimeType", "application/txt"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // List createResponse2 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData2, - // file); - // if (createResponse2.get(0).equals("Attachment created")) { - // attachmentID2 = createResponse2.get(1); - // System.out.println("Attachment created"); - // } + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(4, changeLogs.size(), "Should have exactly 4 changelog entries"); - // System.out.println("Creating attachment EXE"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // Map postData3 = new HashMap<>(); - // postData3.put("up__ID", entityID3); - // postData3.put("mimeType", "application/exe"); - // postData3.put("createdAt", new Date().toString()); - // postData3.put("createdBy", "test@test.com"); - // postData3.put("modifiedBy", "test@test.com"); - - // List createResponse3 = - // api.createAttachment(appUrl, entityName, facetName, entityID3, srvpath, postData3, - // file); - // if (createResponse3.get(0).equals("Attachment created")) { - // attachmentID3 = createResponse3.get(1); - // System.out.println("Attachment created"); - // } + // Verify first entry is 'created' + Map createdEntry = changeLogs.get(0); + assertEquals( + "created", createdEntry.get("operation"), "First entry should be 'created' operation"); + + // Verify remaining entries are 'updated' + long updatedCount = + changeLogs.stream().filter(log -> "updated".equals(log.get("operation"))).count(); + assertEquals(3, updatedCount, "Should have 3 'updated' operations"); + + // Verify that changeDetail exists in updated entries for note field + boolean hasNoteUpdate = + changeLogs.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .anyMatch( + log -> { + @SuppressWarnings("unchecked") + Map changeDetail = (Map) log.get("changeDetail"); + return changeDetail != null + && "cmis:description".equals(changeDetail.get("field")); + }); + assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); + assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); + + // Save the entity so test 62 can edit it + String saveResponseFinal = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Saved", saveResponseFinal, "Entity should be saved successfully"); + } - // String check1 = createResponse1.get(0); - // String check2 = createResponse2.get(0); - // String check3 = createResponse3.get(0); - // if (check1.equals("Attachment created") - // && check2.equals("Attachment created") - // && check3.equals("Attachment created")) { - // Boolean attachment1Updated = false; - // Boolean attachment2Updated = false; - // Boolean attachment3Updated = false; - - // String name1 = "sample1234.pdf"; - // Integer secondaryPropertyInt1 = 1234; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // String invalidPropertyPDF = "testidinvalidPDF"; - // System.out.println("Renaming and updating invalid secondary properties for attachment - // PDF"); - // String responsePDF1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponsePDF1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyint = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponsePDF2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyint); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponsePDF3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponsePDF4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // // Update invalid secondary property - // String updateSecondaryPropertyResponsePDF5 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, invalidPropertyPDF); - // if (responsePDF1 == "Renamed" - // && updateSecondaryPropertyResponsePDF1 == "Updated" - // && updateSecondaryPropertyResponsePDF2 == "Updated" - // && updateSecondaryPropertyResponsePDF3 == "Updated" - // && updateSecondaryPropertyResponsePDF4 == "Updated" - // && updateSecondaryPropertyResponsePDF5 == "Updated") { - // attachment1Updated = true; - // } - - // System.out.println("Updating valid secondary properties for attachment TXT"); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); - // if (updateSecondaryPropertyResponseTXT1 == "Updated") { - // System.out.println("Updated Secondary properties for attachment TXT"); - // attachment2Updated = true; - // } - - // Integer secondaryPropertyInt3 = 1234; - // System.out.println("Updating valid secondary properties for attachment EXE"); - - // // Update secondary properties for String - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); - // // Update secondary properties for Integer - // RequestBody bodyInt3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); - - // if (updateSecondaryPropertyResponseEXE1 == "Updated" - // && updateSecondaryPropertyResponseEXE2 == "Updated") { - // System.out.println("Updated Secondary properties for attachment EXE"); - // attachment3Updated = true; - // } - - // if (attachment1Updated && attachment2Updated && attachment3Updated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // Map attachmentMetadataPDF = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); - // assertEquals("sample.pdf", attachmentMetadataPDF.get("fileName")); - // assertNull(attachmentMetadataPDF.get("customProperty3")); - // assertNull(attachmentMetadataPDF.get("customProperty4")); - // assertNull(attachmentMetadataPDF.get("customProperty1_code")); - // assertNull(attachmentMetadataPDF.get("customProperty2")); - // assertNull(attachmentMetadataPDF.get("customProperty6")); - // assertNull(attachmentMetadataPDF.get("customProperty5")); - - // Map attachmentMetadataTXT = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID2); - // assertEquals("sample.txt", attachmentMetadataTXT.get("fileName")); - // assertNull(attachmentMetadataTXT.get("customProperty3")); - // assertNull(attachmentMetadataTXT.get("customProperty4")); - // assertNull(attachmentMetadataTXT.get("customProperty1_code")); - // assertNull(attachmentMetadataTXT.get("customProperty2")); - // assertTrue((Boolean) attachmentMetadataTXT.get("customProperty6")); - // assertNull(attachmentMetadataTXT.get("customProperty5")); - - // Map attachmentMetadataEXE = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID3); - // assertEquals("sample.exe", attachmentMetadataEXE.get("fileName")); - // assertNull(attachmentMetadataEXE.get("customProperty3")); - // assertNull(attachmentMetadataEXE.get("customProperty4")); - // assertEquals(dropdownValue, attachmentMetadataEXE.get("customProperty1_code")); - // assertEquals(1234, attachmentMetadataEXE.get("customProperty2")); - - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n\\nTable: attachments\\nPage: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update unsuccessfull for invalid Secondary properties and successfull - // for valid property attachments"); - // } - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + @Test + @Order(62) + void testChangelogAfterRenamingAttachment() throws IOException { + System.out.println( + "Test (62): Rename attachment and verify changelog increases with rename entry"); - // @Test - // @Order(30) - // void testUpdateInvalidSecondaryProperty_afterEntityIsSaved_multipleAttachments() - // throws IOException { - // System.out.println( - // "Test (30): Rename & Update invalid and valid secondary properties for multiple - // attachments after entity is saved"); - // System.out.println("Editing entity"); - // Boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID3); - // if (response == "Entity in draft mode") { - // Boolean attachment1Updated = false; - // Boolean attachment2Updated = false; - // Boolean attachment3Updated = false; - - // String name1 = "sample.pdf"; - // Integer secondaryPropertyInt1 = 12; - // LocalDateTime secondaryPropertyDateTime1 = LocalDateTime.now(); - // String invalidPropertyPDF = "testidinvalidPDF"; - // System.out.println("Renaming and updating invalid secondary properties for attachment - // PDF"); - // String responsePDF1 = - // api.renameAttachment(appUrl, entityName, facetName, entityID3, attachmentID1, name1); - // // Update secondary properties for String - // String dropdownValue = integrationTestUtils.getDropDownValue(); - // String jsonDropdown = "{ \"customProperty1_code\" : \"" + dropdownValue + "\" }"; - // RequestBody bodyDropdown = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponsePDF1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDropdown); - // // Update secondary properties for Integer - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt1 + "\n}")); - // String updateSecondaryPropertyResponsePDF2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyInt); - // // Update secondary properties for DateTime - // RequestBody bodyDateTime = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty5\" : \"" + secondaryPropertyDateTime1 + "\"\n}")); - // String updateSecondaryPropertyResponsePDF3 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyDateTime); - // // Update secondary properties for Boolean - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponsePDF4 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, bodyBoolean); - // // Update invalid secondary property - // String updateSecondaryPropertyResponsePDF5 = - // api.updateInvalidSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID1, invalidPropertyPDF); - // if (responsePDF1 == "Renamed" - // && updateSecondaryPropertyResponsePDF1 == "Updated" - // && updateSecondaryPropertyResponsePDF2 == "Updated" - // && updateSecondaryPropertyResponsePDF3 == "Updated" - // && updateSecondaryPropertyResponsePDF4 == "Updated" - // && updateSecondaryPropertyResponsePDF5 == "Updated") { - // attachment1Updated = true; - // } + // Edit entity to put it in draft mode (entity was saved at end of test 61) + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - // System.out.println("Updating valid secondary properties for attachment TXT"); - // // Update secondary properties for Boolean - // RequestBody bodyBool = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + false + "\n}")); - // String updateSecondaryPropertyResponseTXT1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID2, bodyBool); - // if (updateSecondaryPropertyResponseTXT1 == "Updated") { - // System.out.println("Updated Secondary properties for attachment TXT"); - // attachment2Updated = true; - // } + // Rename the attachment + String newFileName = "renamed_sample.txt"; + String renameResponse = + api.renameAttachment( + appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID, newFileName); + assertEquals("Renamed", renameResponse, "Should successfully rename attachment"); + + // Save entity after rename + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully after rename"); + + // Edit entity again and fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); + + // Fetch changelog after rename + Map changelogAfterRename = + api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID); + + assertNotNull(changelogAfterRename, "Changelog response should not be null after rename"); + + // Verify changelog has increased (rename operation adds 1 entry for cmis:name change) + // Expected: 1 created + 3 initial updates + 1 rename update = 5 total + assertEquals( + 5, changelogAfterRename.get("numItems"), "Should have 5 changelog entries after rename"); + + @SuppressWarnings("unchecked") + List> changeLogsAfterRename = + (List>) changelogAfterRename.get("changeLogs"); + assertEquals( + 5, changeLogsAfterRename.size(), "Should have exactly 5 changelog entries after rename"); + + // Verify updated count is 4 (3 initial + 1 from rename operation) + long updatedCountAfterRename = + changeLogsAfterRename.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .count(); + assertEquals(4, updatedCountAfterRename, "Should have 4 'updated' operations after rename"); + + // Verify filename change in changelog + boolean hasFilenameUpdate = + changeLogsAfterRename.stream() + .filter(log -> "updated".equals(log.get("operation"))) + .anyMatch( + log -> { + @SuppressWarnings("unchecked") + Map changeDetail = (Map) log.get("changeDetail"); + return changeDetail != null && "cmis:name".equals(changeDetail.get("field")); + }); + assertTrue(hasFilenameUpdate, "Should have an update entry for filename (cmis:name)"); + + // Cleanup - entity was saved after rename, so delete the active entity + api.deleteEntity(appUrl, entityName, changelogEntityID); + } + + @Test + @Order(63) + void testChangelogWithCustomPropertyEditSave() throws IOException { + System.out.println( + "Test (63): Create entity with custom property, save, edit and save again - verify changelog remains at 3 entries"); + + // Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(newEntityID, "Failed to create new entity"); + assertNotEquals("Could not create entity", newEntityID); + + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + assertTrue(file.exists(), "Sample file should exist"); + + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); + + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + String attachmentID = createResponse.get(1); + + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(attachmentID, "Attachment ID should not be null"); + assertNotEquals("", attachmentID, "Attachment ID should not be empty"); + + // Add a custom property + Integer customPropertyValue = 99999; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customPropertyValue + "}", + MediaType.parse("application/json")); + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, newEntityID, attachmentID, bodyInt); + assertEquals( + "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); + + // Save the entity + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); + + // Edit entity to fetch initial changelog + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); + + // Fetch changelog after initial save + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); + + assertNotNull(changelogResponse, "Changelog response should not be null"); + + // Verify changelog has 3 entries: 1 created + 2 updated (cmis:secondaryObjectTypeIds + + // customProperty2) + assertEquals(3, changelogResponse.get("numItems"), "Should have 3 changelog entries initially"); + + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(3, changeLogs.size(), "Should have exactly 3 changelog entries"); + + // Save entity again without any modifications + saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); + + // Edit entity again and fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); + + // Fetch changelog after second save + Map changelogAfterSecondSave = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); + + assertNotNull( + changelogAfterSecondSave, "Changelog response should not be null after second save"); + + // Verify changelog still has only 3 entries (no new entries added) + assertEquals( + 3, + changelogAfterSecondSave.get("numItems"), + "Should still have only 3 changelog entries after edit-save without modifications"); + + @SuppressWarnings("unchecked") + List> changeLogsAfterSecondSave = + (List>) changelogAfterSecondSave.get("changeLogs"); + assertEquals( + 3, + changeLogsAfterSecondSave.size(), + "Should still have exactly 3 changelog entries after second save"); + + // Clean up the entity + api.deleteEntity(appUrl, entityName, newEntityID); + } + + @Test + @Order(64) + void testChangelogForSavedAttachmentWithoutModification() throws IOException { + System.out.println( + "Test (64): Create entity, upload attachment, save, edit and save again - verify changelog still has only 'created' entry"); + + // Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotNull(newEntityID, "Failed to create new entity"); + assertNotEquals("Could not create entity", newEntityID); + + // Prepare a sample file to upload + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + assertTrue(file.exists(), "Sample file should exist"); + + // Create attachment + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); + + assertEquals(2, createResponse.size(), "Should return status and attachment ID"); + String status = createResponse.get(0); + String newAttachmentID = createResponse.get(1); + + assertEquals("Attachment created", status, "Attachment should be created successfully"); + assertNotNull(newAttachmentID, "Attachment ID should not be null"); + assertNotEquals("", newAttachmentID, "Attachment ID should not be empty"); + + // Save the entity immediately without any modifications + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully"); + + // Edit entity again without making any changes to the attachment + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); + + // Save entity again without modifying the attachment + saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); + + // Edit entity to fetch changelog + editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); + + // Fetch changelog for the attachment + Map changelogResponse = + api.fetchChangelog(appUrl, entityName, facetName, newEntityID, newAttachmentID); + + assertNotNull(changelogResponse, "Changelog response should not be null"); + + // Verify changelog content - should only have 'created' entry even after edit and save + assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); + assertEquals( + "sample.pdf", changelogResponse.get("filename"), "Filename should match uploaded file"); + assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); + assertEquals(1, changelogResponse.get("numItems"), "Should have only 1 changelog entry"); + + // Verify the changelog entry + @SuppressWarnings("unchecked") + List> changeLogs = + (List>) changelogResponse.get("changeLogs"); + assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); + + Map logEntry = changeLogs.get(0); + assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); + assertNotNull(logEntry.get("time"), "Time should not be null"); + assertNotNull(logEntry.get("user"), "User should not be null"); + assertFalse( + logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); + + // Clean up the new entity + api.deleteEntity(appUrl, entityName, newEntityID); + } + + @Test + @Order(65) + void testMoveAttachmentsWithSourceFacet() throws IOException { + System.out.println( + "Test (65): Move attachments from Source Entity to Target Entity with sourceFacet"); + + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } + + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } + + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } + + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } else { + fail("Attachment metadata does not contain objectId"); + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // Integer secondaryPropertyInt3 = 12; - // System.out.println("Updating valid secondary properties for attachment EXE"); - - // // Update secondary properties for String - // RequestBody bodyDropdown1 = - // RequestBody.create(MediaType.parse("application/json"), jsonDropdown); - // String updateSecondaryPropertyResponseEXE1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyDropdown1); - // // Update secondary properties for Integer - // RequestBody bodyInt3 = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8( - // "{\n \"customProperty2\" : " + secondaryPropertyInt3 + "\n}")); - // String updateSecondaryPropertyResponseEXE2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, entityID3, attachmentID3, bodyInt3); - - // if (updateSecondaryPropertyResponseEXE1 == "Updated" - // && updateSecondaryPropertyResponseEXE2 == "Updated") { - // System.out.println("Updated Secondary properties for attachment EXE"); - // attachment3Updated = true; - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // if (attachment1Updated && attachment2Updated && attachment3Updated) { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID3); - // Map attachmentMetadataPDF = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID1); - // assertEquals("sample.pdf", attachmentMetadataPDF.get("fileName")); - // assertNull(attachmentMetadataPDF.get("customProperty3")); - // assertNull(attachmentMetadataPDF.get("customProperty4")); - // assertNull(attachmentMetadataPDF.get("customProperty1_code")); - // assertNull(attachmentMetadataPDF.get("customProperty2")); - // assertNull(attachmentMetadataPDF.get("customProperty6")); - // assertNull(attachmentMetadataPDF.get("customProperty5")); - - // Map attachmentMetadataTXT = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID2); - // assertEquals("sample.txt", attachmentMetadataTXT.get("fileName")); - // assertNull(attachmentMetadataTXT.get("customProperty3")); - // assertNull(attachmentMetadataTXT.get("customProperty4")); - // assertNull(attachmentMetadataTXT.get("customProperty1_code")); - // assertNull(attachmentMetadataTXT.get("customProperty2")); - // assertFalse((Boolean) attachmentMetadataTXT.get("customProperty6")); - // assertNull(attachmentMetadataTXT.get("customProperty5")); - - // Map attachmentMetadataEXE = - // api.fetchMetadata(appUrl, entityName, facetName, entityID3, attachmentID3); - // assertEquals("sample.exe", attachmentMetadataEXE.get("fileName")); - // assertNull(attachmentMetadataEXE.get("customProperty3")); - // assertNull(attachmentMetadataEXE.get("customProperty4")); - // assertEquals(dropdownValue, attachmentMetadataEXE.get("customProperty1_code")); - // assertEquals(12, attachmentMetadataEXE.get("customProperty2")); - - // String expectedResponse = - // "[{\"code\":\"\",\"message\":\"The following secondary properties are not - // supported.\\n" - // + // - // "\\n" - // + // - // "\\t\\u2022 id1\\n" - // + // - // "\\n" - // + // - // "Please contact your administrator for assistance with any necessary - // adjustments.\\n" - // + // - // "\\n" - // + // - // "Table: attachments\\n" - // + // - // "Page: IntegrationTestEntity\",\"numericSeverity\":3}]"; - // if (response.equals(expectedResponse)) { - // System.out.println("Entity saved"); - // testStatus = true; - // System.out.println( - // "Rename & update unsuccessfull for invalid Secondary properties and successfull for - // valid property attachments"); - // } - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID3); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } - // } - // } - // if (!testStatus) { - // fail("Could not update secondary property before entity is saved"); - // } - // } + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // @Test - // @Order(31) - // void testNAttachments_NewEntity() throws IOException { - // System.out.println( - // "Test (31): Creating new entity and checking only max 4 attachments are allowed to be - // uploaded"); - // System.out.println("Creating entity"); - // Boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response != "Could not create entity") { - // entityID4 = response; - - // System.out.println("Entity created"); - - // System.out.println("Creating attachment PDF"); - // ClassLoader classLoader = getClass().getClassLoader(); - - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID4); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // List createResponse1 = - // api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData1, - // file); - // if (createResponse1.get(0).equals("Attachment created")) { - // attachmentID1 = createResponse1.get(1); - // System.out.println("Attachment created"); - // } + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity: " + saveTargetBeforeMoveResponse); + } - // System.out.println("Creating attachment TXT"); - // file = new File(classLoader.getResource("sample.txt").getFile()); - // Map postData2 = new HashMap<>(); - // postData2.put("up__ID", entityID4); - // postData2.put("mimeType", "application/txt"); - // postData2.put("createdAt", new Date().toString()); - // postData2.put("createdBy", "test@test.com"); - // postData2.put("modifiedBy", "test@test.com"); - - // List createResponse2 = - // api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData2, - // file); - // if (createResponse2.get(0).equals("Attachment created")) { - // attachmentID2 = createResponse2.get(1); - // System.out.println("Attachment created"); - // } + // Move attachments from source to target with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + // All attachments moved to target entity in SDM & UI + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals( + sourceAttachmentIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all attachments after move"); + + // Verify attachments can be read from target entity + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + String readResponse = + api.readAttachment(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read moved attachment from target entity"); + } + } - // System.out.println("Creating attachment EXE"); - // file = new File(classLoader.getResource("sample.exe").getFile()); - // Map postData3 = new HashMap<>(); - // postData3.put("up__ID", entityID4); - // postData3.put("mimeType", "application/exe"); - // postData3.put("createdAt", new Date().toString()); - // postData3.put("createdBy", "test@test.com"); - // postData3.put("modifiedBy", "test@test.com"); - - // List createResponse3 = - // api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, - // file); - // if (createResponse3.get(0).equals("Attachment created")) { - // attachmentID3 = createResponse3.get(1); - // System.out.println("Attachment created"); - // } + // All attachments removed from source entity in SDM & UI + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + 0, sourceMetadataAfterMove.size(), "Source entity should have 0 attachments after move"); - // System.out.println("Creating second attachment pdf"); - // file = new File(classLoader.getResource("sample1.pdf").getFile()); - // Map postData4 = new HashMap<>(); - // postData4.put("up__ID", entityID4); - // postData4.put("mimeType", "application/pdf"); - // postData4.put("createdAt", new Date().toString()); - // postData4.put("createdBy", "test@test.com"); - // postData4.put("modifiedBy", "test@test.com"); - - // List createResponse4 = - // api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, - // file); - // if (createResponse4.get(0).equals("Attachment created")) { - // attachmentID4 = createResponse4.get(1); - // System.out.println("Attachment created"); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // System.out.println("Creating third attachment pdf"); - // file = new File(classLoader.getResource("sample2.pdf").getFile()); - // Map postData5 = new HashMap<>(); - // postData5.put("up__ID", entityID4); - // postData5.put("mimeType", "application/pdf"); - // postData5.put("createdAt", new Date().toString()); - // postData5.put("createdBy", "test@test.com"); - // postData5.put("modifiedBy", "test@test.com"); - - // List createResponse5 = - // api.createAttachment(appUrl, entityName, facetName, entityID4, srvpath, postData3, - // file); - // if (createResponse5.get(0).equals("Only 4 attachments allowed.")) { - // testStatus = true; - // attachmentID5 = createResponse5.get(1); - // System.out.println("Expected error received: Only 4 attachments allowed."); - // } - // String check = createResponse5.get(0); - // if (check.equals("Attachment created")) { - // testStatus = false; - // } else { - // response = api.saveEntityDraft(appUrl, entityName, srvpath, entityID4); - // if (response.equals("Saved")) { - // String expectedJson = - // "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 - // attachments.\"}}"; - // ObjectMapper objectMapper = new ObjectMapper(); - // JsonNode actualJsonNode = objectMapper.readTree(check); - // JsonNode expectedJsonNode = objectMapper.readTree(expectedJson); - // if (expectedJsonNode.equals(actualJsonNode)) { - // testStatus = true; - // } - // } - // } - // } - // if (!testStatus) { - // fail("Attachment was created"); - // } - // } + @Test + @Order(66) + public void testMoveAttachmentsToEntityWithDuplicateWithSourceFacet() throws Exception { + System.out.println( + "Test (66): Move attachments to entity with duplicate attachment with sourceFacet"); - // @Test - // @Order(32) - // void testUploadNAttachments() throws IOException { - // System.out.println("Test (32): Upload maximum 4 attachments in an exsisting entity"); + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File originalFile = new File(classLoader.getResource("sample.exe").getFile()); - - // boolean testStatus = false; - // String response = api.editEntityDraft(appUrl, entityName, srvpath, entityID4); - // System.out.println("response: " + response); - - // if ("Entity in draft mode".equals(response)) { - // for (int i = 1; i <= 5; i++) { - // // Ensure only one file is uploaded at a time and complete before next - // File tempFile = File.createTempFile("sample_" + i + "_", ".exe"); - // Files.copy(originalFile.toPath(), tempFile.toPath(), - // StandardCopyOption.REPLACE_EXISTING); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID4); - // postData.put("mimeType", "application/exe"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, entityID4, srvpath, postData, tempFile); - - // String resultMessage = createResponse.get(0); - // System.out.println("Result message for attachment " + i + ": " + resultMessage); - - // String expectedResponse = - // "{\"error\":{\"code\":\"500\",\"message\":\"Cannot upload more than 4 - // attachments.\"}}"; - // if (resultMessage.equals(expectedResponse)) { - // ObjectMapper objectMapper = new ObjectMapper(); - // JsonNode actualJsonNode = objectMapper.readTree(resultMessage); - // JsonNode expectedJsonNode = objectMapper.readTree(expectedResponse); - // if (expectedJsonNode.equals(actualJsonNode)) { - // testStatus = true; - // } - // } else { - // testStatus = false; - // } - // tempFile.delete(); - // } - // if (!testStatus) { - // fail("5th attachment did not trigger the expected error."); - // } - // // Delete the newly created entity - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, entityID4); - // if (deleteEntityResponse != "Entity Deleted") { - // fail("Could not delete entity"); - // } else { - // System.out.println("Successfully deleted the test entity4"); - // } - // } - // } + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // @Test - // @Order(33) - // void testDiscardDraftWithoutAttachments() { - // System.out.println("Test (33) : Discard draft without adding attachments"); + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // if (response.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // response = api.deleteEntityDraft(appUrl, entityName, response); - // if (!response.equals("Entity Draft Deleted")) { - // fail("Draft was not discarded properly"); - // } - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } - // @Test - // @Order(34) - // void testDiscardDraftWithAttachments() throws IOException { - // System.out.println("Test (34) : Discard draft with attachments"); - // boolean testStatus = false; - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!response.equals("Could not create entity")) { - // entityID7 = response; - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - - // Map postData1 = new HashMap<>(); - // postData1.put("up__ID", entityID7); - // postData1.put("mimeType", "application/pdf"); - // postData1.put("createdAt", new Date().toString()); - // postData1.put("createdBy", "test@test.com"); - // postData1.put("modifiedBy", "test@test.com"); - - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, entityID7, srvpath, postData1, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachmentID1 = createResponse.get(1); - // } - // String check = createResponse.get(0); - // if (check.equals("Attachment created")) { - // response = api.deleteEntityDraft(appUrl, entityName, entityID7); - // } - // if (response.equals("Entity Draft Deleted")) { - // testStatus = true; - // } - // } - // if (!testStatus) { - // fail("Draft was not discarded properly"); - // } - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } - // @Test - // @Order(35) - // void testCopyAttachmentsSuccessNewEntity() throws IOException { - // System.out.println("Test (35): Copy attachments from one entity to another new entity"); - // List attachments = new ArrayList<>(); - // copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!copyAttachmentSourceEntity.equals("Could not create entity") - // && !copyAttachmentTargetEntity.equals("Could not create entity")) { - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample1.pdf").getFile())); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (String attachment : attachments) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadata( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } + // Create target entity and add attachment + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // if (sourceObjectIds.size() == 2) { - // String copyResponse; - // copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); - // if (copyResponse.equals("Attachments copied successfully")) { - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadata(appUrl, entityName, facetName, - // copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not create entities"); - // } - // } + Map targetPostData = new HashMap<>(); + targetPostData.put("up__ID", moveTargetEntity); + targetPostData.put("mimeType", "application/pdf"); + targetPostData.put("createdAt", new Date().toString()); + targetPostData.put("createdBy", "test@test.com"); + targetPostData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(36) - // void testCopyAttachmentsUnsuccessfulNewEntity() throws IOException { - // System.out.println("Test (36): Copy attachments from one entity to another new entity"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // copyAttachmentTargetEntityEmpty = - // api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (editResponse1.equals("Entity in draft mode") - // && !copyAttachmentTargetEntityEmpty.equals("Could not create entity")) { - // sourceObjectIds.add("incorrectObjectId"); - // if (sourceObjectIds.size() == 3) { - // try { - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntityEmpty, sourceObjectIds); - // fail("Copy attachments did not throw an error"); - // } catch (IOException e) { - // String saveEntityResponse1 = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String saveEntityResponse2 = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntityEmpty); - // String deleteResponse = - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntityEmpty); - // if (!saveEntityResponse1.equals("Saved") - // || !saveEntityResponse2.equals("Saved") - // || !deleteResponse.equals("Entity Deleted")) { - // fail("Could not save entities"); - // } - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); + List targetCreateResponse = + api.createAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + srvpath, + targetPostData, + duplicateFile); - // @Test - // @Order(37) - // void testCopyAttachmentWithNotesField() throws IOException { - // System.out.println( - // "Test (37): Create entity with attachment containing notes, copy to new entity and verify - // notes field"); - // Boolean testStatus = false; - // // Create source entity - // copyCustomSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyCustomSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + if (!targetCreateResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment on target entity"); + } - // // Create and upload attachment to source entity - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Save target entity to persist the attachment + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse); + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + // Fetch target metadata before move (target entity is now saved with 1 attachment) + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + int targetCountBeforeMove = targetMetadataBeforeMove.size(); + + // Move attachments from source to target with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + // Verify target has duplicate skipped, other attachments moved + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + + // Expected: original attachments + non-duplicate moved attachments + int expectedTargetCount = targetCountBeforeMove + (sourceAttachmentIds.size() - 1); + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target should have duplicate skipped, other attachments moved"); + + // Verify source entity has only the duplicate attachment remaining + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + // Calculate expected source count: number of duplicates that couldn't be moved + int expectedSourceCount = + sourceAttachmentIds.size() - (targetMetadataAfterMove.size() - targetCountBeforeMove); + assertEquals( + expectedSourceCount, + sourceMetadataAfterMove.size(), + "Source should have duplicate attachment remaining"); - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment"); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // String sourceAttachmentId = createResponse.get(1); + @Test + @Order(67) + public void testMoveAttachmentsWithNotesAndSecondaryProperties() throws Exception { + System.out.println( + "Test (67): Move attachments with notes and secondary properties with sourceFacet"); - // // Update attachment with notes field - // String notesValue = "This is a test note for copy attachment verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateBody = RequestBody.create(jsonPayload, mediaType); + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // String updateResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, - // updateBody); + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // if (!updateResponse.equals("Updated")) { - // fail("Could not update attachment notes field"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Save source entity - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity"); - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // // Fetch attachment metadata to get objectId - // Map sourceAttachmentMetadata = - // api.fetchMetadata( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + // Add notes to attachments + String notesValue = "Test note for verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + + for (String attachmentId : sourceAttachmentIds) { + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, attachmentId, updateNotesBody); + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update notes for attachment: " + attachmentId); + } + } - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId"); - // } + // Add custom property to attachments + Integer customProperty2Value = 54321; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + + for (String attachmentId : sourceAttachmentIds) { + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, attachmentId, bodyInt); + if (!updateCustomPropertyResponse.equals("Updated")) { + fail("Could not update custom property for attachment: " + attachmentId); + } + } - // // Store objectId in array - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // if (sourceObjectIds.isEmpty()) { - // sourceObjectIds.add(sourceObjectId); - // } else { - // sourceObjectIds.set(0, sourceObjectId); - // } + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // String sourceNoteValue = - // sourceAttachmentMetadata.get("note") != null - // ? sourceAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(sourceNoteValue)) { - // fail( - // "Notes field was not properly set in source attachment. Expected: " - // + notesValue - // + ", Got: " - // + sourceNoteValue); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } - // // Create target entity - // copyCustomTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyCustomTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } - // // Copy attachment to target entity - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(0)); // Use objectId from array + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, - // objectIdsToCopy); + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse); + } - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity: " + copyResponse); - // } + // Move attachments from source to target with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // // Save target entity - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity"); - // } + // Verify all attachments moved to target + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals( + sourceAttachmentIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all attachments after move"); + + // Verify notes and secondary properties are preserved + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); + + // Verify notes are preserved + if (detailedMetadata.containsKey("note")) { + assertEquals( + notesValue, + detailedMetadata.get("note"), + "Notes should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Notes property missing after move for attachment: " + targetAttachmentId); + } - // // Fetch target entity attachments metadata - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + // Verify custom property is preserved + if (detailedMetadata.containsKey("customProperty2")) { + assertEquals( + customProperty2Value, + detailedMetadata.get("customProperty2"), + "Custom property should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Custom property missing after move for attachment: " + targetAttachmentId); + } + } - // if (targetAttachmentsMetadata.isEmpty()) { - // fail("No attachments found in target entity"); - // } + // Verify source entity has no attachments (all moved with sourceFacet) + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals(0, sourceMetadataAfterMove.size(), "Source entity has no attachments after move"); - // // Verify the copied attachment has the same notes value - // Map copiedAttachmentMetadata = targetAttachmentsMetadata.get(0); - // String copiedNoteValue = - // copiedAttachmentMetadata.get("note") != null - // ? copiedAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(copiedNoteValue)) { - // fail( - // "Notes field was not properly copied. Expected: " - // + notesValue - // + ", Got: " - // + copiedNoteValue); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // // Verify attachment content can be read from target entity - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + @Test + @Order(68) + public void testMoveAttachmentsWithoutSourceFacet() throws Exception { + System.out.println( + "Test (68): Move valid attachments from Source Entity to Target Entity without sourceFacet"); - // if (readResponse.equals("OK")) { - // testStatus = true; - // } - // if (!testStatus) { - // fail("Could not verify that notes field was copied from source to target attachment"); - // } - // } + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // @Test - // @Order(38) - // void testCopyAttachmentWithSecondaryPropertiesField() throws IOException { - // System.out.println( - // "Test (38): Verify that secondary properties are preserved when copying attachments - // between entities"); - // Boolean testStatus = false; - - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyCustomSourceEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity"); - // } + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID from first attachment + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } else { + fail("Attachment metadata does not contain objectId"); + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // String sourceAttachmentId = createResponse.get(1); - - // // Update attachment with secondary properties - // // DocumentInfoRecordBoolean : Set to true - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, - // bodyBoolean); - - // if (!updateSecondaryPropertyResponse1.equals("Updated")) { - // fail( - // "Could not update attachment DocumentInfoRecordBoolean field. Response: " - // + updateSecondaryPropertyResponse1); - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // // customProperty2 : Set to 12345 - // Integer customProperty2Value = 12345; - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); - - // if (!updateSecondaryPropertyResponse2.equals("Updated")) { - // fail( - // "Could not update attachment customProperty2 field. Response: " - // + updateSecondaryPropertyResponse2); - // } + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // // Save source entity - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity. Response: " + saveSourceResponse); - // } + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } - // // Fetch attachment metadata to get objectId and verify secondary properties - // Map sourceAttachmentMetadata = - // api.fetchMetadata( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + // Move attachments without sourceFacet (pass null for sourceFacet parameter) + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId"); - // } + // Verify attachments are in target entity + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals( + moveObjectIds.size(), + targetMetadataAfterMove.size(), + "Target entity should have all moved attachments"); + + // Verify attachments can be read from target entity + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + String readResponse = + api.readAttachment(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); + if (!readResponse.equals("OK")) { + fail("Could not read moved attachment from target entity"); + } + } - // // Store objectId in array for reuse - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // if (sourceObjectIds.size() < 2) { - // sourceObjectIds.add(sourceObjectId); - // } else { - // sourceObjectIds.set(1, sourceObjectId); - // } + // Expected Behavior: Attachments remain in source entity UI (without sourceFacet) + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + moveObjectIds.size(), + sourceMetadataAfterMove.size(), + "Source entity should still have attachments in UI when sourceFacet is not specified"); - // // Verify all secondary properties in source attachment - // Boolean sourceCustomProperty6 = - // sourceAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) sourceAttachmentMetadata.get("customProperty6") - // : null; - // Integer sourceCustomProperty2 = - // sourceAttachmentMetadata.get("customProperty2") != null - // ? (Integer) sourceAttachmentMetadata.get("customProperty2") - // : null; - - // if (sourceCustomProperty6 == null || !sourceCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly set in source attachment. Expected: true, - // Got: " - // + sourceCustomProperty6); - // } + // Verify the same objectIds are still visible in source + for (Map metadata : sourceMetadataAfterMove) { + String objectId = (String) metadata.get("objectId"); + assertTrue( + moveObjectIds.contains(objectId), + "Source entity should still show attachment with objectId: " + objectId); + } - // if (!customProperty2Value.equals(sourceCustomProperty2)) { - // fail( - // "customProperty2 was not properly set in source attachment. Expected: " - // + customProperty2Value - // + ", Got: " - // + sourceCustomProperty2); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // String editTargetResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!editTargetResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity"); - // } + @Test + @Order(69) + public void testMoveAttachmentsToEntityWithDuplicateWithoutSourceFacet() throws Exception { + System.out.println( + "Test (69): Move attachments into existing Target Entity when duplicate exists without sourceFacet"); - // // Copy attachment to target entity - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(1)); // Use objectId from array + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, - // objectIdsToCopy); + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity: " + copyResponse); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Save target entity - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity"); - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // // Fetch target entity attachments metadata - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // if (targetAttachmentsMetadata.isEmpty()) { - // fail("No attachments found in target entity"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID from first attachment + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } else { + fail("Attachment metadata does not contain objectId"); + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // // Verify the copied attachment has the same secondary properties - // // Find the attachment we just copied by matching the filename - // Map copiedAttachmentMetadata = - // targetAttachmentsMetadata.stream() - // .filter(attachment -> "sample1.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // if (copiedAttachmentMetadata == null) { - // fail("Could not find the copied attachment with file in target entity"); - // } + // Create target entity and add duplicate attachment (sample.pdf) + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // Boolean copiedCustomProperty6 = - // copiedAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) copiedAttachmentMetadata.get("customProperty6") - // : null; - // Integer copiedCustomProperty2 = - // copiedAttachmentMetadata.get("customProperty2") != null - // ? (Integer) copiedAttachmentMetadata.get("customProperty2") - // : null; - - // // Verify DocumentInfoRecordBoolean - // if (copiedCustomProperty6 == null || !copiedCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean as not properly copied. Expected: true, Got: " - // + copiedCustomProperty6); - // } + // Add the same first file (sample.pdf) to target entity to create duplicate + Map targetPostData = new HashMap<>(); + targetPostData.put("up__ID", moveTargetEntity); + targetPostData.put("mimeType", "application/pdf"); + targetPostData.put("createdAt", new Date().toString()); + targetPostData.put("createdBy", "test@test.com"); + targetPostData.put("modifiedBy", "test@test.com"); - // // Verify customProperty2 - // if (!customProperty2Value.equals(copiedCustomProperty2)) { - // fail( - // "customProperty2 was not properly copied. Expected: " - // + customProperty2Value - // + ", Got: " - // + copiedCustomProperty2); - // } + List createTargetResponse = + api.createAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + srvpath, + targetPostData, + files.get(0)); // Add same file (sample.pdf) + if (!createTargetResponse.get(0).equals("Attachment created")) { + fail("Could not create duplicate attachment in target entity"); + } - // // Verify attachment content can be read from target entity - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + // Save target entity before move + String saveTargetResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity: " + saveTargetResponse); + } - // if (readResponse.equals("OK")) { - // testStatus = true; - // } - // if (!testStatus) { - // fail( - // "Could not verify that all secondary properties were copied from source to target - // attachment"); - // } - // } + // Get initial target metadata count + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + int initialTargetCount = targetMetadataBeforeMove.size(); - // @Test - // @Order(39) - // void testCopyAttachmentWithNotesAndSecondaryPropertiesField() throws IOException { - // System.out.println( - // "Test (39): Verify that both notes field and secondary properties are preserved during - // attachment copy"); - // Boolean testStatus = false; - - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // copyCustomSourceEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity"); - // } + // Step 3: Move attachments without sourceFacet (duplicate should be skipped) + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample2.pdf").getFile()); + // Expected Behavior - Verify duplicate was skipped, other attachments moved + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // Map postData = new HashMap<>(); - // postData.put("up__ID", copyCustomSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + int nonDuplicateCount = moveObjectIds.size() - 1; + int expectedTargetCount = initialTargetCount + nonDuplicateCount; - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyCustomSourceEntity, srvpath, postData, file); + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target entity should have initial attachments plus non-duplicate moved attachments"); - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment"); - // } + // Verify at least one non-duplicate attachment was moved + assertTrue( + targetMetadataAfterMove.size() > initialTargetCount, + "Target should have more attachments after move (non-duplicates added)"); - // String sourceAttachmentId = createResponse.get(1); - - // // Update attachment with notes field - // String notesValue = "This attachment has both notes and secondary properties for testing"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // copyCustomSourceEntity, - // sourceAttachmentId, - // updateNotesBody); - - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update attachment notes field"); - // } + // Verify all attachments still remain in source entity UI (without sourceFacet) + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + moveObjectIds.size(), + sourceMetadataAfterMove.size(), + "Source entity should still have all attachments in UI when sourceFacet is not specified"); + + // Verify all original objectIds are still visible in source + List sourceObjectIds = new ArrayList<>(); + for (Map metadata : sourceMetadataAfterMove) { + sourceObjectIds.add((String) metadata.get("objectId")); + } + for (String objectId : moveObjectIds) { + assertTrue( + sourceObjectIds.contains(objectId), + "Source entity should still show attachment with objectId: " + objectId); + } - // // Update attachment with secondary properties - // // DocumentInfoRecordBoolean : Set to true - // RequestBody bodyBoolean = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty6\" : " + true + "\n}")); - // String updateSecondaryPropertyResponse1 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, - // bodyBoolean); - - // if (!updateSecondaryPropertyResponse1.equals("Updated")) { - // fail( - // "Could not update attachment DocumentInfoRecordBoolean (customProperty6) field. - // Response: " - // + updateSecondaryPropertyResponse1); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // // customProperty2 : Set to 99999 - // Integer customProperty2Value = 99999; - // RequestBody bodyInt = - // RequestBody.create( - // MediaType.parse("application/json"), - // ByteString.encodeUtf8("{\n \"customProperty2\" : " + customProperty2Value + "\n}")); - // String updateSecondaryPropertyResponse2 = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId, bodyInt); - - // if (!updateSecondaryPropertyResponse2.equals("Updated")) { - // fail( - // "Could not update attachment customProperty2 field. Response: " - // + updateSecondaryPropertyResponse2); - // } + @Test + @Order(70) + public void testMoveAttachmentsWithNotesAndSecondaryPropertiesWithoutSourceFacet() + throws Exception { + System.out.println( + "Test (70): Move attachments with notes and secondary properties without sourceFacet"); - // // Save source entity - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity. Response: " + saveSourceResponse); - // } + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // // Fetch attachment metadata to get objectId and verify notes and secondary properties - // Map sourceAttachmentMetadata = - // api.fetchMetadata( - // appUrl, entityName, facetName, copyCustomSourceEntity, sourceAttachmentId); + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // if (!sourceAttachmentMetadata.containsKey("objectId")) { - // fail("Source attachment metadata does not contain objectId"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // String sourceObjectId = sourceAttachmentMetadata.get("objectId").toString(); - // if (sourceObjectIds.size() < 3) { - // sourceObjectIds.add(sourceObjectId); - // } else { - // sourceObjectIds.set(2, sourceObjectId); - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // String sourceNoteValue = - // sourceAttachmentMetadata.get("note") != null - // ? sourceAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(sourceNoteValue)) { - // fail( - // "Notes field was not properly set in source attachment. Expected: " - // + notesValue - // + ", Got: " - // + sourceNoteValue); - // } + // Add notes to attachments + String notesValue = "Test note for migration verification"; + MediaType mediaType = MediaType.parse("application/json"); + String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; + RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); + + for (String attachmentId : sourceAttachmentIds) { + String updateNotesResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, attachmentId, updateNotesBody); + if (!updateNotesResponse.equals("Updated")) { + fail("Could not update notes for attachment: " + attachmentId); + } + } - // Boolean sourceCustomProperty6 = - // sourceAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) sourceAttachmentMetadata.get("customProperty6") - // : null; - // Integer sourceCustomProperty2 = - // sourceAttachmentMetadata.get("customProperty2") != null - // ? (Integer) sourceAttachmentMetadata.get("customProperty2") - // : null; - - // if (sourceCustomProperty6 == null || !sourceCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly set in source attachment. Expected: true, - // Got: " - // + sourceCustomProperty6); - // } + // Add custom property to attachments + Integer customProperty2Value = 54321; + RequestBody bodyInt = + RequestBody.create( + "{\"customProperty2\": " + customProperty2Value + "}", + MediaType.parse("application/json")); + + for (String attachmentId : sourceAttachmentIds) { + String updateCustomPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, attachmentId, bodyInt); + if (!updateCustomPropertyResponse.equals("Updated")) { + fail("Could not update custom property for attachment: " + attachmentId); + } + } - // if (!customProperty2Value.equals(sourceCustomProperty2)) { - // fail( - // "customProperty2 was not properly set in source attachment. Expected: " - // + customProperty2Value - // + ", Got: " - // + sourceCustomProperty2); - // } + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // String editTargetResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!editTargetResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } - // // Copy attachment to target entity - // List objectIdsToCopy = new ArrayList<>(); - // objectIdsToCopy.add(sourceObjectIds.get(2)); // Use objectId from array + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyCustomTargetEntity, - // objectIdsToCopy); + // Get source attachment count before move + List> sourceMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy attachment to target entity: " + copyResponse); - // } + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // // Save target entity - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyCustomTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity"); - // } + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } - // // Fetch target entity attachments metadata - // List> targetAttachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyCustomTargetEntity); + // Get target attachment count before move + List> targetMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + int targetCountBeforeMove = targetMetadataBeforeMove.size(); - // if (targetAttachmentsMetadata.isEmpty()) { - // fail("No attachments found in target entity"); - // } + // Move attachments from source to target WITHOUT sourceFacet + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // // Verify the copied attachment has the same notes and secondary properties - // // Find the attachment we just copied by matching the filename - // Map copiedAttachmentMetadata = - // targetAttachmentsMetadata.stream() - // .filter(attachment -> "sample2.pdf".equals(attachment.get("fileName"))) - // .findFirst() - // .orElse(null); + // Verify expected number of attachments moved to target + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + int expectedTargetCount = targetCountBeforeMove + sourceAttachmentIds.size(); + assertEquals( + expectedTargetCount, + targetMetadataAfterMove.size(), + "Target entity should have " + expectedTargetCount + " attachments after move"); + + // Verify notes and secondary properties are preserved + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); + + // Verify notes are preserved + if (detailedMetadata.containsKey("note")) { + assertEquals( + notesValue, + detailedMetadata.get("note"), + "Notes should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Notes property missing after move for attachment: " + targetAttachmentId); + } - // if (copiedAttachmentMetadata == null) { - // fail("Could not find the copied attachment with fil in target entity"); - // } + // Verify custom property is preserved + if (detailedMetadata.containsKey("customProperty2")) { + assertEquals( + customProperty2Value, + detailedMetadata.get("customProperty2"), + "Custom property should be preserved after move for attachment: " + targetAttachmentId); + } else { + fail("Custom property missing after move for attachment: " + targetAttachmentId); + } + } - // // Verify notes field was copied - // String copiedNoteValue = - // copiedAttachmentMetadata.get("note") != null - // ? copiedAttachmentMetadata.get("note").toString() - // : null; - - // if (!notesValue.equals(copiedNoteValue)) { - // fail( - // "Notes field was not properly copied. Expected: " - // + notesValue - // + ", Got: " - // + copiedNoteValue); - // } + // Verify source entity still has all attachments (without sourceFacet) + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + sourceCountBeforeMove, + sourceMetadataAfterMove.size(), + "Source entity should still have " + + sourceCountBeforeMove + + " attachments (without sourceFacet)"); + + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // // Verify secondary properties were copied - // Boolean copiedCustomProperty6 = - // copiedAttachmentMetadata.get("customProperty6") != null - // ? (Boolean) copiedAttachmentMetadata.get("customProperty6") - // : null; - // Integer copiedCustomProperty2 = - // copiedAttachmentMetadata.get("customProperty2") != null - // ? (Integer) copiedAttachmentMetadata.get("customProperty2") - // : null; - - // // Verify DocumentInfoRecordBoolean - // if (copiedCustomProperty6 == null || !copiedCustomProperty6) { - // fail( - // "DocumentInfoRecordBoolean was not properly copied. Expected: true, Got: " - // + copiedCustomProperty6); - // } + @Test + @Order(71) + public void testMoveAttachmentsWithInvalidOrUndefinedSecondaryProperties() throws Exception { + System.out.println( + "Test (71): Move attachments with invalid or undefined secondary properties"); - // // Verify customProperty2 - // if (!customProperty2Value.equals(copiedCustomProperty2)) { - // fail( - // "customProperty2 was not properly copied. Expected: " - // + customProperty2Value - // + ", Got: " - // + copiedCustomProperty2); - // } + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // // Verify attachment content can be read from target entity - // String targetAttachmentId = (String) copiedAttachmentMetadata.get("ID"); - // String readResponse = - // api.readAttachment( - // appUrl, entityName, facetName, copyCustomTargetEntity, targetAttachmentId); + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); - // if (readResponse.equals("OK")) { - // testStatus = true; - // } - // if (!testStatus) { - // fail( - // "Could not verify that notes field and all secondary properties were copied from source - // to target attachment"); - // } - // api.deleteEntity(appUrl, entityName, copyCustomSourceEntity); - // api.deleteEntity(appUrl, entityName, copyCustomTargetEntity); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(40) - // void testCopyAttachmentsSuccessExistingEntity() throws IOException { - // System.out.println("Test (40): Copy attachments from one entity to another existing entity"); - // List attachments = new ArrayList<>(); - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // File file1 = new File(classLoader.getResource("sample.pdf").getFile()); - // File file2 = new File(classLoader.getResource("sample1.pdf").getFile()); - // File tempFile1 = new File(System.getProperty("java.io.tmpdir"), - // "sample_copy_existing_1.pdf"); - // Files.copy(file1.toPath(), tempFile1.toPath(), StandardCopyOption.REPLACE_EXISTING); - // File tempFile2 = new File(System.getProperty("java.io.tmpdir"), - // "sample_copy_existing_2.pdf"); - // Files.copy(file2.toPath(), tempFile2.toPath(), StandardCopyOption.REPLACE_EXISTING); - // files.add(tempFile1); - // files.add(tempFile2); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String editResponse2 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (editResponse1.equals("Entity in draft mode") - // && editResponse2.equals("Entity in draft mode")) { - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (String attachment : attachments) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadata( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // sourceObjectIds.clear(); - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } + // Add valid secondary properties to first attachment (customProperty2) + String validAttachmentId = sourceAttachmentIds.get(0); + Integer validCustomProperty2Value = 12345; + RequestBody validPropertyBody = + RequestBody.create( + "{\"customProperty2\": " + validCustomProperty2Value + "}", + MediaType.parse("application/json")); + + String validPropertyResponse = + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, validAttachmentId, validPropertyBody); + if (!validPropertyResponse.equals("Updated")) { + fail("Could not update valid property for attachment: " + validAttachmentId); + } - // if (sourceObjectIds.size() == 2) { - // String copyResponse; - // copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); - // if (copyResponse.equals("Attachments copied successfully")) { - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadata(appUrl, entityName, facetName, - // copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // if (targetAttachmentIds.size() == 4) { - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } - // // api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); - // // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + // add invalid secondary properties to second attachment (non-existent property) + String invalidAttachmentId = sourceAttachmentIds.get(1); + RequestBody invalidPropertyBody = + RequestBody.create( + "{\"nonExistentProperty\": \"invalid\"}", MediaType.parse("application/json")); + + api.updateSecondaryProperty( + appUrl, entityName, facetName, moveSourceEntity, invalidAttachmentId, invalidPropertyBody); + + // add undefined properties to third attachment + String undefinedAttachmentId = sourceAttachmentIds.get(2); + RequestBody undefinedPropertyBody = + RequestBody.create( + "{\"undefinedField\": \"test\", \"anotherUndefined\": 999}", + MediaType.parse("application/json")); + + api.updateSecondaryProperty( + appUrl, + entityName, + facetName, + moveSourceEntity, + undefinedAttachmentId, + undefinedPropertyBody); + + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // @Test - // @Order(41) - // void testCopyAttachmentsUnsuccessfulExistingEntity() throws IOException { - // System.out.println( - // "Test (41): Copy attachments from one entity to another existing entity - unsuccessful"); - // String editResponse1 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // String editResponse2 = - // api.editEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (editResponse1.equals("Entity in draft mode") - // && editResponse2.equals("Entity in draft mode")) { - // sourceObjectIds.add("incorrectObjectId"); - // if (sourceObjectIds.size() == 3) { - // try { - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); - // fail("Copy attachments did not throw an error"); - // } catch (IOException e) { - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentSourceEntity); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentSourceEntity); - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not edit entities"); - // } - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (Exception e) { + fail("Could not fetch metadata for attachment: " + attachmentId); + } + } - // @Test - // @Order(42) - // void testCreateLinkSuccess() throws IOException { - // System.out.println("Test (42): Create link in entity"); - // List attachments = new ArrayList<>(); - // createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!createLinkEntity.equals("Could not create entity")) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse1 = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // String createLinkResponse2 = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName + "1", - // linkUrl); - // if (createLinkResponse1.equals("Link created successfully") - // && createLinkResponse2.equals("Link created successfully")) { - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (saveEntityResponse.equals("Saved")) { - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String openAttachmentResponse; - // for (String attachment : attachments) { - // openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, createLinkEntity, attachment); - // System.out.println("openAttachmentResponse: " + openAttachmentResponse); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open created link"); - // } - // } - // } else { - // fail("Could not save entity"); - // } - // } else { - // fail("Could not create link"); - // } - // } else { - // fail("Could not create entity"); - // } - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch all objectIds from source entity"); + } - // @Test - // @Order(43) - // void testCreateLinkDifferentEntity() throws IOException { - // System.out.println("Test (43): Create link with same name in different entity"); - // String createLinkDifferentEntity = - // api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!createLinkDifferentEntity.equals("Could not edit entity")) { - // String linkName = "sample"; - // String linkUrl = "https://example.com"; - // String createResponse = - // api.createLink( - // appUrl, entityName, facetName, createLinkDifferentEntity, linkName, linkUrl); - // if (!createResponse.equals("Link created successfully")) { - // fail("Could not create link in different entity with same name"); - // } - // String response = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkDifferentEntity); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } - // response = api.deleteEntity(appUrl, entityName, createLinkDifferentEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } else { - // fail("Could not edit entity"); - // } - // } + // Get source attachment count before move + List> sourceMetadataBeforeMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); - // @Test - // @Order(44) - // void testCreateLinkFailure() throws IOException { - // System.out.println("Test (44): Create link fails due to invalid URL and name"); - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Could not edit entity")) { - // String linkName = "sample"; - // String linkUrl = "example.com"; - // try { - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // fail("Create link did not throw an error for invalid url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("400018", errorCode); - // assertTrue( - // errorMessage.equals("Enter a value that is within the expected pattern.") - // || errorMessage.equals("Enter a value that matches the expected pattern."), - // "Unexpected error message: " + errorMessage); - // } - // try { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName + "//", "https://" + - // linkUrl); - // fail("Create link did not throw an error for invalid name"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = - // "\"sample//\" contains unsupported characters (‘/’ or ‘\\’). Rename and try again."; - // assertEquals("500", errorCode); - // assertEquals( - // expected.replaceAll("\\s+", " ").trim(), errorMessage.replaceAll("\\s+", " - // ").trim()); - // } - // try { - // api.createLink(appUrl, entityName, facetName, createLinkEntity, "", ""); - // fail("Create link did not throw an error for empty name and url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = "Provide the missing value."; - // assertEquals("409008", errorCode); - // assertEquals(expected, errorMessage); - // } - // try { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName, "https://" + linkUrl); - // fail("Create link did not throw an error for duplicate name"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals( - // "An object named \"sample\" already exists. Rename the object and try again.", - // errorMessage); - // } - // try { - // for (int i = 2; i < 5; i++) { - // api.createLink( - // appUrl, entityName, facetName, createLinkEntity, linkName + i, "https://" + - // linkUrl); - // } - // fail("More than 5 links were created in the same entity"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals("Cannot upload more than 4 attachments.", errorMessage); - // } - // String response = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } - // response = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } else { - // fail("Could not edit entity"); - // } - // } + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // @Test - // @Order(45) - // void testCreateLinkNoSDMRoles() throws IOException { - // System.out.println("Test (45): Create link fails due to no SDM roles assigned"); - // String createLinkEntityNoSDMRoles = - // apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!createLinkEntityNoSDMRoles.equals("Could not edit entity")) { - // String linkName = "sample27"; - // String linkUrl = "https://example.com"; - // try { - // apiNoRoles.createLink( - // appUrl, entityName, facetName, createLinkEntityNoSDMRoles, linkName, linkUrl); - // fail("Link got created without SDM roles"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals( - // "You do not have the required permissions to upload attachments. Please contact your - // administrator for access.", - // errorMessage); - // } - // String response = - // apiNoRoles.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntityNoSDMRoles); - // if (!response.equals("Saved")) { - // fail("Could not save entity"); - // } - // response = api.deleteEntity(appUrl, entityName, createLinkEntityNoSDMRoles); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } else { - // fail("Could not edit entity"); - // } - // } + // Save target before move + String saveTargetBeforeMoveResponse68 = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse68.equals("Saved")) { + fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse68); + } - // @Test - // @Order(46) - // void testDeleteLink() throws IOException { - // System.out.println("Test (46): Delete link in entity"); - // List attachments = new ArrayList<>(); - // String createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!createLinkEntity.equals("Could not create entity")) { - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (createLinkResponse.equals("Link created successfully")) { - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (saveEntityResponse.equals("Saved")) { - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String editEntityResponse = - // api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } - // String deleteLinkResponse = - // api.deleteAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(0)); - // if (!deleteLinkResponse.equals("Deleted")) { - // fail("Could not delete created link"); - // } else { - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // if (attachments.size() != 0) { - // fail("Link wasn't deleted"); - // } - // String response = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!response.equals("Entity Deleted")) { - // fail("Could not delete entity"); - // } - // } - // } else { - // fail("Could not save entity"); - // } - // } else { - // fail("Could not create link"); - // } - // } else { - // fail("Could not create entity"); - // } - // } + // Move attachments from source to target with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // @Test - // @Order(47) - // void testRenameLinkSuccess() throws IOException { - // System.out.println("Test (47): Rename link in entity"); - // List attachments = new ArrayList<>(); + // Verify attachments moved to target + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // createLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (createLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } + assertTrue( + targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); + assertEquals( + sourceCountBeforeMove, + targetMetadataAfterMove.size(), + "All attachments should move (invalid properties are ignored)"); + + // Verify only allowed properties are populated in target + for (Map metadata : targetMetadataAfterMove) { + String targetAttachmentId = (String) metadata.get("ID"); + assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); + + // Fetch detailed metadata to verify properties + Map detailedMetadata = + api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); + + // Check if this is the attachment with valid customProperty2 + if (detailedMetadata.containsKey("customProperty2") + && detailedMetadata.get("customProperty2") != null) { + assertEquals( + validCustomProperty2Value, + detailedMetadata.get("customProperty2"), + "Valid customProperty2 should be preserved"); + } + } - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } + // Verify source entity has no attachments + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterMove.size(), + "Source entity should have no attachments after move with sourceFacet"); - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + @Test + @Order(72) + public void testMoveAttachmentsFromSourceEntityInDraftMode() throws Exception { + System.out.println( + "Test (72): Move attachments from Source Entity when Source Entity is in draft mode"); - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + // Create source entity and keep it in draft mode + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // attachmentID9 = attachments.get(0); - // String renameLinkResponse = - // api.renameAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(0), - // "sampleRenamed"); - // if (!renameLinkResponse.equals("Renamed")) fail("Could not Renamed created link"); + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); + files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(48) - // void testRenameLinkDuplicate() throws IOException { - // System.out.println("Test (48): Rename link in entity fails due to duplicate error"); - // List attachments = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } + // Verify attachments are added to source entity + int sourceCountBeforeMove = sourceAttachmentIds.size(); + assertTrue(sourceCountBeforeMove > 0, "Source entity should have attachments before move"); + assertEquals( + files.size(), sourceCountBeforeMove, "Source should have " + files.size() + " attachments"); - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID from first attachment + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // .filter(item -> !attachmentID9.equals(item.get("ID"))) // skip unwanted filename - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // attachmentID10 = attachments.get(0); - // api.renameAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(0), "sampleRenamed"); - - // String saveError = - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // String expectedWarning = - // "{\"error\":{\"code\":\"400\",\"message\":\"An object named \\\"sampleRenamed\\\" already - // exists. Rename the object and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // ObjectMapper mapper = new ObjectMapper(); - // assertEquals(mapper.readTree(expectedWarning), mapper.readTree(saveError)); - - // String deleteEntityResponse = api.deleteEntityDraft(appUrl, entityName, createLinkEntity); - // if (!deleteEntityResponse.equals("Entity Draft Deleted")) { - // fail("Entity draft not deleted"); - // } - // } + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // @Test - // @Order(49) - // void testRenameLinkUnsupportedCharacters() throws IOException { - // System.out.println( - // "Test (49): Rename link in entity fails due to unsupported characters in name"); - // List attachments = new ArrayList<>(); + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + String editSourceResponse = api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!editSourceResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity back to draft mode"); + } - // String linkName = "sample2"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, createLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // createLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } + // Save target before move + String saveTargetResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetResponse.equals("Saved")) { + fail("Could not save target entity: " + saveTargetResponse); + } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, createLinkEntity).stream() - // // .filter(item -> "sample2".equals(item.get("filename"))) // skip unwanted filename - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // System.out.println("attachments: " + attachments); - - // editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } + // Move attachments from draft source to target using sourceFacet + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + null); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // api.renameAttachment( - // appUrl, entityName, facetName, createLinkEntity, attachments.get(0), "sampleRenamed//"); - // String warning = - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, createLinkEntity); - // String expectedWarning = - // "{\"error\":{\"code\":\"400\",\"message\":\"\\\"sampleRenamed//\\\" contains unsupported - // characters (‘/’ or ‘\\\\’). Rename and try again.\\n\\nTable: attachments\\nPage: - // IntegrationTestEntity\"}}"; - // ObjectMapper mapper = new ObjectMapper(); - // assertEquals(mapper.readTree(expectedWarning), mapper.readTree(warning)); - - // String deleteEntityResponse = api.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!deleteEntityResponse.equals("Entity Deleted")) { - // fail("Entity draft not deleted"); - // } - // } + // Verify attachments moved to target + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertTrue( + targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); + assertEquals( + sourceCountBeforeMove, + targetMetadataAfterMove.size(), + "Target should have " + sourceCountBeforeMove + " attachments after move"); - // @Test - // @Order(50) - // void testEditLinkSuccess() throws IOException { - // System.out.println("Test (50): Edit existing link in entity"); + // Verify all expected attachments are in target + Set targetFileNames = + targetMetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); - // List attachments = new ArrayList<>(); - // editLinkEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (editLinkEntity.equals("Could not create entity")) { - // fail("Could not create entity"); - // } - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; + for (File file : files) { + assertTrue( + targetFileNames.contains(file.getName()), + "Target should contain attachment: " + file.getName()); + } - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, editLinkEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link"); - // } + // Now save the source entity + String saveSourceAfterMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceAfterMoveResponse.equals("Saved")) { + fail("Could not save source entity after move: " + saveSourceAfterMoveResponse); + } - // String saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // if (attachments.isEmpty()) { - // fail("Could not edit link"); - // } - // String linkId = attachments.get(0); - // String updatedUrl = "https://editedexample.com"; - // String editLinkResponse = - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // if (!editLinkResponse.equals("Link edited successfully")) { - // fail("Could not edit link"); - // } - // saveEntityResponse = api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!saveEntityResponse.equals("Saved")) { - // fail("Could not save entity"); - // } - // String openAttachmentResponse; - // for (String attachment : attachments) { - // openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, editLinkEntity, attachment); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open created link"); - // } - // } - // } + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + sourceCountBeforeMove, + sourceMetadataAfterMove.size(), + "Source entity in draft mode retains attachments after move (copy behavior)"); - // @Test - // @Order(51) - // void testEditLinkFailureInvalidURL() throws IOException { - // System.out.println("Test (51): Edit existing link with invalid url"); - // Boolean testStatus = false; - // List attachments = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // if (attachments.isEmpty()) { - // fail("Could not edit link"); - // } - // String linkId = attachments.get(0); - // String updatedUrl = "https://editedexample"; - // try { - - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // fail("Create link did not throw an error for invalid url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("400018", errorCode); - // assertTrue( - // errorMessage.equals("Enter a value that is within the expected pattern.") - // || errorMessage.equals("Enter a value that matches the expected pattern."), - // "Unexpected error message: " + errorMessage); - - // testStatus = true; - // } - // api.saveEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!testStatus) { - // fail("Could not edit link with an invalid URL"); - // } - // } + Set sourceFileNamesAfterMove = + sourceMetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); - // @Test - // @Order(52) - // void testEditLinkFailureEmptyURL() throws IOException { - // System.out.println("Test (52): Edit existing link with an empty url"); - // Boolean testStatus = false; - // List attachments = new ArrayList<>(); - - // String editEntityResponse = api.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } - // attachments = - // api.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // if (attachments.isEmpty()) { - // fail("Could not edit link"); - // } - // String linkId = attachments.get(0); - // String updatedUrl = ""; - // try { - // api.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // fail("edit link did not throw an error for empty url"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // String expected = "Provide the missing value."; - // assertEquals("409008", errorCode); - // assertEquals(expected, errorMessage); - // testStatus = true; - // } - // api.deleteEntityDraft(appUrl, entityName, editLinkEntity); - // if (!testStatus) { - // fail("Could not edit link with an empty URL"); - // } - // } + for (File file : files) { + assertTrue( + sourceFileNamesAfterMove.contains(file.getName()), + "Source (draft) should still contain attachment: " + file.getName()); + } - // @Test - // @Order(53) - // void testEditLinkNoSDMRoles() throws IOException { - // System.out.println("Test (53): Edit link fails due to no SDM roles assigned"); + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // Boolean testStatus = false; - // List attachments = new ArrayList<>(); + @Test + @Order(73) + public void testEditAttachmentFileNameAndMoveToTarget() throws Exception { + System.out.println( + "Test (73): Edit attachment file name in Source Entity and move it to Target Entity"); - // String editEntityResponse = - // apiNoRoles.editEntityDraft(appUrl, entityName, srvpath, editLinkEntity); - // if (!editEntityResponse.equals("Entity in draft mode")) { - // fail("Could not edit entity"); - // } - // attachments = - // apiNoRoles.fetchEntityMetadata(appUrl, entityName, facetName, editLinkEntity).stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - - // if (attachments.isEmpty()) { - // fail("Could not edit link"); - // } - // String linkId = attachments.get(0); - // String updatedUrl = "https://www.example1.com"; - // try { - // apiNoRoles.editLink(appUrl, entityName, facetName, editLinkEntity, linkId, updatedUrl); - // fail("Link got edited without SDM roles in facet: \" + facetName"); - // } catch (IOException e) { - // String message = e.getMessage(); - // int jsonStart = message.indexOf("{"); - // String jsonPart = message.substring(jsonStart); - // JSONObject json = new JSONObject(jsonPart); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("500", errorCode); - // assertEquals( - // "You do not have the required permissions to update attachments. Kindly contact the - // admin", - // errorMessage); - // testStatus = true; - // } - // apiNoRoles.deleteEntity(appUrl, entityName, createLinkEntity); - // if (!testStatus) { - // fail("Link got edited without SDM roles"); - // } - // api.deleteEntity(appUrl, entityName, editLinkEntity); - // } + // Create source entity and add attachment + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // @Test - // @Order(54) - // void testCopyLinkSuccessNewEntity() throws IOException { - // System.out.println("Test (54): Copy link from one entity to another new entity"); - // List> attachmentsMetadata = new ArrayList<>(); + // Add attachment with original name (sample.txt) + ClassLoader classLoader = getClass().getClassLoader(); + File originalFile = new File(classLoader.getResource("sample.txt").getFile()); - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (copyLinkSourceEntity.equals("Could not create entity") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not create source or target entity"); - // } + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, originalFile); + if (!createResponse.get(0).equals("Attachment created")) { + fail("Could not create attachment in source entity"); + } - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link in source entity"); - // } + String attachmentId = createResponse.get(1); + assertNotNull(attachmentId, "Attachment ID should not be null"); - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // List sourceObjectIds = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + // Verify original filename + List> metadataBeforeRename = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals(1, metadataBeforeRename.size(), "Source should have 1 attachment"); + assertEquals( + "sample.txt", + metadataBeforeRename.get(0).get("fileName"), + "Original filename should be sample.txt"); + + // Edit source entity back to draft mode + String editSourceResponse = api.editEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!editSourceResponse.equals("Entity in draft mode")) { + fail("Could not edit source entity to draft mode"); + } - // if (sourceObjectIds.isEmpty()) { - // fail("Could not fetch object Id for link"); - // } + // Rename the attachment to testEdited.txt + String newFileName = "testEdited.txt"; + String renameResponse = + api.renameAttachment( + appUrl, entityName, facetName, moveSourceEntity, attachmentId, newFileName); + assertEquals("Renamed", renameResponse, "Attachment should be renamed successfully"); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy link: " + copyResponse); - // } + // Save source entity after rename + saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity after rename: " + saveSourceResponse); + } - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveResponse.equals("Saved")) { - // fail("Could not save target entity after copying link"); - // } + // Verify renamed filename in source + List> metadataAfterRename = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals(1, metadataAfterRename.size(), "Source should still have 1 attachment"); + assertEquals( + newFileName, + metadataAfterRename.get(0).get("fileName"), + "Filename should be updated to " + newFileName); + + // Get objectId and folderId for move operation + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + String objectId = metadata.get("objectId").toString(); + moveSourceFolderId = metadata.get("folderId").toString(); + assertNotNull(objectId, "Object ID should not be null"); + assertNotNull(moveSourceFolderId, "Folder ID should not be null"); + + moveObjectIds.clear(); + moveObjectIds.add(objectId); + + // Create target entity + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity"); + } - // attachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); - - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch. Expected '" - // + expectedType - // + "' but got '" - // + receivedType - // + "'."); - - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); - - // System.out.println("Attachment type and URL validated successfully."); - - // String attachmentId = (String) copiedAttachment.get("ID"); - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open the attachment"); - // } + // Save target before move + String saveTargetBeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTargetBeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity before move"); + } - // String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // if (!deleteSourceResponse.equals("Entity Deleted") - // || !deleteTargetResponse.equals("Entity Deleted")) { - // fail("could not delete source or target entity"); - // } - // } + // Move attachment from source to target with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + fail("Move operation returned null result"); + } - // @Test - // @Order(55) - // void testCopyLinkUnsuccessfulNewEntity() throws IOException { - // System.out.println( - // "Test (55): Copy invalid type of link from one entity to another new entity"); + // Verify attachment moved to target with renamed filename + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals(1, targetMetadataAfterMove.size(), "Target should have 1 attachment after move"); + assertEquals( + newFileName, + targetMetadataAfterMove.get(0).get("fileName"), + "Target should have attachment with renamed filename: " + newFileName); - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // Verify attachment removed from source + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterMove.size(), + "Source entity should have no attachments after move with sourceFacet"); - // if (copyLinkSourceEntity.equals("Could not create entity") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not create source or target entity"); - // } + // Clean up - delete both entities + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // List invalidObjectIds = Collections.singletonList("incorrectObjectId"); + @Test + @Order(74) + public void testChainMoveAttachmentsFromSourceToTarget1ToTarget2() throws Exception { + System.out.println( + "Test (74): Move attachments from Source Entity to Target Entity 1 and then to Target Entity 2"); - // try { - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, invalidObjectIds); - // fail("Copy attachments did not throw error for invalid ID"); - // } catch (IOException e) { - // System.out.println("Caught expected error: " + e.getMessage()); - // } + // Create source entity and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveResponse.equals("Saved")) { - // fail("Could not save target entity after unsuccessful copy"); - // } + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // if (!deleteSourceResponse.equals("Entity Deleted")) { - // fail("Could not delete source entity"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(56) - // void testCopyLinkFromNewEntityToExistingEntity() throws IOException { - // System.out.println("Test (56): Copy link from a new entity to an existing target entity"); - // List> attachmentsMetadata = new ArrayList<>(); - - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyLinkSourceEntity.equals("Could not create entity")) { - // fail("Could not create new source entity"); - // } + // Create attachments in source entity + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // String linkName = "Sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link in new source entity"); - // } + // Save source entity + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save new source entity"); - // } + // Get count of attachments in source + int sourceCountInitial = sourceAttachmentIds.size(); + assertTrue(sourceCountInitial > 0, "Source should have attachments"); - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID from first attachment + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // List sourceObjectIds = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // if (sourceObjectIds.isEmpty()) { - // fail("Could not fetch objectId from new source entity"); - // } + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy link from new source entity to existing target entity: " + - // copyResponse); - // } + // Create Target Entity 1 + moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity 1"); + } - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); + // Save target1 before move + String saveTarget1BeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); + if (!saveTarget1BeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity 1 before move"); + } - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity after copying link"); - // } + // Move attachments from source to Target Entity 1 with sourceFacet + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult1 = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult1 == null) { + fail("Move operation from source to target 1 returned null result"); + } - // attachmentsMetadata = - // api.fetchEntityMetadata(appUrl, entityName, facetName, copyLinkTargetEntity); - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); + // Verify attachments moved to Target Entity 1 + List> target1MetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertTrue( + target1MetadataAfterMove.size() > 0, "Target entity 1 should have attachments after move"); + assertEquals( + sourceCountInitial, + target1MetadataAfterMove.size(), + "Target 1 should have " + sourceCountInitial + " attachments"); - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch. Expected '" - // + expectedType - // + "' but got '" - // + receivedType - // + "'."); + // Verify all expected files are in Target Entity 1 + Set target1FileNames = + target1MetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); + for (File file : files) { + assertTrue( + target1FileNames.contains(file.getName()), + "Target 1 should contain attachment: " + file.getName()); + } - // System.out.println("Attachment type and URL validated successfully."); + // Verify attachments removed from source + List> sourceMetadataAfterFirstMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + 0, + sourceMetadataAfterFirstMove.size(), + "Source entity should have no attachments after move to target 1"); + + // Create Target Entity 2 + String moveTargetEntity2 = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity2.equals("Could not create entity")) { + fail("Could not create target entity 2"); + } - // String attachmentId = (String) copiedAttachment.get("ID"); - // assertNotNull(attachmentId, "Could not find 'ID' in the copied attachment metadata."); + // Save target2 before move + String saveTarget2BeforeMoveResponse = + api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity2); + if (!saveTarget2BeforeMoveResponse.equals("Saved")) { + fail("Could not save target entity 2 before move"); + } - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open the attachment"); - // } + // Get new object IDs and folder ID from Target Entity 1 for second move + List target1AttachmentIds = new ArrayList<>(); + for (Map metadata : target1MetadataAfterMove) { + String attachmentId = metadata.get("ID").toString(); + target1AttachmentIds.add(attachmentId); + } - // String deleteResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // if (!deleteResponse.equals("Entity Deleted")) { - // fail("Could not delete new source entity"); - // } - // } + moveObjectIds.clear(); + String target1FolderId = null; + for (String attachmentId : target1AttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get folder ID from first attachment + if (target1FolderId == null && metadata.containsKey("folderId")) { + target1FolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata from target 1: " + e.getMessage()); + } + } - // @Test - // @Order(57) - // void testCopyInvalidLinkFromNewEntityToExistingEntity() throws IOException { - // System.out.println( - // "Test (57): Copy invalid type of link from new entity to existing target entity"); + assertNotNull(target1FolderId, "Target 1 folder ID should not be null"); - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (copyLinkSourceEntity.equals("Could not create entity")) { - // fail("Could not create new source entity"); - // } + // Move attachments from Target Entity 1 to Target Entity 2 with sourceFacet + Map moveResult2 = + api.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity2, + target1FolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult2 == null) { + fail("Move operation from target 1 to target 2 returned null result"); + } - // String linkName = "Sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link in new source entity"); - // } + // Verify attachments moved to Target Entity 2 + List> target2MetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity2); + assertTrue( + target2MetadataAfterMove.size() > 0, "Target entity 2 should have attachments after move"); + assertEquals( + sourceCountInitial, + target2MetadataAfterMove.size(), + "Target 2 should have " + sourceCountInitial + " attachments"); - // String saveSourceResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save new source entity"); - // } + // Verify all expected files are in Target Entity 2 + Set target2FileNames = + target2MetadataAfterMove.stream() + .map(m -> (String) m.get("fileName")) + .collect(java.util.stream.Collectors.toSet()); - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!editResponse.equals("Entity in draft mode")) { - // fail("Could not edit target entity draft"); - // } + for (File file : files) { + assertTrue( + target2FileNames.contains(file.getName()), + "Target 2 should contain attachment: " + file.getName()); + } - // List invalidObjectIds = Collections.singletonList("invalidObjectId123"); + // Verify attachments removed from Target Entity 1 + List> target1MetadataAfterSecondMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals( + 0, + target1MetadataAfterSecondMove.size(), + "Target entity 1 should have no attachments after move to target 2"); + + // Clean up - delete all three entities + api.deleteEntity(appUrl, entityName, moveTargetEntity2); + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // try { - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, invalidObjectIds); - // fail("Copy did not throw error for invalid link ID"); - // } catch (IOException e) { - // System.out.println("Caught expected error while copying invalid link: " + e.getMessage()); - // } + @Test + @Order(75) + public void testMoveAttachmentsWithoutSDMRole() throws Exception { + System.out.println("Test (75): Move attachments when user does not have SDM Role"); + + // Create source entity with SDM role and add attachments + moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveSourceEntity.equals("Could not create entity")) { + fail("Could not create source entity"); + } - // // No need to wait for upload completion as copy failed, but ensure clean state - // String saveTargetResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity after unsuccessful copy"); - // } + // Prepare sample files + ClassLoader classLoader = getClass().getClassLoader(); + List files = new ArrayList<>(); + files.add(new File(classLoader.getResource("sample.pdf").getFile())); + files.add(new File(classLoader.getResource("sample.txt").getFile())); - // String deleteSourceResponse = api.deleteEntity(appUrl, entityName, copyLinkSourceEntity); - // String deleteTargetResponse = api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // if (!deleteSourceResponse.equals("Entity Deleted") - // || !deleteTargetResponse.equals("Entity Deleted")) { - // fail("Could not delete new source entity or target entity"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", moveSourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // @Test - // @Order(58) - // void testCopyLinkSuccessNewEntityDraft() throws IOException { - // System.out.println("Test (58): Copy link from one entity to another new entity draft mode"); + // Create attachments in source entity with SDM role + List sourceAttachmentIds = new ArrayList<>(); + for (File file : files) { + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); + if (createResponse.get(0).equals("Attachment created")) { + sourceAttachmentIds.add(createResponse.get(1)); + } else { + fail("Could not create attachment in source entity"); + } + } - // copyLinkSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyLinkTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // Save source entity with SDM role + String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); + if (!saveSourceResponse.equals("Saved")) { + fail("Could not save source entity: " + saveSourceResponse); + } - // if (copyLinkSourceEntity.equals("Could not create entity") - // || copyLinkTargetEntity.equals("Could not create entity")) { - // fail("Could not create source or target entity"); - // } + // Get count of attachments in source + int sourceCountInitial = sourceAttachmentIds.size(); + assertTrue(sourceCountInitial > 0, "Source should have attachments"); - // String linkName = "sample"; - // String linkUrl = "https://www.example.com"; - // String createLinkResponse = - // api.createLink(appUrl, entityName, facetName, copyLinkSourceEntity, linkName, linkUrl); - // if (!createLinkResponse.equals("Link created successfully")) { - // fail("Could not create link in source entity"); - // } + // Fetch object IDs from source entity + moveObjectIds.clear(); + for (String attachmentId : sourceAttachmentIds) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); + if (metadata.containsKey("objectId")) { + moveObjectIds.add(metadata.get("objectId").toString()); + // Get source folder ID from first attachment + if (moveSourceFolderId == null && metadata.containsKey("folderId")) { + moveSourceFolderId = metadata.get("folderId").toString(); + } + } + } catch (IOException e) { + fail("Could not fetch attachment metadata: " + e.getMessage()); + } + } - // List sourceObjectIds = - // api.fetchEntityMetadataDraft(appUrl, entityName, facetName, - // copyLinkSourceEntity).stream() - // .map(item -> (String) item.get("objectId")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); + if (moveObjectIds.size() != sourceAttachmentIds.size()) { + fail("Could not fetch object IDs for all attachments"); + } - // if (sourceObjectIds.isEmpty()) { - // fail("Could not fetch object Id for link"); - // } + assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); - // String copyResponse = - // api.copyAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, sourceObjectIds); - // if (!copyResponse.equals("Attachments copied successfully")) { - // fail("Could not copy link: " + copyResponse); - // } + // Create target entity with no SDM role + moveTargetEntity = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (moveTargetEntity.equals("Could not create entity")) { + fail("Could not create target entity with no SDM role"); + } - // List> attachmentsMetadata = new ArrayList<>(); - // attachmentsMetadata = - // api.fetchEntityMetadataDraft(appUrl, entityName, facetName, copyLinkTargetEntity); - // Map copiedAttachment = attachmentsMetadata.get(0); - // String receivedType = (String) copiedAttachment.get("type"); - // String receivedUrl = (String) copiedAttachment.get("linkUrl"); - - // String expectedType = "sap-icon://internet-browser"; - // assertTrue( - // expectedType.equalsIgnoreCase(receivedType), - // "Attachment type mismatch. Expected '" - // + expectedType - // + "' but got '" - // + receivedType - // + "'."); - - // assertEquals(linkUrl, receivedUrl, "Attachment URL mismatch."); - - // System.out.println("Attachment type and URL validated successfully."); - - // String attachmentId = (String) copiedAttachment.get("ID"); - // assertNotNull(attachmentId, "Could not find 'ID' in the copied attachment metadata."); - - // String openAttachmentResponse = - // api.openAttachment(appUrl, entityName, facetName, copyLinkTargetEntity, attachmentId); - // if (!openAttachmentResponse.equals("Attachment opened successfully")) { - // fail("Could not open the attachment"); - // } + // Try to move attachments from source to target using user without SDM role + String sourceFacet = serviceName + "." + entityName + "." + facetName; + String targetFacet = serviceName + "." + entityName + "." + facetName; + Map moveResult = null; + boolean moveOperationFailed = false; + String errorMessage = null; + + try { + moveResult = + apiNoRoles.moveAttachment( + appUrl, + entityName, + facetName, + moveTargetEntity, + moveSourceFolderId, + moveObjectIds, + targetFacet, + sourceFacet); + + if (moveResult == null) { + moveOperationFailed = true; + errorMessage = "Move operation returned null"; + } else if (moveResult.containsKey("error")) { + moveOperationFailed = true; + errorMessage = moveResult.get("error").toString(); + } + } catch (Exception e) { + moveOperationFailed = true; + errorMessage = e.getMessage(); + } - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, copyLinkTargetEntity); - // if (!saveResponse.equals("Saved")) { - // fail("Could not save target entity after copying link"); - // } - // api.deleteEntityDraft(appUrl, entityName, copyLinkSourceEntity); - // api.deleteEntity(appUrl, entityName, copyLinkTargetEntity); - // } + // Verify move operation failed + assertTrue(moveOperationFailed, "Move operation should fail when user does not have SDM role"); + assertNotNull(errorMessage, "Error message should be present when move operation fails"); + System.out.println("Move operation failed as expected. Error: " + errorMessage); - // @Test - // @Order(59) - // void testCopyAttachmentsSuccessNewEntityDraft() throws IOException { - // System.out.println( - // "Test (59): Copy attachments from one entity to another new entity draft mode"); - // List attachments = new ArrayList<>(); - // copyAttachmentSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // copyAttachmentTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (!copyAttachmentSourceEntity.equals("Could not create entity") - // && !copyAttachmentTargetEntity.equals("Could not create entity")) { - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample1.pdf").getFile())); - // Map postData = new HashMap<>(); - // postData.put("up__ID", entityID7); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // sourceObjectIds.clear(); - - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, srvpath, postData, - // file); - // if (createResponse.get(0).equals("Attachment created")) { - // attachments.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment"); - // } - // } + // Verify attachments are still in source entity (not moved) + List> sourceMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); + assertEquals( + sourceCountInitial, + sourceMetadataAfterMove.size(), + "Source should still have all attachments after failed move"); - // List> attachmentsMetadata = new ArrayList<>(); - // Map fetchAttachmentMetadataResponse; - // for (String attachment : attachments) { - // try { - // fetchAttachmentMetadataResponse = - // api.fetchMetadataDraft( - // appUrl, entityName, facetName, copyAttachmentSourceEntity, attachment); - // attachmentsMetadata.add(fetchAttachmentMetadataResponse); - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } - // for (Map metadata : attachmentsMetadata) { - // if (metadata.containsKey("objectId")) { - // sourceObjectIds.add(metadata.get("objectId").toString()); - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } + // Verify target entity has no attachments + List> targetMetadataAfterMove = + api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); + assertEquals( + 0, targetMetadataAfterMove.size(), "Target should have no attachments after failed move"); - // if (sourceObjectIds.size() == 2) { - // String copyResponse; - // copyResponse = - // api.copyAttachment( - // appUrl, entityName, facetName, copyAttachmentTargetEntity, sourceObjectIds); - // if (copyResponse.equals("Attachments copied successfully")) { - // String saveEntityResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, copyAttachmentTargetEntity); - // if (saveEntityResponse.equals("Saved")) { - // List> fetchEntityMetadataResponse; - // fetchEntityMetadataResponse = - // api.fetchEntityMetadata(appUrl, entityName, facetName, - // copyAttachmentTargetEntity); - // targetAttachmentIds = - // fetchEntityMetadataResponse.stream() - // .map(item -> (String) item.get("ID")) - // .filter(Objects::nonNull) - // .collect(Collectors.toList()); - // String readResponse; - // for (String targetAttachmentId : targetAttachmentIds) { - // readResponse = - // api.readAttachment( - // appUrl, - // entityName, - // facetName, - // copyAttachmentTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read copied attachment"); - // } - // } - // } else { - // fail("Could not save entity after copying attachments: " + saveEntityResponse); - // } - // } else { - // fail("Could not copy attachments: " + copyResponse); - // } - // } else { - // fail("Could not fetch objects Ids for all attachments"); - // } - // } else { - // fail("Could not create entities"); - // } - // api.deleteEntityDraft(appUrl, entityName, copyAttachmentSourceEntity); - // api.deleteEntity(appUrl, entityName, copyAttachmentTargetEntity); - // } + // Clean up - delete both entities using SDM role + api.deleteEntity(appUrl, entityName, moveTargetEntity); + api.deleteEntity(appUrl, entityName, moveSourceEntity); + } - // @Test - // @Order(60) - // void testViewChangelogForNewlyCreatedAttachment() throws IOException { - // System.out.println("Test (60): View changelog for newly created attachment"); + @Test + @Order(76) + void testRenameAttachmentWithExtensionChange() throws IOException { + System.out.println( + "Test (76) : Rename attachment changing extension from .pdf to .txt - should return extension change warning"); + + // Step 1: Create a new entity + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (newEntityID.equals("Could not create entity")) { + fail("Could not create entity"); + } + String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!saveResponse.equals("Saved")) { + fail("Could not save new entity: " + saveResponse); + } - // // Create a new entity for changelog test - // changelogEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(changelogEntityID, "Failed to create changelog test entity"); - // assertNotEquals("Could not create entity", changelogEntityID); + // Step 2: Upload a PDF attachment + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.txt").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", changelogEntityID); - // postData.put("mimeType", "text/plain"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (editResponse != "Entity in draft mode") { + fail("Could not put entity in draft mode for PDF upload"); + } - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, changelogEntityID, srvpath, postData, file); - - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // changelogAttachmentID = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(changelogAttachmentID, "Attachment ID should not be null"); - // assertNotEquals("", changelogAttachmentID, "Attachment ID should not be empty"); - - // // Fetch changelog for the newly created attachment - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, - // changelogAttachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog structure - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // "sample.txt", changelogResponse.get("filename"), "Filename should match uploaded file"); - // assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); - // assertEquals(1, changelogResponse.get("numItems"), "Should have 1 changelog entry"); - - // // Verify the changelog entry - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); - - // Map logEntry = changeLogs.get(0); - // assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); - // assertNotNull(logEntry.get("time"), "Time should not be null"); - // assertNotNull(logEntry.get("user"), "User should not be null"); - // assertFalse( - // logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); + String check = createResponse.get(0); + if (!check.equals("Attachment created")) { + fail("Could not upload sample.pdf: " + check); + } + String newAttachmentID = createResponse.get(1); - // @Test - // @Order(61) - // void testChangelogAfterModifyingNoteAndCustomProperty() throws IOException { - // System.out.println( - // "Test (61): Modify note field and custom property, then verify changelog shows created + - // 3 updated entries"); - - // // Update attachment with notes field (entity is already in draft mode from test 60) - // String notesValue = "Test note for changelog verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // changelogEntityID, - // changelogAttachmentID, - // updateNotesBody); - // assertEquals("Updated", updateNotesResponse, "Should successfully update notes field"); - - // // Update attachment with custom property - // Integer customProperty2Value = 12345; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID, bodyInt); - // assertEquals( - // "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - - // // Save the entity - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity again to fetch changelog - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after modifications - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, - // changelogAttachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog content - should have 1 created + 3 updated (note, customProperty2, and - // // internal update) - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // 4, - // changelogResponse.get("numItems"), - // "Should have 4 changelog entries (1 created + 3 updated)"); - - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(4, changeLogs.size(), "Should have exactly 4 changelog entries"); - - // // Verify first entry is 'created' - // Map createdEntry = changeLogs.get(0); - // assertEquals( - // "created", createdEntry.get("operation"), "First entry should be 'created' operation"); - - // // Verify remaining entries are 'updated' - // long updatedCount = - // changeLogs.stream().filter(log -> "updated".equals(log.get("operation"))).count(); - // assertEquals(3, updatedCount, "Should have 3 'updated' operations"); - - // // Verify that changeDetail exists in updated entries for note field - // boolean hasNoteUpdate = - // changeLogs.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .anyMatch( - // log -> { - // @SuppressWarnings("unchecked") - // Map changeDetail = (Map) - // log.get("changeDetail"); - // return changeDetail != null - // && "cmis:description".equals(changeDetail.get("field")); - // }); - // assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); - // assertTrue(hasNoteUpdate, "Should have an update entry for note field (cmis:description)"); - - // // Save the entity so test 62 can edit it - // String saveResponseFinal = api.saveEntityDraft(appUrl, entityName, srvpath, - // changelogEntityID); - // assertEquals("Saved", saveResponseFinal, "Entity should be saved successfully"); - // } + // Step 3: Save the entity + String savedAfterUpload = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (!savedAfterUpload.equals("Saved")) { + fail("Could not save entity after PDF upload: " + savedAfterUpload); + } - // @Test - // @Order(62) - // void testChangelogAfterRenamingAttachment() throws IOException { - // System.out.println( - // "Test (62): Rename attachment and verify changelog increases with rename entry"); - - // // Edit entity to put it in draft mode (entity was saved at end of test 61) - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Rename the attachment - // String newFileName = "renamed_sample.txt"; - // String renameResponse = - // api.renameAttachment( - // appUrl, entityName, facetName, changelogEntityID, changelogAttachmentID, - // newFileName); - // assertEquals("Renamed", renameResponse, "Should successfully rename attachment"); - - // // Save entity after rename - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, changelogEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully after rename"); - - // // Edit entity again and fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, changelogEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after rename - // Map changelogAfterRename = - // api.fetchChangelog(appUrl, entityName, facetName, changelogEntityID, - // changelogAttachmentID); - - // assertNotNull(changelogAfterRename, "Changelog response should not be null after rename"); - - // // Verify changelog has increased (rename operation adds 1 entry for cmis:name change) - // // Expected: 1 created + 3 initial updates + 1 rename update = 5 total - // assertEquals( - // 5, changelogAfterRename.get("numItems"), "Should have 5 changelog entries after rename"); - - // @SuppressWarnings("unchecked") - // List> changeLogsAfterRename = - // (List>) changelogAfterRename.get("changeLogs"); - // assertEquals( - // 5, changeLogsAfterRename.size(), "Should have exactly 5 changelog entries after rename"); - - // // Verify updated count is 4 (3 initial + 1 from rename operation) - // long updatedCountAfterRename = - // changeLogsAfterRename.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .count(); - // assertEquals(4, updatedCountAfterRename, "Should have 4 'updated' operations after rename"); - - // // Verify filename change in changelog - // boolean hasFilenameUpdate = - // changeLogsAfterRename.stream() - // .filter(log -> "updated".equals(log.get("operation"))) - // .anyMatch( - // log -> { - // @SuppressWarnings("unchecked") - // Map changeDetail = (Map) - // log.get("changeDetail"); - // return changeDetail != null && "cmis:name".equals(changeDetail.get("field")); - // }); - // assertTrue(hasFilenameUpdate, "Should have an update entry for filename (cmis:name)"); - - // // Cleanup - entity was saved after rename, so delete the active entity - // api.deleteEntity(appUrl, entityName, changelogEntityID); - // } + // Step 4: Edit the entity + String editDraftResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); + if (editDraftResponse != "Entity in draft mode") { + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not put entity in draft mode for rename"); + } - // @Test - // @Order(63) - // void testChangelogWithCustomPropertyEditSave() throws IOException { - // System.out.println( - // "Test (63): Create entity with custom property, save, edit and save again - verify - // changelog remains at 3 entries"); + // Step 5: Rename the attachment changing the extension from .pdf to .txt + String renameResponse = + api.renameAttachment( + appUrl, entityName, facetName, newEntityID, newAttachmentID, "renamed_document.txt"); + if (!renameResponse.equals("Renamed")) { + api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + api.deleteEntity(appUrl, entityName, newEntityID); + fail("Could not rename attachment: " + renameResponse); + } - // // Create a new entity - // String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(newEntityID, "Failed to create new entity"); - // assertNotEquals("Could not create entity", newEntityID); + // Step 6: Save and validate the extension change error message + String saveWithWarningResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertNotNull(saveWithWarningResponse, "Response should not be null"); - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + String expectedMessage = + "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", newEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + com.fasterxml.jackson.databind.JsonNode messagesNode = + new ObjectMapper().readTree(saveWithWarningResponse); + assertTrue(messagesNode.isArray(), "sap-messages response should be a JSON array"); - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, - // file); - - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // String attachmentID = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(attachmentID, "Attachment ID should not be null"); - // assertNotEquals("", attachmentID, "Attachment ID should not be empty"); - - // // Add a custom property - // Integer customPropertyValue = 99999; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customPropertyValue + "}", - // MediaType.parse("application/json")); - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, newEntityID, attachmentID, bodyInt); - // assertEquals( - // "Updated", updateCustomPropertyResponse, "Should successfully update custom property"); - - // // Save the entity - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity to fetch initial changelog - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after initial save - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog has 3 entries: 1 created + 2 updated (cmis:secondaryObjectTypeIds + - // // customProperty2) - // assertEquals(3, changelogResponse.get("numItems"), "Should have 3 changelog entries - // initially"); - - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(3, changeLogs.size(), "Should have exactly 3 changelog entries"); - - // // Save entity again without any modifications - // saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - - // // Edit entity again and fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog after second save - // Map changelogAfterSecondSave = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, attachmentID); - - // assertNotNull( - // changelogAfterSecondSave, "Changelog response should not be null after second save"); - - // // Verify changelog still has only 3 entries (no new entries added) - // assertEquals( - // 3, - // changelogAfterSecondSave.get("numItems"), - // "Should still have only 3 changelog entries after edit-save without modifications"); - - // @SuppressWarnings("unchecked") - // List> changeLogsAfterSecondSave = - // (List>) changelogAfterSecondSave.get("changeLogs"); - // assertEquals( - // 3, - // changeLogsAfterSecondSave.size(), - // "Should still have exactly 3 changelog entries after second save"); - - // // Clean up the entity - // api.deleteEntity(appUrl, entityName, newEntityID); - // } + boolean foundExtensionError = false; + for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { + if (messageNode.has("message")) { + String message = messageNode.get("message").asText(); + if (message.contains("Changing the file extension is not allowed")) { + foundExtensionError = true; + assertEquals( + expectedMessage, + message, + "Extension change error message does not match expected value"); + break; + } + } + } - // @Test - // @Order(64) - // void testChangelogForSavedAttachmentWithoutModification() throws IOException { - // System.out.println( - // "Test (64): Create entity, upload attachment, save, edit and save again - verify - // changelog still has only 'created' entry"); + assertTrue( + foundExtensionError, + "Expected extension change warning not found in response. Full response: " + + saveWithWarningResponse); - // // Create a new entity - // String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // assertNotNull(newEntityID, "Failed to create new entity"); - // assertNotEquals("Could not create entity", newEntityID); + // Clean up + api.deleteEntity(appUrl, entityName, newEntityID); + } - // // Prepare a sample file to upload - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample.pdf").getFile()); - // assertTrue(file.exists(), "Sample file should exist"); + @Test + @Order(77) + void testRenameAttachmentWithExtensionChange_WhileUpload() throws IOException { + System.out.println( + "Test (77) : Upload attachment in draft, rename changing extension before save - should return extension change warning"); - // // Create attachment - // Map postData = new HashMap<>(); - // postData.put("up__ID", newEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Step 1: Create a new entity draft (do NOT save it yet) + String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (newEntityID.equals("Could not create entity")) { + fail("Could not create entity"); + } - // List createResponse = - // api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, - // file); - - // assertEquals(2, createResponse.size(), "Should return status and attachment ID"); - // String status = createResponse.get(0); - // String newAttachmentID = createResponse.get(1); - - // assertEquals("Attachment created", status, "Attachment should be created successfully"); - // assertNotNull(newAttachmentID, "Attachment ID should not be null"); - // assertNotEquals("", newAttachmentID, "Attachment ID should not be empty"); - - // // Save the entity immediately without any modifications - // String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully"); - - // // Edit entity again without making any changes to the attachment - // String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Save entity again without modifying the attachment - // saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Saved", saveResponse, "Entity should be saved successfully again"); - - // // Edit entity to fetch changelog - // editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - // assertEquals("Entity in draft mode", editResponse, "Entity should be in draft mode"); - - // // Fetch changelog for the attachment - // Map changelogResponse = - // api.fetchChangelog(appUrl, entityName, facetName, newEntityID, newAttachmentID); - - // assertNotNull(changelogResponse, "Changelog response should not be null"); - - // // Verify changelog content - should only have 'created' entry even after edit and save - // assertEquals(false, changelogResponse.get("hasMoreItems"), "hasMoreItems should be false"); - // assertEquals( - // "sample.pdf", changelogResponse.get("filename"), "Filename should match uploaded file"); - // assertNotNull(changelogResponse.get("objectId"), "ObjectId should not be null"); - // assertEquals(1, changelogResponse.get("numItems"), "Should have only 1 changelog entry"); - - // // Verify the changelog entry - // @SuppressWarnings("unchecked") - // List> changeLogs = - // (List>) changelogResponse.get("changeLogs"); - // assertEquals(1, changeLogs.size(), "Should have exactly 1 changelog entry"); - - // Map logEntry = changeLogs.get(0); - // assertEquals("created", logEntry.get("operation"), "Operation should be 'created'"); - // assertNotNull(logEntry.get("time"), "Time should not be null"); - // assertNotNull(logEntry.get("user"), "User should not be null"); - // assertFalse( - // logEntry.containsKey("changeDetail"), "Created operation should not have changeDetail"); - - // // Clean up the new entity - // api.deleteEntity(appUrl, entityName, newEntityID); - // } + // Step 2: Upload a PDF attachment while entity is still in draft (unsaved) + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // @Test - // @Order(65) - // void testMoveAttachmentsWithSourceFacet() throws IOException { - // System.out.println( - // "Test (65): Move attachments from Source Entity to Target Entity with sourceFacet"); + Map postData = new HashMap<>(); + postData.put("up__ID", newEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); + String check = createResponse.get(0); + if (!check.equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, newEntityID); + fail("Could not upload sample.pdf: " + check); + } + String newAttachmentID = createResponse.get(1); - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Step 3: Rename the attachment changing extension from .pdf to .txt — entity still not saved + String renameResponse = + api.renameAttachment( + appUrl, entityName, facetName, newEntityID, newAttachmentID, "renamed_document.txt"); + if (!renameResponse.equals("Renamed")) { + api.deleteEntityDraft(appUrl, entityName, newEntityID); + fail("Could not rename attachment: " + renameResponse); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Step 4: Save — should receive extension change warning, not "Saved" + String saveWithWarningResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); + assertNotNull(saveWithWarningResponse, "Response should not be null"); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + String expectedMessage = + "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + com.fasterxml.jackson.databind.JsonNode messagesNode = + new ObjectMapper().readTree(saveWithWarningResponse); + assertTrue(messagesNode.isArray(), "sap-messages response should be a JSON array"); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + boolean foundExtensionError = false; + for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { + if (messageNode.has("message")) { + String message = messageNode.get("message").asText(); + if (message.contains("Changing the file extension is not allowed")) { + foundExtensionError = true; + assertEquals( + expectedMessage, + message, + "Extension change error message does not match expected value"); + break; + } + } + } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + assertTrue( + foundExtensionError, + "Expected extension change warning not found in response. Full response: " + + saveWithWarningResponse); - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Clean up + api.deleteEntity(appUrl, entityName, newEntityID); + } - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity: " + saveTargetBeforeMoveResponse); - // } + @Test + @Order(78) + void testReadCmisMetadataCreatedBy() throws Exception { + System.out.println("Test (78) : Read CMIS metadata and verify createdBy field"); - // // Move attachments from source to target with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // // All attachments moved to target entity in SDM & UI - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals( - // sourceAttachmentIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all attachments after move"); - - // // Verify attachments can be read from target entity - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // String readResponse = - // api.readAttachment(appUrl, entityName, facetName, moveTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read moved attachment from target entity"); - // } - // } + // Create a self-contained entity with an attachment + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // All attachments removed from source entity in SDM & UI - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // 0, sourceMetadataAfterMove.size(), "Source entity should have 0 attachments after move"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - // @Test - // @Order(66) - // public void testMoveAttachmentsToEntityWithDuplicateWithSourceFacet() throws Exception { - // System.out.println( - // "Test (66): Move attachments to entity with duplicate attachment with sourceFacet"); + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Verify createdBy CMIS property + String createdBy = + CmisDocumentHelper.getCmisProperty(testEntityID, "sample.pdf", "cmis:createdBy"); + System.out.println("cmis:createdBy value: " + createdBy); + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); + } else { + assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); + assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); + } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + @Test + @Order(79) + void testUploadVirusFileInScanDisabledRepo() throws IOException { + System.out.println( + "Test (79) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + boolean testStatus = false; + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + } + String testEntityID = response; - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Use EICAR test virus file + String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); + File file = new File(eicarFilePath); + if (!file.exists()) { + fail("EICAR virus test file not found at: " + file.getAbsolutePath()); + } - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "text/plain"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + String check = createResponse.get(0); + if (check.equals("Attachment created")) { + String testAttachmentID = createResponse.get(1); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (response.equals("Saved")) { + boolean uploadCompleted = waitForUploadCompletion(testEntityID, testAttachmentID, 120); + if (uploadCompleted) { + // Verify attachment is readable (upload succeeded despite being a virus file) + response = + api.readAttachment(appUrl, entityName, facetName, testEntityID, testAttachmentID); + assertEquals("OK", response, "Virus file should be readable in scan-disabled repository"); + testStatus = true; + } + } + } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); - // // Create target entity and add attachment - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + if (!testStatus) { + fail("Virus file upload should succeed in a virus scan disabled repository"); + } + } - // Map targetPostData = new HashMap<>(); - // targetPostData.put("up__ID", moveTargetEntity); - // targetPostData.put("mimeType", "application/pdf"); - // targetPostData.put("createdAt", new Date().toString()); - // targetPostData.put("createdBy", "test@test.com"); - // targetPostData.put("modifiedBy", "test@test.com"); + private boolean waitForUploadCompletion( + String entityId, String attachmentId, int timeoutSeconds) { + int maxIterations = timeoutSeconds / 2; + for (int i = 0; i < maxIterations; i++) { + try { + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, entityId, attachmentId); + String uploadStatus = (String) metadata.get("uploadStatus"); - // File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); - // List targetCreateResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // srvpath, - // targetPostData, - // duplicateFile); - - // if (!targetCreateResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment on target entity"); - // } + if ("Success".equals(uploadStatus)) { + return true; + } else if ("Failed".equals(uploadStatus)) { + System.err.println("Upload failed for attachment: " + attachmentId); + return false; + } - // // Save target entity to persist the attachment - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse); - // } + Thread.sleep(2000); + } catch (Exception e) { + System.err.println( + "Error checking upload status for attachment " + attachmentId + ": " + e.getMessage()); + return false; + } + } - // // Fetch target metadata before move (target entity is now saved with 1 attachment) - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // int targetCountBeforeMove = targetMetadataBeforeMove.size(); - - // // Move attachments from source to target with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // // Verify target has duplicate skipped, other attachments moved - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - - // // Expected: original attachments + non-duplicate moved attachments - // int expectedTargetCount = targetCountBeforeMove + (sourceAttachmentIds.size() - 1); - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target should have duplicate skipped, other attachments moved"); - - // // Verify source entity has only the duplicate attachment remaining - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // // Calculate expected source count: number of duplicates that couldn't be moved - // int expectedSourceCount = - // sourceAttachmentIds.size() - (targetMetadataAfterMove.size() - targetCountBeforeMove); - // assertEquals( - // expectedSourceCount, - // sourceMetadataAfterMove.size(), - // "Source should have duplicate attachment remaining"); - - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + System.err.println("Upload timed out for attachment: " + attachmentId); + return false; + } - // @Test - // @Order(67) - // public void testMoveAttachmentsWithNotesAndSecondaryProperties() throws Exception { - // System.out.println( - // "Test (67): Move attachments with notes and secondary properties with sourceFacet"); + @Test + @Order(80) + void testDownloadMultipleAttachments() throws IOException { + System.out.println( + "Test (76): Create entity, upload 3 attachments (pdf, txt, exe), and download all"); + boolean testStatus = false; - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Step 1: Create entity + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String downloadTestEntityID = response; - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + ClassLoader classLoader = getClass().getClassLoader(); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Step 2: Upload pdf, txt, exe in one draft session + Map postData = new HashMap<>(); + postData.put("up__ID", downloadTestEntityID); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Upload pdf + postData.put("mimeType", "application/pdf"); + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List createResponse1 = + api.createAttachment( + appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, pdfFile); + if (!createResponse1.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not upload sample.pdf"); + return; + } + String downloadAttachmentID1 = createResponse1.get(1); - // // Add notes to attachments - // String notesValue = "Test note for verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, attachmentId, updateNotesBody); - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update notes for attachment: " + attachmentId); - // } - // } + // Upload txt + postData.put("mimeType", "application/txt"); + File txtFile = new File(classLoader.getResource("sample.txt").getFile()); + List createResponse2 = + api.createAttachment( + appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, txtFile); + if (!createResponse2.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not upload sample.txt"); + return; + } + String downloadAttachmentID2 = createResponse2.get(1); - // // Add custom property to attachments - // Integer customProperty2Value = 54321; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, attachmentId, bodyInt); - // if (!updateCustomPropertyResponse.equals("Updated")) { - // fail("Could not update custom property for attachment: " + attachmentId); - // } - // } + // Upload exe + postData.put("mimeType", "application/exe"); + File exeFile = new File(classLoader.getResource("sample.exe").getFile()); + List createResponse3 = + api.createAttachment( + appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, exeFile); + if (!createResponse3.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not upload sample.exe"); + return; + } + String downloadAttachmentID3 = createResponse3.get(1); - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Step 3: Save entity draft + response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); + fail("Could not save entity draft: " + response); + return; + } - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + // Step 4: Select first attachment - Download button should be enabled + // Verify download works with a single attachment selection + String singleDownloadResult = + api.downloadSelectedAttachments( + appUrl, entityName, facetName, downloadTestEntityID, List.of(downloadAttachmentID1)); + JSONArray singleResultArray = new JSONArray(singleDownloadResult); + assertEquals(1, singleResultArray.length(), "Expected 1 result in download response"); + JSONObject singleResult = singleResultArray.getJSONObject(0); + assertEquals( + "success", + singleResult.getString("status"), + "Download button should be enabled: single attachment download should succeed"); + assertTrue(singleResult.has("content"), "Downloaded attachment should have a content field"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + // Step 5: Select all 3 and click download + String multiDownloadResult = + api.downloadSelectedAttachments( + appUrl, + entityName, + facetName, + downloadTestEntityID, + List.of(downloadAttachmentID1, downloadAttachmentID2, downloadAttachmentID3)); + JSONArray multiResultArray = new JSONArray(multiDownloadResult); + assertEquals(3, multiResultArray.length(), "Expected 3 results in download response"); + for (int i = 0; i < multiResultArray.length(); i++) { + JSONObject result = multiResultArray.getJSONObject(i); + assertEquals( + "success", + result.getString("status"), + "Attachment " + (i + 1) + " should download successfully"); + assertTrue( + result.has("content"), + "Attachment " + (i + 1) + " should have a content field in the response"); + } + testStatus = true; - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Clean up + api.deleteEntity(appUrl, entityName, downloadTestEntityID); - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse); - // } + if (!testStatus) { + fail("Multiple attachment download test failed"); + } + } - // // Move attachments from source to target with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + @Test + @Order(81) + void testDownloadButtonDisabledWhenLinkSelected() throws IOException { + System.out.println( + "Test (77): Download button enabled for pdf only; disabled when link is also selected"); - // // Verify all attachments moved to target - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals( - // sourceAttachmentIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all attachments after move"); - - // // Verify notes and secondary properties are preserved - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); - - // // Verify notes are preserved - // if (detailedMetadata.containsKey("note")) { - // assertEquals( - // notesValue, - // detailedMetadata.get("note"), - // "Notes should be preserved after move for attachment: " + targetAttachmentId); - // } else { - // fail("Notes property missing after move for attachment: " + targetAttachmentId); - // } + // Step 1: Create entity (already in draft mode) + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String testEntityID = response; - // // Verify custom property is preserved - // if (detailedMetadata.containsKey("customProperty2")) { - // assertEquals( - // customProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Custom property should be preserved after move for attachment: " + - // targetAttachmentId); - // } else { - // fail("Custom property missing after move for attachment: " + targetAttachmentId); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); - // // Verify source entity has no attachments (all moved with sourceFacet) - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals(0, sourceMetadataAfterMove.size(), "Source entity has no attachments after - // move"); + // Step 2: Upload one pdf attachment (entity is already in draft mode) + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, testEntityID, srvpath, postData, pdfFile); + if (!createResponse.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not upload sample.pdf"); + return; + } + String pdfAttachmentID = createResponse.get(1); - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Step 3: Create a link attachment (entity still in draft mode) + String linkResponse = + api.createLink( + appUrl, entityName, facetName, testEntityID, "TestLink", "https://www.example.com"); + if (!linkResponse.equals("Link created successfully")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not create link attachment"); + return; + } - // @Test - // @Order(68) - // public void testMoveAttachmentsWithoutSourceFacet() throws Exception { - // System.out.println( - // "Test (68): Move valid attachments from Source Entity to Target Entity without - // sourceFacet"); + // Save entity draft + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not save entity draft: " + response); + return; + } - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Fetch metadata to find the link attachment ID (mimeType = "application/internet-shortcut") + List> allAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + String linkAttachmentID = + allAttachments.stream() + .filter( + a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) + .map(a -> (String) a.get("ID")) + .findFirst() + .orElse(null); + if (linkAttachmentID == null) { + api.deleteEntity(appUrl, entityName, testEntityID); + fail("Could not find link attachment in entity metadata"); + return; + } - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Step 4: Select only the pdf - Download button should be enabled (succeeds) + String pdfOnlyResult = + api.downloadSelectedAttachments( + appUrl, entityName, facetName, testEntityID, List.of(pdfAttachmentID)); + JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); + assertEquals(1, pdfOnlyArray.length(), "Expected 1 result when only pdf is selected"); + assertEquals( + "success", + pdfOnlyArray.getJSONObject(0).getString("status"), + "Download button should be enabled: pdf-only download should succeed"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Step 5: Select both pdf and link - Download button should be disabled + // (link attachment returns error status, disabling the download) + String mixedResult = + api.downloadSelectedAttachments( + appUrl, + entityName, + facetName, + testEntityID, + List.of(pdfAttachmentID, linkAttachmentID)); + JSONArray mixedArray = new JSONArray(mixedResult); + assertEquals(2, mixedArray.length(), "Expected 2 results when pdf and link are selected"); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Find the result for the link attachment and assert it has error status + JSONObject linkResult = null; + for (int i = 0; i < mixedArray.length(); i++) { + JSONObject item = mixedArray.getJSONObject(i); + if (linkAttachmentID.equals(item.getString("id"))) { + linkResult = item; + break; + } + } + assertNotNull(linkResult, "Result for link attachment should be present"); + assertEquals( + "error", + linkResult.getString("status"), + "Download button should be disabled: link attachment download should return error"); + assertEquals( + "Download is not supported for link attachments", + linkResult.getString("message"), + "Error message for link attachment download should match"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + @Test + @Order(82) + void testDownloadMultipleAttachmentsInDraftState() throws IOException { + System.out.println( + "Test (78): Create entity in draft state, upload 3 attachments (pdf, txt, exe), and" + + " download before saving"); + boolean testStatus = false; - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID from first attachment - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + // Step 1: Create entity draft (do NOT save) + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String draftEntityID = response; - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + ClassLoader classLoader = getClass().getClassLoader(); - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Step 2: Upload pdf, txt, exe while entity remains in draft state + Map postData = new HashMap<>(); + postData.put("up__ID", draftEntityID); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + // Upload pdf + postData.put("mimeType", "application/pdf"); + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List createResponse1 = + api.createAttachment( + appUrl, entityName, facetName, draftEntityID, srvpath, postData, pdfFile); + if (!createResponse1.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, draftEntityID); + fail("Could not upload sample.pdf"); + return; + } + String draftAttachmentID1 = createResponse1.get(1); - // // Move attachments without sourceFacet (pass null for sourceFacet parameter) - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Upload txt + postData.put("mimeType", "application/txt"); + File txtFile = new File(classLoader.getResource("sample.txt").getFile()); + List createResponse2 = + api.createAttachment( + appUrl, entityName, facetName, draftEntityID, srvpath, postData, txtFile); + if (!createResponse2.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, draftEntityID); + fail("Could not upload sample.txt"); + return; + } + String draftAttachmentID2 = createResponse2.get(1); - // // Verify attachments are in target entity - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals( - // moveObjectIds.size(), - // targetMetadataAfterMove.size(), - // "Target entity should have all moved attachments"); - - // // Verify attachments can be read from target entity - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // String readResponse = - // api.readAttachment(appUrl, entityName, facetName, moveTargetEntity, - // targetAttachmentId); - // if (!readResponse.equals("OK")) { - // fail("Could not read moved attachment from target entity"); - // } - // } + // Upload exe + postData.put("mimeType", "application/exe"); + File exeFile = new File(classLoader.getResource("sample.exe").getFile()); + List createResponse3 = + api.createAttachment( + appUrl, entityName, facetName, draftEntityID, srvpath, postData, exeFile); + if (!createResponse3.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, draftEntityID); + fail("Could not upload sample.exe"); + return; + } + String draftAttachmentID3 = createResponse3.get(1); - // // Expected Behavior: Attachments remain in source entity UI (without sourceFacet) - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // moveObjectIds.size(), - // sourceMetadataAfterMove.size(), - // "Source entity should still have attachments in UI when sourceFacet is not specified"); - - // // Verify the same objectIds are still visible in source - // for (Map metadata : sourceMetadataAfterMove) { - // String objectId = (String) metadata.get("objectId"); - // assertTrue( - // moveObjectIds.contains(objectId), - // "Source entity should still show attachment with objectId: " + objectId); - // } + // Step 3: Select first attachment - Download button should be enabled even in draft state + String singleDownloadResult = + api.downloadSelectedAttachmentsDraft( + appUrl, entityName, facetName, draftEntityID, List.of(draftAttachmentID1)); + JSONArray singleResultArray = new JSONArray(singleDownloadResult); + assertEquals(1, singleResultArray.length(), "Expected 1 result in download response"); + JSONObject singleResult = singleResultArray.getJSONObject(0); + assertEquals( + "success", + singleResult.getString("status"), + "Download button should be enabled in draft state: single attachment download should" + + " succeed"); + assertTrue(singleResult.has("content"), "Downloaded attachment should have a content field"); - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Step 4: Select all 3 and download while entity is still in draft state + String multiDownloadResult = + api.downloadSelectedAttachmentsDraft( + appUrl, + entityName, + facetName, + draftEntityID, + List.of(draftAttachmentID1, draftAttachmentID2, draftAttachmentID3)); + JSONArray multiResultArray = new JSONArray(multiDownloadResult); + assertEquals(3, multiResultArray.length(), "Expected 3 results in download response"); + for (int i = 0; i < multiResultArray.length(); i++) { + JSONObject result = multiResultArray.getJSONObject(i); + assertEquals( + "success", + result.getString("status"), + "Attachment " + (i + 1) + " should download successfully in draft state"); + assertTrue( + result.has("content"), + "Attachment " + (i + 1) + " should have a content field in the response"); + } + testStatus = true; - // @Test - // @Order(69) - // public void testMoveAttachmentsToEntityWithDuplicateWithoutSourceFacet() throws Exception { - // System.out.println( - // "Test (69): Move attachments into existing Target Entity when duplicate exists without - // sourceFacet"); + // Clean up - entity was never saved, so delete the draft + api.deleteEntityDraft(appUrl, entityName, draftEntityID); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + if (!testStatus) { + fail("Multiple attachment download in draft state test failed"); + } + } - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + @Test + @Order(83) + void testDownloadButtonWithPdfAndLinkInDraftState() throws IOException { + System.out.println( + "Test (79): Upload pdf and link, save entity, edit entity (draft state)," + + " download button enabled for pdf only, disabled when link also selected"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Step 1: Create entity draft + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + if (response.equals("Could not create entity")) { + fail("Could not create entity"); + return; + } + String testEntityID = response; - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Step 2: Upload one pdf attachment (entity in draft state) + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID from first attachment - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } else { - // fail("Attachment metadata does not contain objectId"); - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, testEntityID, srvpath, postData, pdfFile); + if (!createResponse.get(0).equals("Attachment created")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not upload sample.pdf"); + return; + } + // Capture pdf attachment ID directly from upload response (draft state, reliable) + String pdfAttachmentID = createResponse.get(1); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + // Step 3: Create a link attachment (entity still in draft state) + String linkResponse = + api.createLink( + appUrl, entityName, facetName, testEntityID, "TestLink", "https://www.example.com"); + if (!linkResponse.equals("Link created successfully")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not create link attachment"); + return; + } - // // Create target entity and add duplicate attachment (sample.pdf) - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Fetch link attachment ID from draft metadata while still in draft state + List> draftAttachments = + api.fetchEntityMetadataDraft(appUrl, entityName, facetName, testEntityID); + String linkAttachmentID = + draftAttachments.stream() + .filter( + a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) + .map(a -> (String) a.get("ID")) + .findFirst() + .orElse(null); + if (linkAttachmentID == null) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not find link attachment in draft entity metadata"); + return; + } - // // Add the same first file (sample.pdf) to target entity to create duplicate - // Map targetPostData = new HashMap<>(); - // targetPostData.put("up__ID", moveTargetEntity); - // targetPostData.put("mimeType", "application/pdf"); - // targetPostData.put("createdAt", new Date().toString()); - // targetPostData.put("createdBy", "test@test.com"); - // targetPostData.put("modifiedBy", "test@test.com"); + // Step 4: Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!response.equals("Saved")) { + api.deleteEntityDraft(appUrl, entityName, testEntityID); + fail("Could not save entity draft: " + response); + return; + } - // List createTargetResponse = - // api.createAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // srvpath, - // targetPostData, - // files.get(0)); // Add same file (sample.pdf) - // if (!createTargetResponse.get(0).equals("Attachment created")) { - // fail("Could not create duplicate attachment in target entity"); - // } + // Step 5: Edit entity - puts it back into draft state + String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + if (!editResponse.equals("Entity in draft mode")) { + api.deleteEntity(appUrl, entityName, testEntityID); + fail("Could not put entity into edit/draft mode: " + editResponse); + return; + } - // // Save target entity before move - // String saveTargetResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity: " + saveTargetResponse); - // } + // Step 7: Select only pdf - Download button should be enabled (succeeds) + String pdfOnlyResult = + api.downloadSelectedAttachmentsDraft( + appUrl, entityName, facetName, testEntityID, List.of(pdfAttachmentID)); + JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); + assertEquals(1, pdfOnlyArray.length(), "Expected 1 result when only pdf is selected"); + assertEquals( + "success", + pdfOnlyArray.getJSONObject(0).getString("status"), + "Download button should be enabled in draft state: pdf-only download should succeed"); - // // Get initial target metadata count - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // int initialTargetCount = targetMetadataBeforeMove.size(); - - // // Step 3: Move attachments without sourceFacet (duplicate should be skipped) - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Step 8: Select pdf + link - Download button should be disabled + // (link attachment returns error status, disabling the download) + String mixedResult = + api.downloadSelectedAttachmentsDraft( + appUrl, + entityName, + facetName, + testEntityID, + List.of(pdfAttachmentID, linkAttachmentID)); + JSONArray mixedArray = new JSONArray(mixedResult); + assertEquals(2, mixedArray.length(), "Expected 2 results when pdf and link are selected"); - // // Expected Behavior - Verify duplicate was skipped, other attachments moved - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - - // int nonDuplicateCount = moveObjectIds.size() - 1; - // int expectedTargetCount = initialTargetCount + nonDuplicateCount; - - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target entity should have initial attachments plus non-duplicate moved attachments"); - - // // Verify at least one non-duplicate attachment was moved - // assertTrue( - // targetMetadataAfterMove.size() > initialTargetCount, - // "Target should have more attachments after move (non-duplicates added)"); - - // // Verify all attachments still remain in source entity UI (without sourceFacet) - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // moveObjectIds.size(), - // sourceMetadataAfterMove.size(), - // "Source entity should still have all attachments in UI when sourceFacet is not - // specified"); - - // // Verify all original objectIds are still visible in source - // List sourceObjectIds = new ArrayList<>(); - // for (Map metadata : sourceMetadataAfterMove) { - // sourceObjectIds.add((String) metadata.get("objectId")); - // } - // for (String objectId : moveObjectIds) { - // assertTrue( - // sourceObjectIds.contains(objectId), - // "Source entity should still show attachment with objectId: " + objectId); - // } + JSONObject linkResult = null; + for (int i = 0; i < mixedArray.length(); i++) { + JSONObject item = mixedArray.getJSONObject(i); + if (linkAttachmentID.equals(item.getString("id"))) { + linkResult = item; + break; + } + } + assertNotNull(linkResult, "Result for link attachment should be present"); + assertEquals( + "error", + linkResult.getString("status"), + "Download button should be disabled in draft state: link attachment should return error"); + assertEquals( + "Download is not supported for link attachments", + linkResult.getString("message"), + "Error message for link attachment download should match"); - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } // @Test - // @Order(70) - // public void testMoveAttachmentsWithNotesAndSecondaryPropertiesWithoutSourceFacet() - // throws Exception { + // @Order(84) + // void testUploadAttachmentExceedingMaximumFileSize() throws IOException { // System.out.println( - // "Test (70): Move attachments with notes and secondary properties without sourceFacet"); + // "Test (78) : Upload attachment exceeding maximum file size in references facet"); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); + // // Create a new entity + // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + // if (response.equals("Could not create entity")) { + // fail("Could not create entity"); // } + // String testEntityID = response; - // // Prepare sample files + // // Load the 150MB sample file // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // File file = new File(classLoader.getResource("sample32mb.pdf").getFile()); // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); + // postData.put("up__ID", testEntityID); // postData.put("mimeType", "application/pdf"); // postData.put("createdAt", new Date().toString()); // postData.put("createdBy", "test@test.com"); // postData.put("modifiedBy", "test@test.com"); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } - - // // Add notes to attachments - // String notesValue = "Test note for migration verification"; - // MediaType mediaType = MediaType.parse("application/json"); - // String jsonPayload = "{\"note\": \"" + notesValue + "\"}"; - // RequestBody updateNotesBody = RequestBody.create(jsonPayload, mediaType); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateNotesResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, attachmentId, updateNotesBody); - // if (!updateNotesResponse.equals("Updated")) { - // fail("Could not update notes for attachment: " + attachmentId); - // } - // } - - // // Add custom property to attachments - // Integer customProperty2Value = 54321; - // RequestBody bodyInt = - // RequestBody.create( - // "{\"customProperty2\": " + customProperty2Value + "}", - // MediaType.parse("application/json")); - - // for (String attachmentId : sourceAttachmentIds) { - // String updateCustomPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, attachmentId, bodyInt); - // if (!updateCustomPropertyResponse.equals("Updated")) { - // fail("Could not update custom property for attachment: " + attachmentId); - // } - // } - - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // // Try to upload to the 'references' facet which has the 100MB limit + // String referencesFacet = "references"; + // List createResponse = + // api.createAttachment( + // appUrl, entityName, referencesFacet, testEntityID, srvpath, postData, file); + // String check = createResponse.get(0); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { + // // The upload should fail with AttachmentSizeExceeded error + // if (!check.equals("Attachment created")) { // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } + // JSONObject json = new JSONObject(check); + // String errorCode = json.getJSONObject("error").getString("code"); + // String errorMessage = json.getJSONObject("error").getString("message"); + // assertEquals("413", errorCode); + // assertEquals("File size exceeds the limit of 30MB.", errorMessage); // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); + // fail("Failed to parse error response: " + e.getMessage()); // } + // } else { + // fail("Attachment got created with file size exceeding maximum limit"); // } - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + // // delete the test entity draft + // api.deleteEntityDraft(appUrl, entityName, testEntityID); + // } - // // Get source attachment count before move - // List> sourceMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + @Test + @Order(85) + void testRenameToDuplicateFilename_BackendConflict_ErrorThrown() throws Exception { + System.out.println( + "Test (79) : Rename attachment to name that exists in backend — expect DI error"); - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Create a new entity and upload an attachment + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Get target attachment count before move - // List> targetMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // int targetCountBeforeMove = targetMetadataBeforeMove.size(); - - // // Move attachments from source to target WITHOUT sourceFacet - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Verify expected number of attachments moved to target - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // int expectedTargetCount = targetCountBeforeMove + sourceAttachmentIds.size(); - // assertEquals( - // expectedTargetCount, - // targetMetadataAfterMove.size(), - // "Target entity should have " + expectedTargetCount + " attachments after move"); - - // // Verify notes and secondary properties are preserved - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); - - // // Verify notes are preserved - // if (detailedMetadata.containsKey("note")) { - // assertEquals( - // notesValue, - // detailedMetadata.get("note"), - // "Notes should be preserved after move for attachment: " + targetAttachmentId); - // } else { - // fail("Notes property missing after move for attachment: " + targetAttachmentId); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // // Verify custom property is preserved - // if (detailedMetadata.containsKey("customProperty2")) { - // assertEquals( - // customProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Custom property should be preserved after move for attachment: " + - // targetAttachmentId); - // } else { - // fail("Custom property missing after move for attachment: " + targetAttachmentId); - // } - // } + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // // Verify source entity still has all attachments (without sourceFacet) - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // sourceCountBeforeMove, - // sourceMetadataAfterMove.size(), - // "Source entity should still have " - // + sourceCountBeforeMove - // + " attachments (without sourceFacet)"); - - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Add a file with a conflicting name directly in the CMIS backend + String conflictingName = "backend-file.pdf"; + System.out.println(" Adding '" + conflictingName + "' directly in CMIS backend..."); + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testEntityID); - // @Test - // @Order(71) - // public void testMoveAttachmentsWithInvalidOrUndefinedSecondaryProperties() throws Exception { - // System.out.println( - // "Test (71): Move attachments with invalid or undefined secondary properties"); + // Enter draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Rename the existing attachment to the conflicting name + System.out.println(" Renaming 'sample.pdf' to '" + conflictingName + "'..."); + response = + api.renameAttachment( + appUrl, entityName, facetName, testEntityID, testAttachmentID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); - // files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + // Save the entity — DI should throw error about duplicate name + System.out.println(" Saving entity — expecting DI error..."); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + System.out.println(" Save response: " + response); + assertTrue( + response.contains("already exists") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + @Test + @Order(86) + void testUploadDuplicateAttachment_DIError_RemovedFromDrafts() throws IOException { + System.out.println( + "Test (80) : Upload attachment that triggers DI error — verify error message and attachment removed from drafts"); - // // Add valid secondary properties to first attachment (customProperty2) - // String validAttachmentId = sourceAttachmentIds.get(0); - // Integer validCustomProperty2Value = 12345; - // RequestBody validPropertyBody = - // RequestBody.create( - // "{\"customProperty2\": " + validCustomProperty2Value + "}", - // MediaType.parse("application/json")); - - // String validPropertyResponse = - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, validAttachmentId, - // validPropertyBody); - // if (!validPropertyResponse.equals("Updated")) { - // fail("Could not update valid property for attachment: " + validAttachmentId); - // } + // Create a new entity + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // add invalid secondary properties to second attachment (non-existent property) - // String invalidAttachmentId = sourceAttachmentIds.get(1); - // RequestBody invalidPropertyBody = - // RequestBody.create( - // "{\"nonExistentProperty\": \"invalid\"}", MediaType.parse("application/json")); - - // api.updateSecondaryProperty( - // appUrl, entityName, facetName, moveSourceEntity, invalidAttachmentId, - // invalidPropertyBody); - - // // add undefined properties to third attachment - // String undefinedAttachmentId = sourceAttachmentIds.get(2); - // RequestBody undefinedPropertyBody = - // RequestBody.create( - // "{\"undefinedField\": \"test\", \"anotherUndefined\": 999}", - // MediaType.parse("application/json")); - - // api.updateSecondaryProperty( - // appUrl, - // entityName, - // facetName, - // moveSourceEntity, - // undefinedAttachmentId, - // undefinedPropertyBody); - - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Upload sample.pdf — first upload should succeed + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (Exception e) { - // fail("Could not fetch metadata for attachment: " + attachmentId); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch all objectIds from source entity"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals( + "Attachment created", createResponse.get(0), "First upload of sample.pdf should succeed"); - // // Get source attachment count before move - // List> sourceMetadataBeforeMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // int sourceCountBeforeMove = sourceMetadataBeforeMove.size(); + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after first upload"); - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Edit entity to go back into draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // // Save target before move - // String saveTargetBeforeMoveResponse68 = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse68.equals("Saved")) { - // fail("Could not save target entity before move: " + saveTargetBeforeMoveResponse68); - // } + // Upload the same file again — DI should throw an error (duplicate filename) + List duplicateResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + String errorResponse = duplicateResponse.get(0); + assertNotEquals( + "Attachment created", errorResponse, "Duplicate upload should fail with DI error"); + System.out.println(" DI error response (first attempt): " + errorResponse); + assertTrue( + errorResponse.contains("already exists") || errorResponse.contains("error"), + "Error should contain DI message. Actual: " + errorResponse); - // // Move attachments from source to target with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + // Verify the failed attachment is removed from drafts + List> draftAttachments = + api.fetchEntityMetadataDraft(appUrl, entityName, facetName, testEntityID); + assertEquals( + 1, + draftAttachments.size(), + "Only the original attachment should remain in drafts after failed duplicate upload"); - // // Verify attachments moved to target - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - - // assertTrue( - // targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); - // assertEquals( - // sourceCountBeforeMove, - // targetMetadataAfterMove.size(), - // "All attachments should move (invalid properties are ignored)"); - - // // Verify only allowed properties are populated in target - // for (Map metadata : targetMetadataAfterMove) { - // String targetAttachmentId = (String) metadata.get("ID"); - // assertNotNull(targetAttachmentId, "Target attachment ID should not be null"); - - // // Fetch detailed metadata to verify properties - // Map detailedMetadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, targetAttachmentId); - - // // Check if this is the attachment with valid customProperty2 - // if (detailedMetadata.containsKey("customProperty2") - // && detailedMetadata.get("customProperty2") != null) { - // assertEquals( - // validCustomProperty2Value, - // detailedMetadata.get("customProperty2"), - // "Valid customProperty2 should be preserved"); - // } - // } + // Try to upload the same file again — should get same DI error + List duplicateResponse2 = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + String errorResponse2 = duplicateResponse2.get(0); + assertNotEquals( + "Attachment created", + errorResponse2, + "Second duplicate upload should also fail with DI error"); + System.out.println(" DI error response (second attempt): " + errorResponse2); + assertTrue( + errorResponse2.contains("already exists") || errorResponse2.contains("error"), + "Error should contain DI message. Actual: " + errorResponse2); - // // Verify source entity has no attachments - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterMove.size(), - // "Source entity should have no attachments after move with sourceFacet"); - - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Verify drafts still only have the original attachment + draftAttachments = api.fetchEntityMetadataDraft(appUrl, entityName, facetName, testEntityID); + assertEquals( + 1, + draftAttachments.size(), + "Only the original attachment should remain after second failed upload"); - // @Test - // @Order(72) - // public void testMoveAttachmentsFromSourceEntityInDraftMode() throws Exception { - // System.out.println( - // "Test (72): Move attachments from Source Entity when Source Entity is in draft mode"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Create source entity and keep it in draft mode - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + @Test + @Order(87) + void testReadAttachment_DeletedFromBackend_NotAvailable() throws IOException { + System.out.println( + "Test (81) : Read attachment after it has been deleted from repository backend" + + " — verify app handles gracefully"); - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); - // files.add(new File(classLoader.getResource("WDIRSCodeList.csv").getFile())); + // Create a new entity + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Upload an attachment + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Verify attachments are added to source entity - // int sourceCountBeforeMove = sourceAttachmentIds.size(); - // assertTrue(sourceCountBeforeMove > 0, "Source entity should have attachments before move"); - // assertEquals( - // files.size(), sourceCountBeforeMove, "Source should have " + files.size() + " - // attachments"); - - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID from first attachment - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + // Verify attachment is readable before backend deletion + response = api.readAttachment(appUrl, entityName, facetName, testEntityID, testAttachmentID); + assertEquals("OK", response, "Attachment should be readable before backend deletion"); - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + // Delete the attachment directly from the repository backend (CMIS) + System.out.println(" Deleting attachment from backend via CMIS..."); + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, "sample.pdf"); - // String editSourceResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!editSourceResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity back to draft mode"); - // } + // Try to read the attachment through the app — app serves from its own state + System.out.println(" Attempting to read deleted attachment..."); + response = api.readAttachment(appUrl, entityName, facetName, testEntityID, testAttachmentID); + System.out.println(" Read response after backend deletion: " + response); - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + // Verify app still lists the attachment in metadata (app state is not synced with backend) + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertEquals(1, attachments.size(), "App should still show the attachment in its metadata"); - // // Save target before move - // String saveTargetResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveTargetEntity); - // if (!saveTargetResponse.equals("Saved")) { - // fail("Could not save target entity: " + saveTargetResponse); - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Move attachments from draft source to target using sourceFacet - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // null); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + @Test + @Order(88) + void testDeleteAttachment_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println( + "Test (82) : Delete attachment when it is not present in the repository — expect removed from UI"); - // // Verify attachments moved to target - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertTrue( - // targetMetadataAfterMove.size() > 0, "Target entity should have attachments after move"); - // assertEquals( - // sourceCountBeforeMove, - // targetMetadataAfterMove.size(), - // "Target should have " + sourceCountBeforeMove + " attachments after move"); - - // // Verify all expected attachments are in target - // Set targetFileNames = - // targetMetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // targetFileNames.contains(file.getName()), - // "Target should contain attachment: " + file.getName()); - // } + // Create a new entity and upload an attachment + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Now save the source entity - // String saveSourceAfterMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceAfterMoveResponse.equals("Saved")) { - // fail("Could not save source entity after move: " + saveSourceAfterMoveResponse); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // sourceCountBeforeMove, - // sourceMetadataAfterMove.size(), - // "Source entity in draft mode retains attachments after move (copy behavior)"); - - // Set sourceFileNamesAfterMove = - // sourceMetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // sourceFileNamesAfterMove.contains(file.getName()), - // "Source (draft) should still contain attachment: " + file.getName()); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); + String testAttachmentID = createResponse.get(1); - // @Test - // @Order(73) - // public void testEditAttachmentFileNameAndMoveToTarget() throws Exception { - // System.out.println( - // "Test (73): Edit attachment file name in Source Entity and move it to Target Entity"); + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // // Create source entity and add attachment - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Delete the attachment from the repository backend directly + System.out.println(" Deleting attachment from CMIS backend..."); + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, "sample.pdf"); - // // Add attachment with original name (sample.txt) - // ClassLoader classLoader = getClass().getClassLoader(); - // File originalFile = new File(classLoader.getResource("sample.txt").getFile()); + // Enter draft mode by clicking edit + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "text/plain"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + // Delete the attachment through the app (select and click delete) + System.out.println(" Deleting attachment via app..."); + response = api.deleteAttachment(appUrl, entityName, facetName, testEntityID, testAttachmentID); + assertEquals("Deleted", response, "Delete attachment should succeed even if not in repo"); - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, originalFile); - // if (!createResponse.get(0).equals("Attachment created")) { - // fail("Could not create attachment in source entity"); - // } + // Save the entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after delete"); - // String attachmentId = createResponse.get(1); - // assertNotNull(attachmentId, "Attachment ID should not be null"); + // Verify the attachment is removed from UI + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertEquals(0, attachments.size(), "No attachments should remain after deletion"); - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // // Verify original filename - // List> metadataBeforeRename = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals(1, metadataBeforeRename.size(), "Source should have 1 attachment"); - // assertEquals( - // "sample.txt", - // metadataBeforeRename.get(0).get("fileName"), - // "Original filename should be sample.txt"); - - // // Edit source entity back to draft mode - // String editSourceResponse = api.editEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!editSourceResponse.equals("Entity in draft mode")) { - // fail("Could not edit source entity to draft mode"); - // } + @Test + @Order(89) + void testDeleteEntity_FolderAndContentDeletedFromRepository() throws Exception { + System.out.println( + "Test (83) : Delete entity — expect entity and attachments no longer accessible via app"); - // // Rename the attachment to testEdited.txt - // String newFileName = "testEdited.txt"; - // String renameResponse = - // api.renameAttachment( - // appUrl, entityName, facetName, moveSourceEntity, attachmentId, newFileName); - // assertEquals("Renamed", renameResponse, "Attachment should be renamed successfully"); - - // // Save source entity after rename - // saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity after rename: " + saveSourceResponse); - // } + // Create a new entity and upload an attachment + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Verify renamed filename in source - // List> metadataAfterRename = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals(1, metadataAfterRename.size(), "Source should still have 1 attachment"); - // assertEquals( - // newFileName, - // metadataAfterRename.get(0).get("fileName"), - // "Filename should be updated to " + newFileName); - - // // Get objectId and folderId for move operation - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // String objectId = metadata.get("objectId").toString(); - // moveSourceFolderId = metadata.get("folderId").toString(); - // assertNotNull(objectId, "Object ID should not be null"); - // assertNotNull(moveSourceFolderId, "Folder ID should not be null"); - - // moveObjectIds.clear(); - // moveObjectIds.add(objectId); - - // // Create target entity - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Save target before move - // String saveTargetBeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTargetBeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity before move"); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Move attachment from source to target with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // fail("Move operation returned null result"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - // // Verify attachment moved to target with renamed filename - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals(1, targetMetadataAfterMove.size(), "Target should have 1 attachment after - // move"); - // assertEquals( - // newFileName, - // targetMetadataAfterMove.get(0).get("fileName"), - // "Target should have attachment with renamed filename: " + newFileName); - - // // Verify attachment removed from source - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterMove.size(), - // "Source entity should have no attachments after move with sourceFacet"); - - // // Clean up - delete both entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + // Save entity to commit attachment to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // @Test - // @Order(74) - // public void testChainMoveAttachmentsFromSourceToTarget1ToTarget2() throws Exception { - // System.out.println( - // "Test (74): Move attachments from Source Entity to Target Entity 1 and then to Target - // Entity 2"); + // Verify entity exists before deletion + response = api.checkEntity(appUrl, entityName, testEntityID); + assertEquals("Entity exists", response, "Entity should exist before delete"); - // // Create source entity and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Delete the entity + System.out.println(" Deleting entity..."); + response = api.deleteEntity(appUrl, entityName, testEntityID); + assertEquals("Entity Deleted", response, "Entity deletion should succeed"); - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Verify the entity is no longer accessible via the app + System.out.println(" Verifying entity is no longer accessible..."); + response = api.checkEntity(appUrl, entityName, testEntityID); + assertEquals("Entity doesn't exist", response, "Entity should not exist after delete"); + } - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + @Test + @Order(90) + void testDiscardDraft_AttachmentsAndFolderDeletedFromDI() throws Exception { + System.out.println( + "Test (84) : Discard draft after uploading attachments — expect attachments and folder deleted from DI"); - // // Create attachments in source entity - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Create a new entity in draft mode + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Save source entity - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + // Upload attachments in draft + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Get count of attachments in source - // int sourceCountInitial = sourceAttachmentIds.size(); - // assertTrue(sourceCountInitial > 0, "Source should have attachments"); + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID from first attachment - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment upload should succeed"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + // Discard the draft before saving + System.out.println(" Discarding draft..."); + response = api.deleteEntityDraft(appUrl, entityName, testEntityID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + // Verify the attachment is no longer accessible via the app + System.out.println(" Verifying attachments are cleaned up after discard..."); + List> attachmentsAfterDiscard = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertEquals( + 0, + attachmentsAfterDiscard.size(), + "Entity should have no attachments after discarding draft"); + } - // // Create Target Entity 1 - // moveTargetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity 1"); - // } + @Test + @Order(91) + void testDeleteAllAttachments_FolderDeletedFromDI() throws Exception { + System.out.println( + "Test (85) : Delete all attachments from entity — expect folder deleted from DI"); - // // Save target1 before move - // String saveTarget1BeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity); - // if (!saveTarget1BeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity 1 before move"); - // } + // Create a new entity and upload attachments + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // // Move attachments from source to Target Entity 1 with sourceFacet - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult1 = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult1 == null) { - // fail("Move operation from source to target 1 returned null result"); - // } + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); - // // Verify attachments moved to Target Entity 1 - // List> target1MetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertTrue( - // target1MetadataAfterMove.size() > 0, "Target entity 1 should have attachments after - // move"); - // assertEquals( - // sourceCountInitial, - // target1MetadataAfterMove.size(), - // "Target 1 should have " + sourceCountInitial + " attachments"); - - // // Verify all expected files are in Target Entity 1 - // Set target1FileNames = - // target1MetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // target1FileNames.contains(file.getName()), - // "Target 1 should contain attachment: " + file.getName()); - // } + Map postData = new HashMap<>(); + postData.put("up__ID", testEntityID); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Verify attachments removed from source - // List> sourceMetadataAfterFirstMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // 0, - // sourceMetadataAfterFirstMove.size(), - // "Source entity should have no attachments after move to target 1"); - - // // Create Target Entity 2 - // String moveTargetEntity2 = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity2.equals("Could not create entity")) { - // fail("Could not create target entity 2"); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); + assertEquals( + "Attachment created", createResponse.get(0), "First attachment upload should succeed"); + String attachID1 = createResponse.get(1); - // // Save target2 before move - // String saveTarget2BeforeMoveResponse = - // api.saveEntityDraft(appUrl, entityName, srvpath, moveTargetEntity2); - // if (!saveTarget2BeforeMoveResponse.equals("Saved")) { - // fail("Could not save target entity 2 before move"); - // } + // Save entity to commit to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + // Verify folder exists in CMIS + String folderName = testEntityID + "__attachments"; + ShellScriptRunner.Result folderCheck = + ShellScriptRunner.runAndCaptureAll( + CmisDocumentHelper.getCmisEnvPublic(), + "src/test/java/integration/com/sap/cds/sdm/utils/get-object-id.sh", + folderName); + assertEquals(0, folderCheck.getExitCode(), "Entity folder should exist in CMIS"); + + // Edit entity to enter draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + // Delete all attachments + System.out.println(" Deleting all attachments..."); + response = api.deleteAttachment(appUrl, entityName, facetName, testEntityID, attachID1); + assertEquals("Deleted", response, "Delete attachment should succeed"); + + // Save the entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after deleting all attachments"); - // // Get new object IDs and folder ID from Target Entity 1 for second move - // List target1AttachmentIds = new ArrayList<>(); - // for (Map metadata : target1MetadataAfterMove) { - // String attachmentId = metadata.get("ID").toString(); - // target1AttachmentIds.add(attachmentId); - // } + // Verify no attachments remain on the entity + System.out.println(" Verifying all attachments are removed..."); + List> attachmentsAfterDelete = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertEquals( + 0, attachmentsAfterDelete.size(), "Entity should have no attachments after deleting all"); - // moveObjectIds.clear(); - // String target1FolderId = null; - // for (String attachmentId : target1AttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveTargetEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get folder ID from first attachment - // if (target1FolderId == null && metadata.containsKey("folderId")) { - // target1FolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata from target 1: " + e.getMessage()); - // } - // } + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // assertNotNull(target1FolderId, "Target 1 folder ID should not be null"); - - // // Move attachments from Target Entity 1 to Target Entity 2 with sourceFacet - // Map moveResult2 = - // api.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity2, - // target1FolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult2 == null) { - // fail("Move operation from target 1 to target 2 returned null result"); - // } + @Test + @Order(92) + void testCopyInvalidAttachments_IntoNewEntity_NothingCopied() throws Exception { + System.out.println( + "Test (86) : Copy attachments with invalid secondary property into a new entity" + + " — expect copy succeeds but invalid property is not propagated"); - // // Verify attachments moved to Target Entity 2 - // List> target2MetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity2); - // assertTrue( - // target2MetadataAfterMove.size() > 0, "Target entity 2 should have attachments after - // move"); - // assertEquals( - // sourceCountInitial, - // target2MetadataAfterMove.size(), - // "Target 2 should have " + sourceCountInitial + " attachments"); - - // // Verify all expected files are in Target Entity 2 - // Set target2FileNames = - // target2MetadataAfterMove.stream() - // .map(m -> (String) m.get("fileName")) - // .collect(java.util.stream.Collectors.toSet()); - - // for (File file : files) { - // assertTrue( - // target2FileNames.contains(file.getName()), - // "Target 2 should contain attachment: " + file.getName()); - // } + // Step 1: Create source entity with attachment + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", sourceEntity, "Source entity creation should succeed"); + + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // // Verify attachments removed from Target Entity 1 - // List> target1MetadataAfterSecondMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals( - // 0, - // target1MetadataAfterSecondMove.size(), - // "Target entity 1 should have no attachments after move to target 2"); - - // // Clean up - delete all three entities - // api.deleteEntity(appUrl, entityName, moveTargetEntity2); - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + List createResponse = + api.createAttachment(appUrl, entityName, facetName, sourceEntity, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment should be created"); + String sourceAttachmentID = createResponse.get(1); + + // Save source entity + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save should succeed"); + + // Step 2: Add invalid secondary property via app API + String invalidPropValue = "invalidTestValue"; + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, sourceEntity, sourceAttachmentID, invalidPropValue); + + // Get objectId of source attachment for copy + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facetName, sourceEntity, sourceAttachmentID); + assertNotNull(sourceMetadata.get("objectId"), "Source attachment should have objectId"); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + // Step 3: Create target entity and copy attachment + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, targetEntity, objectIdsToCopy); + assertEquals( + "Attachments copied successfully", copyResponse, "Copy should succeed at CMIS level"); - // @Test - // @Order(75) - // public void testMoveAttachmentsWithoutSDMRole() throws Exception { - // System.out.println("Test (75): Move attachments when user does not have SDM Role"); - - // // Create source entity with SDM role and add attachments - // moveSourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveSourceEntity.equals("Could not create entity")) { - // fail("Could not create source entity"); - // } + // Save target entity — succeeds because copy doesn't propagate app-level properties + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Save should succeed — copy does not propagate invalid props"); - // // Prepare sample files - // ClassLoader classLoader = getClass().getClassLoader(); - // List files = new ArrayList<>(); - // files.add(new File(classLoader.getResource("sample.pdf").getFile())); - // files.add(new File(classLoader.getResource("sample.txt").getFile())); + // Verify: target attachment exists but does NOT have the invalid CMIS property + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals(1, targetAttachments.size(), "Target should have 1 copied attachment"); - // Map postData = new HashMap<>(); - // postData.put("up__ID", moveSourceEntity); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); + String targetAttId = (String) targetAttachments.get(0).get("ID"); + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetEntity, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); - // // Create attachments in source entity with SDM role - // List sourceAttachmentIds = new ArrayList<>(); - // for (File file : files) { - // List createResponse = - // api.createAttachment( - // appUrl, entityName, facetName, moveSourceEntity, srvpath, postData, file); - // if (createResponse.get(0).equals("Attachment created")) { - // sourceAttachmentIds.add(createResponse.get(1)); - // } else { - // fail("Could not create attachment in source entity"); - // } - // } + // Clean up + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); + } - // // Save source entity with SDM role - // String saveSourceResponse = api.saveEntityDraft(appUrl, entityName, srvpath, - // moveSourceEntity); - // if (!saveSourceResponse.equals("Saved")) { - // fail("Could not save source entity: " + saveSourceResponse); - // } + @Test + @Order(93) + void testCopyInvalidAttachments_IntoExistingEntity_NothingCopied() throws Exception { + System.out.println( + "Test (87) : Copy attachments with invalid secondary property into an existing entity" + + " — expect copy succeeds, invalid property not propagated, existing attachment intact"); - // // Get count of attachments in source - // int sourceCountInitial = sourceAttachmentIds.size(); - // assertTrue(sourceCountInitial > 0, "Source should have attachments"); + // Step 1: Create source entity with attachment + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", sourceEntity, "Source entity creation should succeed"); - // // Fetch object IDs from source entity - // moveObjectIds.clear(); - // for (String attachmentId : sourceAttachmentIds) { - // try { - // Map metadata = - // api.fetchMetadata(appUrl, entityName, facetName, moveSourceEntity, attachmentId); - // if (metadata.containsKey("objectId")) { - // moveObjectIds.add(metadata.get("objectId").toString()); - // // Get source folder ID from first attachment - // if (moveSourceFolderId == null && metadata.containsKey("folderId")) { - // moveSourceFolderId = metadata.get("folderId").toString(); - // } - // } - // } catch (IOException e) { - // fail("Could not fetch attachment metadata: " + e.getMessage()); - // } - // } + ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); + Map postData = new HashMap<>(); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); + postData.put("createdAt", new Date().toString()); + postData.put("createdBy", "test@test.com"); + postData.put("modifiedBy", "test@test.com"); - // if (moveObjectIds.size() != sourceAttachmentIds.size()) { - // fail("Could not fetch object IDs for all attachments"); - // } + List createResponse = + api.createAttachment( + appUrl, entityName, facetName, sourceEntity, srvpath, postData, filePdf); + assertEquals( + "Attachment created", createResponse.get(0), "Source attachment should be created"); + String sourceAttachmentID = createResponse.get(1); + + // Save source entity + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save should succeed"); + + // Step 2: Add invalid secondary property via app API + String invalidPropValue = "invalidTestValue"; + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, sourceEntity, sourceAttachmentID, invalidPropValue); + + // Get objectId of source attachment + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facetName, sourceEntity, sourceAttachmentID); + assertNotNull(sourceMetadata.get("objectId"), "Source attachment should have objectId"); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + // Step 3: Create target entity with its own attachment (pre-existing) + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + + File file1Pdf = new File(classLoader.getResource("sample1.pdf").getFile()); + Map postDataTarget = new HashMap<>(); + postDataTarget.put("up__ID", targetEntity); + postDataTarget.put("mimeType", "application/pdf"); + postDataTarget.put("createdAt", new Date().toString()); + postDataTarget.put("createdBy", "test@test.com"); + postDataTarget.put("modifiedBy", "test@test.com"); + + List targetCreateResponse = + api.createAttachment( + appUrl, entityName, facetName, targetEntity, srvpath, postDataTarget, file1Pdf); + assertEquals( + "Attachment created", targetCreateResponse.get(0), "Target attachment should be created"); + String targetAttachmentID = targetCreateResponse.get(1); - // assertNotNull(moveSourceFolderId, "Source folder ID should not be null"); + // Save target entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target entity save should succeed"); - // // Create target entity with no SDM role - // moveTargetEntity = apiNoRoles.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (moveTargetEntity.equals("Could not create entity")) { - // fail("Could not create target entity with no SDM role"); - // } + // Step 4: Edit target entity and copy invalid attachment + response = api.editEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Entity in draft mode", response, "Target entity should enter draft mode"); - // // Try to move attachments from source to target using user without SDM role - // String sourceFacet = serviceName + "." + entityName + "." + facetName; - // String targetFacet = serviceName + "." + entityName + "." + facetName; - // Map moveResult = null; - // boolean moveOperationFailed = false; - // String errorMessage = null; - - // try { - // moveResult = - // apiNoRoles.moveAttachment( - // appUrl, - // entityName, - // facetName, - // moveTargetEntity, - // moveSourceFolderId, - // moveObjectIds, - // targetFacet, - // sourceFacet); - - // if (moveResult == null) { - // moveOperationFailed = true; - // errorMessage = "Move operation returned null"; - // } else if (moveResult.containsKey("error")) { - // moveOperationFailed = true; - // errorMessage = moveResult.get("error").toString(); - // } - // } catch (Exception e) { - // moveOperationFailed = true; - // errorMessage = e.getMessage(); - // } + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); - // // Verify move operation failed - // assertTrue(moveOperationFailed, "Move operation should fail when user does not have SDM - // role"); - // assertNotNull(errorMessage, "Error message should be present when move operation fails"); - // System.out.println("Move operation failed as expected. Error: " + errorMessage); - - // // Verify attachments are still in source entity (not moved) - // List> sourceMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveSourceEntity); - // assertEquals( - // sourceCountInitial, - // sourceMetadataAfterMove.size(), - // "Source should still have all attachments after failed move"); - - // // Verify target entity has no attachments - // List> targetMetadataAfterMove = - // api.fetchEntityMetadata(appUrl, entityName, facetName, moveTargetEntity); - // assertEquals( - // 0, targetMetadataAfterMove.size(), "Target should have no attachments after failed - // move"); - - // // Clean up - delete both entities using SDM role - // api.deleteEntity(appUrl, entityName, moveTargetEntity); - // api.deleteEntity(appUrl, entityName, moveSourceEntity); - // } + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, targetEntity, objectIdsToCopy); + assertEquals( + "Attachments copied successfully", copyResponse, "Copy should succeed at CMIS level"); - @Test - @Order(76) - void testReadCmisMetadataCreatedBy() { - System.out.println("Test (76) : Read CMIS metadata and verify createdBy field"); - String createdBy = CmisDocumentHelper.getCmisProperty(entityID, "sample.pdf", "cmis:createdBy"); - System.out.println("cmis:createdBy value: " + createdBy); - String tokenFlowFlag = System.getProperty("tokenFlow"); - if ("namedUser".equals(tokenFlowFlag)) { - assertEquals(username, createdBy, "cmis:createdBy should match username from credentials"); - } else { - assertNotNull(createdBy, "cmis:createdBy should not be null for technical user"); - assertFalse(createdBy.isEmpty(), "cmis:createdBy should not be empty for technical user"); - } - } + // Save target entity — succeeds because copy does not propagate invalid properties + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Save should succeed — copy does not propagate invalid props"); - private boolean waitForUploadCompletion( - String entityId, String attachmentId, int timeoutSeconds) { - int maxIterations = timeoutSeconds / 2; - for (int i = 0; i < maxIterations; i++) { - try { - Map metadata = - api.fetchMetadata(appUrl, entityName, facetName, entityId, attachmentId); - String uploadStatus = (String) metadata.get("uploadStatus"); + // Verify: both the original and copied attachment should exist + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals( + 2, targetAttachments.size(), "Target should have 2 attachments (original + copied)"); - if ("Success".equals(uploadStatus)) { - return true; - } else if ("Failed".equals(uploadStatus)) { - System.err.println("Upload failed for attachment: " + attachmentId); - return false; - } + // Verify the original attachment is still there + boolean foundOriginal = + targetAttachments.stream().anyMatch(a -> targetAttachmentID.equals(a.get("ID"))); + assertTrue(foundOriginal, "Original attachment should still be present"); - Thread.sleep(2000); - } catch (Exception e) { - System.err.println( - "Error checking upload status for attachment " + attachmentId + ": " + e.getMessage()); - return false; - } - } + // Verify invalid property is NOT in CMIS for the copied attachment + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetEntity, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); - System.err.println("Upload timed out for attachment: " + attachmentId); - return false; + // Clean up + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); } @Test - @Order(77) - void testUploadVirusFileInScanDisabledRepo() throws IOException { + @Order(94) + void testCopyInvalidAttachments_FromDraftEntity_IntoNewEntity_Fails() throws Exception { System.out.println( - "Test (77) : Upload EICAR virus file in virus scan disabled repo — expect upload to succeed"); + "Test (88) : Copy attachments with invalid secondary property from draft entity" + + " into a new entity — expect copy succeeds, invalid property not propagated"); - boolean testStatus = false; - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - } - String testEntityID = response; - - // Use EICAR test virus file - String eicarFilePath = System.getProperty("eicar.file.path", "eicar.com.txt"); - File file = new File(eicarFilePath); - if (!file.exists()) { - fail("EICAR virus test file not found at: " + file.getAbsolutePath()); - } + // Step 1: Create source entity with attachment and save it first + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", sourceEntity, "Source entity creation should succeed"); + ClassLoader classLoader = getClass().getClassLoader(); + File file = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); - postData.put("mimeType", "text/plain"); + postData.put("up__ID", sourceEntity); + postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); List createResponse = - api.createAttachment(appUrl, entityName, facetName, testEntityID, srvpath, postData, file); - String check = createResponse.get(0); - if (check.equals("Attachment created")) { - String testAttachmentID = createResponse.get(1); - response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (response.equals("Saved")) { - boolean uploadCompleted = waitForUploadCompletion(testEntityID, testAttachmentID, 120); - if (uploadCompleted) { - // Verify attachment is readable (upload succeeded despite being a virus file) - response = - api.readAttachment(appUrl, entityName, facetName, testEntityID, testAttachmentID); - assertEquals("OK", response, "Virus file should be readable in scan-disabled repository"); - testStatus = true; - } - } - } + api.createAttachment(appUrl, entityName, facetName, sourceEntity, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment should be created"); + String sourceAttachmentID = createResponse.get(1); + + // Save source entity first to persist attachment in CMIS + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save should succeed"); + + // Get objectId from saved state + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facetName, sourceEntity, sourceAttachmentID); + String sourceObjectId = + sourceMetadata.get("objectId") != null ? sourceMetadata.get("objectId").toString() : null; + + // Step 2: Edit source back to draft and add invalid secondary property + response = api.editEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Entity in draft mode", response, "Source should enter draft mode"); + + String invalidPropValue = "invalidTestValue"; + api.updateInvalidSecondaryProperty( + appUrl, entityName, facetName, sourceEntity, sourceAttachmentID, invalidPropValue); + + // Step 3: Create target entity and attempt copy using the persisted objectId + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + + if (sourceObjectId != null && !sourceObjectId.isEmpty()) { + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, targetEntity, objectIdsToCopy); + assertEquals( + "Attachments copied successfully", copyResponse, "Copy should succeed at CMIS level"); - // Clean up - api.deleteEntity(appUrl, entityName, testEntityID); + // Save target — succeeds because copy does not propagate invalid properties + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals( + "Saved", response, "Save should succeed — copy does not propagate invalid props"); - if (!testStatus) { - fail("Virus file upload should succeed in a virus scan disabled repository"); + // Verify: target has the copied attachment but NOT the invalid property + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals(1, targetAttachments.size(), "Target should have 1 copied attachment"); + + String cmisInvalidProp = + CmisDocumentHelper.getCmisPropertyOrNull(targetEntity, "sample.pdf", "abc:myId1"); + assertNull(cmisInvalidProp, "Invalid property should NOT be propagated via copy"); + } else { + System.out.println( + " Source attachment has no objectId (not yet persisted in DI) — copy not possible"); } + + // Clean up — discard draft on source since it has unsaveable invalid prop + api.deleteEntityDraft(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); } @Test - @Order(76) - void testRenameAttachmentWithExtensionChange() throws IOException { + @Order(95) + void testCopyEditedFileName_FromOneEntityToAnother() throws Exception { System.out.println( - "Test (76) : Rename attachment changing extension from .pdf to .txt - should return extension change warning"); + "Test (89) : Copy attachment with edited filename from one entity to another" + + " — expect target shows edited filename"); - // Step 1: Create a new entity - String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (newEntityID.equals("Could not create entity")) { - fail("Could not create entity"); - } - String saveResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!saveResponse.equals("Saved")) { - fail("Could not save new entity: " + saveResponse); - } + // Step 1: Create source entity and upload attachment + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", sourceEntity, "Source entity creation should succeed"); - // Step 2: Upload a PDF attachment ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource("sample.pdf").getFile()); - Map postData = new HashMap<>(); - postData.put("up__ID", newEntityID); + postData.put("up__ID", sourceEntity); postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (editResponse != "Entity in draft mode") { - fail("Could not put entity in draft mode for PDF upload"); - } - List createResponse = - api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); - String check = createResponse.get(0); - if (!check.equals("Attachment created")) { - fail("Could not upload sample.pdf: " + check); - } - String newAttachmentID = createResponse.get(1); + api.createAttachment(appUrl, entityName, facetName, sourceEntity, srvpath, postData, file); + assertEquals("Attachment created", createResponse.get(0), "Attachment should be created"); + String sourceAttachmentID = createResponse.get(1); - // Step 3: Save the entity - String savedAfterUpload = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (!savedAfterUpload.equals("Saved")) { - fail("Could not save entity after PDF upload: " + savedAfterUpload); - } + // Save source entity + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save should succeed"); - // Step 4: Edit the entity - String editDraftResponse = api.editEntityDraft(appUrl, entityName, srvpath, newEntityID); - if (editDraftResponse != "Entity in draft mode") { - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not put entity in draft mode for rename"); - } + // Step 2: Edit source entity, rename the attachment, and save + String editedFileName = "sampleEdited.pdf"; + response = api.editEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Entity in draft mode", response, "Source entity should enter draft mode"); - // Step 5: Rename the attachment changing the extension from .pdf to .txt - String renameResponse = + response = api.renameAttachment( - appUrl, entityName, facetName, newEntityID, newAttachmentID, "renamed_document.txt"); - if (!renameResponse.equals("Renamed")) { - api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - api.deleteEntity(appUrl, entityName, newEntityID); - fail("Could not rename attachment: " + renameResponse); - } + appUrl, entityName, facetName, sourceEntity, sourceAttachmentID, editedFileName); + assertEquals("Renamed", response, "Attachment rename should succeed"); - // Step 6: Save and validate the extension change error message - String saveWithWarningResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - assertNotNull(saveWithWarningResponse, "Response should not be null"); - - String expectedMessage = - "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; - - com.fasterxml.jackson.databind.JsonNode messagesNode = - new ObjectMapper().readTree(saveWithWarningResponse); - assertTrue(messagesNode.isArray(), "sap-messages response should be a JSON array"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save after rename should succeed"); - boolean foundExtensionError = false; - for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { - if (messageNode.has("message")) { - String message = messageNode.get("message").asText(); - if (message.contains("Changing the file extension is not allowed")) { - foundExtensionError = true; - assertEquals( - expectedMessage, - message, - "Extension change error message does not match expected value"); - break; - } + // Verify renamed filename on source + Map sourceMetadata = + api.fetchMetadata(appUrl, entityName, facetName, sourceEntity, sourceAttachmentID); + assertEquals( + editedFileName, sourceMetadata.get("fileName"), "Source should reflect edited filename"); + assertNotNull(sourceMetadata.get("objectId"), "Source attachment should have objectId"); + String sourceObjectId = sourceMetadata.get("objectId").toString(); + + // Step 3: Create target entity and copy the attachment + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + + List objectIdsToCopy = new ArrayList<>(); + objectIdsToCopy.add(sourceObjectId); + + String copyResponse = + api.copyAttachment(appUrl, entityName, facetName, targetEntity, objectIdsToCopy); + assertEquals("Attachments copied successfully", copyResponse, "Copy should succeed"); + + // Save target entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target entity save should succeed"); + + // Step 4: Verify target attachment has the edited filename + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertFalse(targetAttachments.isEmpty(), "Target entity should have attachments"); + + boolean foundEditedFile = false; + for (Map attachment : targetAttachments) { + if (editedFileName.equals(attachment.get("fileName"))) { + foundEditedFile = true; + break; } } - assertTrue( - foundExtensionError, - "Expected extension change warning not found in response. Full response: " - + saveWithWarningResponse); + foundEditedFile, + "Target entity should have attachment with edited filename '" + + editedFileName + + "', not the original 'sample.pdf'"); // Clean up - api.deleteEntity(appUrl, entityName, newEntityID); + api.deleteEntity(appUrl, entityName, sourceEntity); + api.deleteEntity(appUrl, entityName, targetEntity); } @Test - @Order(77) - void testRenameAttachmentWithExtensionChange_WhileUpload() throws IOException { + @Order(96) + void testLinkAttachment_CreatedByIsUserNotClientId() throws Exception { System.out.println( - "Test (77) : Upload attachment in draft, rename changing extension before save - should return extension change warning"); + "Test (90) : Create a link attachment and verify createdBy/modifiedBy is the user," + + " not the application clientID"); - // Step 1: Create a new entity draft (do NOT save it yet) - String newEntityID = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (newEntityID.equals("Could not create entity")) { - fail("Could not create entity"); - } + // Create entity and add a link + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // Step 2: Upload a PDF attachment while entity is still in draft (unsaved) - ClassLoader classLoader = getClass().getClassLoader(); - File file = new File(classLoader.getResource("sample.pdf").getFile()); + String linkName = "testLink"; + String linkUrl = "https://www.example.com"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); - Map postData = new HashMap<>(); - postData.put("up__ID", newEntityID); - postData.put("mimeType", "application/pdf"); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + // Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - List createResponse = - api.createAttachment(appUrl, entityName, facetName, newEntityID, srvpath, postData, file); - String check = createResponse.get(0); - if (!check.equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, newEntityID); - fail("Could not upload sample.pdf: " + check); - } - String newAttachmentID = createResponse.get(1); + // Fetch link metadata to verify createdBy and modifiedBy + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have at least one attachment (link)"); - // Step 3: Rename the attachment changing extension from .pdf to .txt — entity still not saved - String renameResponse = - api.renameAttachment( - appUrl, entityName, facetName, newEntityID, newAttachmentID, "renamed_document.txt"); - if (!renameResponse.equals("Renamed")) { - api.deleteEntityDraft(appUrl, entityName, newEntityID); - fail("Could not rename attachment: " + renameResponse); + Map linkMetadata = attachments.get(0); + String linkID = (String) linkMetadata.get("ID"); + assertNotNull(linkID, "Link should have an ID"); + + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, testEntityID, linkID); + String createdBy = (String) metadata.get("createdBy"); + String modifiedBy = (String) metadata.get("modifiedBy"); + + assertNotNull(createdBy, "createdBy should not be null"); + assertNotNull(modifiedBy, "modifiedBy should not be null"); + + String tokenFlowFlag = System.getProperty("tokenFlow"); + if ("namedUser".equals(tokenFlowFlag)) { + assertEquals( + username, createdBy, "createdBy should be the user, not the application clientID"); + assertEquals( + username, modifiedBy, "modifiedBy should be the user, not the application clientID"); + } else { + assertFalse(createdBy.isEmpty(), "createdBy should not be empty"); + assertFalse(modifiedBy.isEmpty(), "modifiedBy should not be empty"); } - // Step 4: Save — should receive extension change warning, not "Saved" - String saveWithWarningResponse = api.saveEntityDraft(appUrl, entityName, srvpath, newEntityID); - assertNotNull(saveWithWarningResponse, "Response should not be null"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(97) + void testDeleteLink_NotPresentInRepository_RemovedFromUI() throws Exception { + System.out.println( + "Test (91) : Delete a link that has been removed from the repository" + + " — expect it is removed from the UI"); + + // Create entity and add a link + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "linkToDelete"; + String linkUrl = "https://www.example.com/delete-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + // Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + // Get the link's ID + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + assertNotNull(linkID, "Link should have an ID"); + + // Delete the link from the CMIS backend directly + System.out.println(" Deleting link from CMIS backend..."); + CmisDocumentHelper.deleteDocumentFromCmis(testEntityID, linkName); - String expectedMessage = - "Changing the file extension is not allowed. The file \"renamed_document.txt\" must retain its original extension \".pdf\"."; + // Enter draft mode and delete the link via the app + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - com.fasterxml.jackson.databind.JsonNode messagesNode = - new ObjectMapper().readTree(saveWithWarningResponse); - assertTrue(messagesNode.isArray(), "sap-messages response should be a JSON array"); + response = api.deleteAttachment(appUrl, entityName, facetName, testEntityID, linkID); + assertEquals("Deleted", response, "Delete link should succeed in the UI"); - boolean foundExtensionError = false; - for (com.fasterxml.jackson.databind.JsonNode messageNode : messagesNode) { - if (messageNode.has("message")) { - String message = messageNode.get("message").asText(); - if (message.contains("Changing the file extension is not allowed")) { - foundExtensionError = true; - assertEquals( - expectedMessage, - message, - "Extension change error message does not match expected value"); - break; - } - } - } + // Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed after deleting link"); - assertTrue( - foundExtensionError, - "Expected extension change warning not found in response. Full response: " - + saveWithWarningResponse); + // Verify the link is gone from the UI + List> remainingAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertTrue(remainingAttachments.isEmpty(), "No attachments should remain after link deletion"); // Clean up - api.deleteEntity(appUrl, entityName, newEntityID); + api.deleteEntity(appUrl, entityName, testEntityID); } @Test - @Order(78) - void testDownloadMultipleAttachments() throws IOException { + @Order(98) + void testRenameLinkToDuplicateName_BackendConflict_ErrorThrown() throws Exception { System.out.println( - "Test (76): Create entity, upload 3 attachments (pdf, txt, exe), and download all"); - boolean testStatus = false; + "Test (92) : Create a link via backend, rename existing link to same name" + + " — expect duplicate filename error"); - // Step 1: Create entity + // Create entity and add a link String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String downloadTestEntityID = response; - - ClassLoader classLoader = getClass().getClassLoader(); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; - // Step 2: Upload pdf, txt, exe in one draft session - Map postData = new HashMap<>(); - postData.put("up__ID", downloadTestEntityID); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + String linkName = "originalLink"; + String linkUrl = "https://www.example.com/original"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); - // Upload pdf - postData.put("mimeType", "application/pdf"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - List createResponse1 = - api.createAttachment( - appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, pdfFile); - if (!createResponse1.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not upload sample.pdf"); - return; - } - String downloadAttachmentID1 = createResponse1.get(1); + // Save entity to commit link to DI + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - // Upload txt - postData.put("mimeType", "application/txt"); - File txtFile = new File(classLoader.getResource("sample.txt").getFile()); - List createResponse2 = - api.createAttachment( - appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, txtFile); - if (!createResponse2.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not upload sample.txt"); - return; - } - String downloadAttachmentID2 = createResponse2.get(1); + // Get the link's ID + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); - // Upload exe - postData.put("mimeType", "application/exe"); - File exeFile = new File(classLoader.getResource("sample.exe").getFile()); - List createResponse3 = - api.createAttachment( - appUrl, entityName, facetName, downloadTestEntityID, srvpath, postData, exeFile); - if (!createResponse3.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not upload sample.exe"); - return; - } - String downloadAttachmentID3 = createResponse3.get(1); + // Create a link with a conflicting name directly in the CMIS backend + String conflictingName = "backendLink"; + System.out.println(" Creating '" + conflictingName + "' directly in CMIS backend..."); + ClassLoader classLoader = getClass().getClassLoader(); + CmisDocumentHelper.createDocumentInCmis( + conflictingName, classLoader.getResource("sample.pdf").getFile(), testEntityID); - // Step 3: Save entity draft - response = api.saveEntityDraft(appUrl, entityName, srvpath, downloadTestEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, downloadTestEntityID); - fail("Could not save entity draft: " + response); - return; - } + // Enter draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // Step 4: Select first attachment - Download button should be enabled - // Verify download works with a single attachment selection - String singleDownloadResult = - api.downloadSelectedAttachments( - appUrl, entityName, facetName, downloadTestEntityID, List.of(downloadAttachmentID1)); - JSONArray singleResultArray = new JSONArray(singleDownloadResult); - assertEquals(1, singleResultArray.length(), "Expected 1 result in download response"); - JSONObject singleResult = singleResultArray.getJSONObject(0); - assertEquals( - "success", - singleResult.getString("status"), - "Download button should be enabled: single attachment download should succeed"); - assertTrue(singleResult.has("content"), "Downloaded attachment should have a content field"); + // Rename the existing link to the conflicting name + System.out.println(" Renaming '" + linkName + "' to '" + conflictingName + "'..."); + response = + api.renameAttachment(appUrl, entityName, facetName, testEntityID, linkID, conflictingName); + assertEquals("Renamed", response, "Rename in draft should succeed"); - // Step 5: Select all 3 and click download - String multiDownloadResult = - api.downloadSelectedAttachments( - appUrl, - entityName, - facetName, - downloadTestEntityID, - List.of(downloadAttachmentID1, downloadAttachmentID2, downloadAttachmentID3)); - JSONArray multiResultArray = new JSONArray(multiDownloadResult); - assertEquals(3, multiResultArray.length(), "Expected 3 results in download response"); - for (int i = 0; i < multiResultArray.length(); i++) { - JSONObject result = multiResultArray.getJSONObject(i); - assertEquals( - "success", - result.getString("status"), - "Attachment " + (i + 1) + " should download successfully"); - assertTrue( - result.has("content"), - "Attachment " + (i + 1) + " should have a content field in the response"); - } - testStatus = true; + // Save the entity — should fail due to duplicate name + System.out.println(" Saving entity — expecting duplicate filename error..."); + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertNotEquals("Saved", response, "Save should fail due to duplicate filename in DI"); + System.out.println(" Save response: " + response); + assertTrue( + response.contains("already exist") || response.contains("error"), + "Error should indicate duplicate filename. Actual: " + response); // Clean up - api.deleteEntity(appUrl, entityName, downloadTestEntityID); - - if (!testStatus) { - fail("Multiple attachment download test failed"); - } + api.deleteEntity(appUrl, entityName, testEntityID); } @Test - @Order(79) - void testDownloadButtonDisabledWhenLinkSelected() throws IOException { + @Order(99) + void testRenameLink_WhitespaceOnly_WarningThrown() throws Exception { System.out.println( - "Test (77): Download button enabled for pdf only; disabled when link is also selected"); + "Test (93) : Rename a link with whitespace-only name" + + " — expect warning about empty filename"); - // Step 1: Create entity (already in draft mode) + // Create entity and add a link String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); String testEntityID = response; - ClassLoader classLoader = getClass().getClassLoader(); + String linkName = "linkToRename"; + String linkUrl = "https://www.example.com/rename-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, testEntityID, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); - // Step 2: Upload one pdf attachment (entity is already in draft mode) - Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); - postData.put("mimeType", "application/pdf"); - postData.put("createdAt", new Date().toString()); - postData.put("createdBy", "test@test.com"); - postData.put("modifiedBy", "test@test.com"); + // Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - List createResponse = - api.createAttachment( - appUrl, entityName, facetName, testEntityID, srvpath, postData, pdfFile); - if (!createResponse.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not upload sample.pdf"); - return; - } - String pdfAttachmentID = createResponse.get(1); + // Get the link's ID + List> attachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); - // Step 3: Create a link attachment (entity still in draft mode) - String linkResponse = - api.createLink( - appUrl, entityName, facetName, testEntityID, "TestLink", "https://www.example.com"); - if (!linkResponse.equals("Link created successfully")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not create link attachment"); - return; - } + // Enter draft mode + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); - // Save entity draft + // Rename link with whitespace only + String whitespaceOnlyName = " "; + System.out.println(" Renaming link to whitespace-only name..."); + response = + api.renameAttachment( + appUrl, entityName, facetName, testEntityID, linkID, whitespaceOnlyName); + assertEquals("Renamed", response, "Rename in draft should succeed"); + + // Save the entity — should return warning about empty filename + System.out.println(" Saving entity — expecting warning..."); response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not save entity draft: " + response); - return; - } + assertNotEquals("Saved", response, "Save should not succeed with whitespace-only filename"); + System.out.println(" Save response: " + response); + assertTrue( + response.contains("cannot be empty") || response.contains("could not be updated"), + "Warning should indicate filename cannot be empty. Actual: " + response); - // Fetch metadata to find the link attachment ID (mimeType = "application/internet-shortcut") - List> allAttachments = + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } + + @Test + @Order(100) + void testDiscardDraftEditedLink_RevertsToOriginalUrl() throws Exception { + System.out.println( + "Test (94) : Create link, save, edit link URL, discard draft" + + " — expect link reverts to original URL"); + + String originalUrl = "https://abc.com"; + String editedUrl = "https://xyz.com"; + + // Step 1: Create entity with link + String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals("Could not create entity", response, "Entity creation should succeed"); + String testEntityID = response; + + String linkName = "discardTestLink"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, testEntityID, linkName, originalUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + // Step 2: Save entity + response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Saved", response, "Entity save should succeed"); + + // Get the link's ID + List> attachments = api.fetchEntityMetadata(appUrl, entityName, facetName, testEntityID); - String linkAttachmentID = - allAttachments.stream() - .filter( - a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) - .map(a -> (String) a.get("ID")) - .findFirst() - .orElse(null); - if (linkAttachmentID == null) { - api.deleteEntity(appUrl, entityName, testEntityID); - fail("Could not find link attachment in entity metadata"); - return; + assertFalse(attachments.isEmpty(), "Entity should have the link"); + String linkID = (String) attachments.get(0).get("ID"); + assertNotNull(linkID, "Link should have an ID"); + + // Verify original URL is stored + Map metadataBefore = + api.fetchMetadata(appUrl, entityName, facetName, testEntityID, linkID); + assertEquals(originalUrl, metadataBefore.get("linkUrl"), "Link should have original URL"); + + // Step 3: Edit entity and change link URL + response = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); + assertEquals("Entity in draft mode", response, "Entity should enter draft mode"); + + String editResponse = + api.editLink(appUrl, entityName, facetName, testEntityID, linkID, editedUrl); + assertEquals("Link edited successfully", editResponse, "Link edit should succeed in draft"); + + // Step 4: Discard draft + System.out.println(" Discarding draft..."); + response = api.deleteEntityDraft(appUrl, entityName, testEntityID); + assertEquals("Entity Draft Deleted", response, "Discard draft should succeed"); + + // Step 5: Read attachment metadata — should revert to original URL + Map metadataAfterDiscard = + api.fetchMetadata(appUrl, entityName, facetName, testEntityID, linkID); + assertEquals( + originalUrl, + metadataAfterDiscard.get("linkUrl"), + "Link URL should revert to original after discarding draft"); + + // Also verify via CMIS that the document in DI still has original URL + String cmisName = CmisDocumentHelper.getCmisPropertyOrNull(testEntityID, linkName, "cmis:name"); + if (cmisName != null) { + assertEquals(linkName, cmisName, "Link name in CMIS should remain unchanged"); } - // Step 4: Select only the pdf - Download button should be enabled (succeeds) - String pdfOnlyResult = - api.downloadSelectedAttachments( - appUrl, entityName, facetName, testEntityID, List.of(pdfAttachmentID)); - JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); - assertEquals(1, pdfOnlyArray.length(), "Expected 1 result when only pdf is selected"); - assertEquals( - "success", - pdfOnlyArray.getJSONObject(0).getString("status"), - "Download button should be enabled: pdf-only download should succeed"); + // Clean up + api.deleteEntity(appUrl, entityName, testEntityID); + } - // Step 5: Select both pdf and link - Download button should be disabled - // (link attachment returns error status, disabling the download) - String mixedResult = - api.downloadSelectedAttachments( - appUrl, - entityName, - facetName, - testEntityID, - List.of(pdfAttachmentID, linkAttachmentID)); - JSONArray mixedArray = new JSONArray(mixedResult); - assertEquals(2, mixedArray.length(), "Expected 2 results when pdf and link are selected"); + @Test + @Order(101) + void testMoveAttachments_FromSdmFolder_ToTargetEntity() throws Exception { + System.out.println( + "Test (95) : Move attachments from standalone SDM folder to target entity" + + " — expect all attachments moved successfully"); - // Find the result for the link attachment and assert it has error status - JSONObject linkResult = null; - for (int i = 0; i < mixedArray.length(); i++) { - JSONObject item = mixedArray.getJSONObject(i); - if (linkAttachmentID.equals(item.getString("id"))) { - linkResult = item; - break; - } - } - assertNotNull(linkResult, "Result for link attachment should be present"); - assertEquals( - "error", - linkResult.getString("status"), - "Download button should be disabled: link attachment download should return error"); - assertEquals( - "Download is not supported for link attachments", - linkResult.getString("message"), - "Error message for link attachment download should match"); + // Step 1: Create a standalone folder in CMIS and upload documents + String folderName = "move-test-folder-" + System.currentTimeMillis(); + System.out.println(" Creating SDM folder: " + folderName); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); + + ClassLoader classLoader = getClass().getClassLoader(); + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); + + System.out.println(" Uploading documents to SDM folder..."); + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created in folder"); + assertNotNull(docId2, "Second document should be created in folder"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + // Step 2: Create target entity and save it + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + String response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target entity save should succeed"); + + // Step 3: Move attachments from SDM folder to target entity + String targetFacet = serviceName + "." + entityName + "." + facetName; + System.out.println(" Moving attachments from SDM folder to target entity..."); + Map moveResult = + api.moveAttachment( + appUrl, + entityName, + facetName, + targetEntity, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + // Step 4: Verify all attachments are on target entity + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals(2, targetAttachments.size(), "Target entity should have 2 attachments after move"); + + // Verify attachments are readable + for (Map attachment : targetAttachments) { + String attachmentId = (String) attachment.get("ID"); + String readResponse = + api.readAttachment(appUrl, entityName, facetName, targetEntity, attachmentId); + assertEquals("OK", readResponse, "Moved attachment should be readable"); + } // Clean up - api.deleteEntity(appUrl, entityName, testEntityID); + api.deleteEntity(appUrl, entityName, targetEntity); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); } @Test - @Order(80) - void testDownloadMultipleAttachmentsInDraftState() throws IOException { + @Order(102) + void testMoveAttachments_FromSdmFolder_DuplicateInTarget_Skipped() throws Exception { System.out.println( - "Test (78): Create entity in draft state, upload 3 attachments (pdf, txt, exe), and" - + " download before saving"); - boolean testStatus = false; + "Test (96) : Move attachments from SDM folder when duplicate exists in target" + + " — expect duplicate skipped, others moved"); - // Step 1: Create entity draft (do NOT save) - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String draftEntityID = response; + // Step 1: Create a standalone folder in CMIS and upload documents + String folderName = "move-dup-test-folder-" + System.currentTimeMillis(); + System.out.println(" Creating SDM folder: " + folderName); + String sourceFolderId = CmisDocumentHelper.createFolderInCmis(folderName); + assertNotNull(sourceFolderId, "Source folder should be created in CMIS"); ClassLoader classLoader = getClass().getClassLoader(); - - // Step 2: Upload pdf, txt, exe while entity remains in draft state + String pdfPath = classLoader.getResource("sample.pdf").getFile(); + String pdf1Path = classLoader.getResource("sample1.pdf").getFile(); + + System.out.println(" Uploading documents to SDM folder..."); + String docId1 = + CmisDocumentHelper.createDocumentInFolder("sample.pdf", pdfPath, sourceFolderId); + String docId2 = + CmisDocumentHelper.createDocumentInFolder("sample1.pdf", pdf1Path, sourceFolderId); + assertNotNull(docId1, "First document should be created"); + assertNotNull(docId2, "Second document should be created"); + + List objectIdsToMove = new ArrayList<>(); + objectIdsToMove.add(docId1); + objectIdsToMove.add(docId2); + + // Step 2: Create target entity with one duplicate attachment (sample.pdf) + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + + File duplicateFile = new File(classLoader.getResource("sample.pdf").getFile()); Map postData = new HashMap<>(); - postData.put("up__ID", draftEntityID); + postData.put("up__ID", targetEntity); + postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - // Upload pdf - postData.put("mimeType", "application/pdf"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - List createResponse1 = - api.createAttachment( - appUrl, entityName, facetName, draftEntityID, srvpath, postData, pdfFile); - if (!createResponse1.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, draftEntityID); - fail("Could not upload sample.pdf"); - return; - } - String draftAttachmentID1 = createResponse1.get(1); - - // Upload txt - postData.put("mimeType", "application/txt"); - File txtFile = new File(classLoader.getResource("sample.txt").getFile()); - List createResponse2 = - api.createAttachment( - appUrl, entityName, facetName, draftEntityID, srvpath, postData, txtFile); - if (!createResponse2.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, draftEntityID); - fail("Could not upload sample.txt"); - return; - } - String draftAttachmentID2 = createResponse2.get(1); - - // Upload exe - postData.put("mimeType", "application/exe"); - File exeFile = new File(classLoader.getResource("sample.exe").getFile()); - List createResponse3 = + List createResponse = api.createAttachment( - appUrl, entityName, facetName, draftEntityID, srvpath, postData, exeFile); - if (!createResponse3.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, draftEntityID); - fail("Could not upload sample.exe"); - return; - } - String draftAttachmentID3 = createResponse3.get(1); - - // Step 3: Select first attachment - Download button should be enabled even in draft state - String singleDownloadResult = - api.downloadSelectedAttachmentsDraft( - appUrl, entityName, facetName, draftEntityID, List.of(draftAttachmentID1)); - JSONArray singleResultArray = new JSONArray(singleDownloadResult); - assertEquals(1, singleResultArray.length(), "Expected 1 result in download response"); - JSONObject singleResult = singleResultArray.getJSONObject(0); + appUrl, entityName, facetName, targetEntity, srvpath, postData, duplicateFile); assertEquals( - "success", - singleResult.getString("status"), - "Download button should be enabled in draft state: single attachment download should" - + " succeed"); - assertTrue(singleResult.has("content"), "Downloaded attachment should have a content field"); + "Attachment created", createResponse.get(0), "Target attachment should be created"); - // Step 4: Select all 3 and download while entity is still in draft state - String multiDownloadResult = - api.downloadSelectedAttachmentsDraft( + String response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target entity save should succeed"); + + // Step 3: Move attachments from SDM folder to target entity + String targetFacet = serviceName + "." + entityName + "." + facetName; + System.out.println(" Moving attachments (one duplicate expected)..."); + Map moveResult = + api.moveAttachment( appUrl, entityName, facetName, - draftEntityID, - List.of(draftAttachmentID1, draftAttachmentID2, draftAttachmentID3)); - JSONArray multiResultArray = new JSONArray(multiDownloadResult); - assertEquals(3, multiResultArray.length(), "Expected 3 results in download response"); - for (int i = 0; i < multiResultArray.length(); i++) { - JSONObject result = multiResultArray.getJSONObject(i); - assertEquals( - "success", - result.getString("status"), - "Attachment " + (i + 1) + " should download successfully in draft state"); - assertTrue( - result.has("content"), - "Attachment " + (i + 1) + " should have a content field in the response"); - } - testStatus = true; - - // Clean up - entity was never saved, so delete the draft - api.deleteEntityDraft(appUrl, entityName, draftEntityID); + targetEntity, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + // Step 4: Verify target entity has original + non-duplicate moved + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals( + 2, + targetAttachments.size(), + "Target should have 2 attachments (1 original + 1 moved non-duplicate)"); + + // Verify file names present + List fileNames = + targetAttachments.stream() + .map(a -> (String) a.get("fileName")) + .collect(Collectors.toList()); + assertTrue(fileNames.contains("sample.pdf"), "Target should have sample.pdf"); + assertTrue(fileNames.contains("sample1.pdf"), "Target should have sample1.pdf (moved)"); - if (!testStatus) { - fail("Multiple attachment download in draft state test failed"); - } + // Clean up + api.deleteEntity(appUrl, entityName, targetEntity); + CmisDocumentHelper.deleteObjectFromCmis(sourceFolderId); } @Test - @Order(81) - void testDownloadButtonWithPdfAndLinkInDraftState() throws IOException { + @Order(103) + void testMoveAttachments_FromSdmFolder_WithSecondaryProperties_Preserved() throws Exception { System.out.println( - "Test (79): Upload pdf and link, save entity, edit entity (draft state)," - + " download button enabled for pdf only, disabled when link also selected"); + "Test (97) : Move attachments from SDM folder with secondary properties and links" + + " — expect properties and notes preserved after move"); - // Step 1: Create entity draft - String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - if (response.equals("Could not create entity")) { - fail("Could not create entity"); - return; - } - String testEntityID = response; + // Step 1: Create source entity, add attachments and link, set secondary properties + String sourceEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", sourceEntity, "Source entity creation should succeed"); ClassLoader classLoader = getClass().getClassLoader(); + File filePdf = new File(classLoader.getResource("sample.pdf").getFile()); + File fileTxt = new File(classLoader.getResource("sample.txt").getFile()); - // Step 2: Upload one pdf attachment (entity in draft state) Map postData = new HashMap<>(); - postData.put("up__ID", testEntityID); + postData.put("up__ID", sourceEntity); postData.put("mimeType", "application/pdf"); postData.put("createdAt", new Date().toString()); postData.put("createdBy", "test@test.com"); postData.put("modifiedBy", "test@test.com"); - File pdfFile = new File(classLoader.getResource("sample.pdf").getFile()); - List createResponse = + List createResponse1 = api.createAttachment( - appUrl, entityName, facetName, testEntityID, srvpath, postData, pdfFile); - if (!createResponse.get(0).equals("Attachment created")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not upload sample.pdf"); - return; - } - // Capture pdf attachment ID directly from upload response (draft state, reliable) - String pdfAttachmentID = createResponse.get(1); - - // Step 3: Create a link attachment (entity still in draft state) - String linkResponse = - api.createLink( - appUrl, entityName, facetName, testEntityID, "TestLink", "https://www.example.com"); - if (!linkResponse.equals("Link created successfully")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not create link attachment"); - return; - } - - // Fetch link attachment ID from draft metadata while still in draft state - List> draftAttachments = - api.fetchEntityMetadataDraft(appUrl, entityName, facetName, testEntityID); - String linkAttachmentID = - draftAttachments.stream() - .filter( - a -> "application/internet-shortcut".equalsIgnoreCase((String) a.get("mimeType"))) - .map(a -> (String) a.get("ID")) - .findFirst() - .orElse(null); - if (linkAttachmentID == null) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not find link attachment in draft entity metadata"); - return; - } - - // Step 4: Save entity - response = api.saveEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!response.equals("Saved")) { - api.deleteEntityDraft(appUrl, entityName, testEntityID); - fail("Could not save entity draft: " + response); - return; - } + appUrl, entityName, facetName, sourceEntity, srvpath, postData, filePdf); + assertEquals("Attachment created", createResponse1.get(0)); + String attachId1 = createResponse1.get(1); - // Step 5: Edit entity - puts it back into draft state - String editResponse = api.editEntityDraft(appUrl, entityName, srvpath, testEntityID); - if (!editResponse.equals("Entity in draft mode")) { - api.deleteEntity(appUrl, entityName, testEntityID); - fail("Could not put entity into edit/draft mode: " + editResponse); - return; + postData.put("mimeType", "text/plain"); + List createResponse2 = + api.createAttachment( + appUrl, entityName, facetName, sourceEntity, srvpath, postData, fileTxt); + assertEquals("Attachment created", createResponse2.get(0)); + String attachId2 = createResponse2.get(1); + + // Create a link + String linkName = "testMoveLink"; + String linkUrl = "https://www.example.com/move-test"; + String createLinkResponse = + api.createLink(appUrl, entityName, facetName, sourceEntity, linkName, linkUrl); + assertEquals("Link created successfully", createLinkResponse, "Link creation should succeed"); + + // Add notes and secondary property to attachments + String notesValue = "Move test note"; + MediaType mediaType = MediaType.parse("application/json"); + RequestBody notesBody = RequestBody.create(mediaType, "{\"note\": \"" + notesValue + "\"}"); + String updateNotes1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, sourceEntity, attachId1, notesBody); + assertEquals("Updated", updateNotes1, "Notes update should succeed for attachment 1"); + + Integer customIntValue = 42; + RequestBody intBody = + RequestBody.create(mediaType, "{\"customProperty2\": " + customIntValue + "}"); + String updateInt1 = + api.updateSecondaryProperty( + appUrl, entityName, facetName, sourceEntity, attachId1, intBody); + assertEquals("Updated", updateInt1, "Custom property update should succeed for attachment 1"); + + // Save source entity + String response = api.saveEntityDraft(appUrl, entityName, srvpath, sourceEntity); + assertEquals("Saved", response, "Source entity save should succeed"); + + // Step 2: Fetch objectIds and folderId from source entity + List objectIdsToMove = new ArrayList<>(); + String sourceFolderId = null; + + List> sourceAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, sourceEntity); + for (Map attachment : sourceAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, sourceEntity, attId); + if (metadata.get("objectId") != null) { + objectIdsToMove.add(metadata.get("objectId").toString()); + } + if (sourceFolderId == null && metadata.get("folderId") != null) { + sourceFolderId = metadata.get("folderId").toString(); + } } - - // Step 7: Select only pdf - Download button should be enabled (succeeds) - String pdfOnlyResult = - api.downloadSelectedAttachmentsDraft( - appUrl, entityName, facetName, testEntityID, List.of(pdfAttachmentID)); - JSONArray pdfOnlyArray = new JSONArray(pdfOnlyResult); - assertEquals(1, pdfOnlyArray.length(), "Expected 1 result when only pdf is selected"); - assertEquals( - "success", - pdfOnlyArray.getJSONObject(0).getString("status"), - "Download button should be enabled in draft state: pdf-only download should succeed"); - - // Step 8: Select pdf + link - Download button should be disabled - // (link attachment returns error status, disabling the download) - String mixedResult = - api.downloadSelectedAttachmentsDraft( + assertNotNull(sourceFolderId, "Source folder ID should be found"); + assertFalse(objectIdsToMove.isEmpty(), "Should have objectIds to move"); + + // Step 3: Create target entity and save it + String targetEntity = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); + assertNotEquals( + "Could not create entity", targetEntity, "Target entity creation should succeed"); + response = api.saveEntityDraft(appUrl, entityName, srvpath, targetEntity); + assertEquals("Saved", response, "Target entity save should succeed"); + + // Step 4: Move attachments from source to target (without sourceFacet) + String targetFacet = serviceName + "." + entityName + "." + facetName; + System.out.println(" Moving attachments with secondary properties..."); + Map moveResult = + api.moveAttachment( appUrl, entityName, facetName, - testEntityID, - List.of(pdfAttachmentID, linkAttachmentID)); - JSONArray mixedArray = new JSONArray(mixedResult); - assertEquals(2, mixedArray.length(), "Expected 2 results when pdf and link are selected"); + targetEntity, + sourceFolderId, + objectIdsToMove, + targetFacet, + null); + assertNotNull(moveResult, "Move result should not be null"); + + // Step 5: Verify target entity has all attachments (files + link) + List> targetAttachments = + api.fetchEntityMetadata(appUrl, entityName, facetName, targetEntity); + assertEquals( + sourceAttachments.size(), + targetAttachments.size(), + "Target should have all attachments after move"); + + // Step 6: Verify secondary properties and notes are preserved + boolean foundWithNotes = false; + boolean foundLink = false; + for (Map attachment : targetAttachments) { + String attId = (String) attachment.get("ID"); + Map metadata = + api.fetchMetadata(appUrl, entityName, facetName, targetEntity, attId); + + // Check if this is the attachment with notes + if (notesValue.equals(metadata.get("note"))) { + foundWithNotes = true; + assertEquals( + customIntValue, + metadata.get("customProperty2"), + "Custom property should be preserved after move"); + } - JSONObject linkResult = null; - for (int i = 0; i < mixedArray.length(); i++) { - JSONObject item = mixedArray.getJSONObject(i); - if (linkAttachmentID.equals(item.getString("id"))) { - linkResult = item; - break; + // Check if this is the link + if (linkUrl.equals(metadata.get("linkUrl"))) { + foundLink = true; } } - assertNotNull(linkResult, "Result for link attachment should be present"); - assertEquals( - "error", - linkResult.getString("status"), - "Download button should be disabled in draft state: link attachment should return error"); - assertEquals( - "Download is not supported for link attachments", - linkResult.getString("message"), - "Error message for link attachment download should match"); + assertTrue( + foundWithNotes, "Attachment with notes and secondary properties should be preserved"); + assertTrue(foundLink, "Link should be moved successfully"); // Clean up - api.deleteEntity(appUrl, entityName, testEntityID); + api.deleteEntity(appUrl, entityName, targetEntity); + api.deleteEntity(appUrl, entityName, sourceEntity); } - - // @Test - // @Order(78) - // void testUploadAttachmentExceedingMaximumFileSize() throws IOException { - // System.out.println( - // "Test (76) : Upload attachment exceeding maximum file size in references facet"); - - // // Create a new entity - // String response = api.createEntityDraft(appUrl, entityName, entityName2, srvpath); - // if (response.equals("Could not create entity")) { - // fail("Could not create entity"); - // } - // String testEntityID = response; - - // // Load the 150MB sample file - // ClassLoader classLoader = getClass().getClassLoader(); - // File file = new File(classLoader.getResource("sample32mb.pdf").getFile()); - - // Map postData = new HashMap<>(); - // postData.put("up__ID", testEntityID); - // postData.put("mimeType", "application/pdf"); - // postData.put("createdAt", new Date().toString()); - // postData.put("createdBy", "test@test.com"); - // postData.put("modifiedBy", "test@test.com"); - - // // Try to upload to the 'references' facet which has the 100MB limit - // String referencesFacet = "references"; - // List createResponse = - // api.createAttachment( - // appUrl, entityName, referencesFacet, testEntityID, srvpath, postData, file); - // String check = createResponse.get(0); - - // // The upload should fail with AttachmentSizeExceeded error - // if (!check.equals("Attachment created")) { - // try { - // JSONObject json = new JSONObject(check); - // String errorCode = json.getJSONObject("error").getString("code"); - // String errorMessage = json.getJSONObject("error").getString("message"); - // assertEquals("413", errorCode); - // assertEquals("File size exceeds the limit of 30MB.", errorMessage); - // } catch (Exception e) { - // fail("Failed to parse error response: " + e.getMessage()); - // } - // } else { - // fail("Attachment got created with file size exceeding maximum limit"); - // } - - // // delete the test entity draft - // api.deleteEntityDraft(appUrl, entityName, testEntityID); - // } } diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Subscription.java b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Subscription.java index 2ec8dc3f..2c5ada23 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Subscription.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Subscription.java @@ -41,12 +41,25 @@ class IntegrationTest_Subscription { private static Properties credentials; private static String consumerSubdomain; + /** Cached OAuth2 token for the consumer subdomain — fetched once in @BeforeAll. */ + private static Map cmisEnv; + @BeforeAll static void setup() throws Exception { credentials = Credentials.getCredentials(System.getProperty("tenant", "TENANT1")); consumerSubdomain = credentials.getProperty("consumerSubdomainMT"); assertNotNull(consumerSubdomain, "consumerSubdomainMT must be set in credentials.properties"); + // Fetch OAuth2 token once for all CMIS calls in this test run. + // Stored in cmisEnv and passed via CMIS_ACCESS_TOKEN env var to sdm-repo-manage.sh, + // which short-circuits the per-call HTTP token fetch in get_token(). + System.out.println("BeforeAll: Fetching CMIS access token..."); + String token = + ShellScriptRunner.runAndCaptureOutput( + REPO_MANAGE_SCRIPT, "get-token", "--subdomain", consumerSubdomain); + assertNotNull(token, "CMIS access token must not be null"); + cmisEnv = Map.of("CMIS_ACCESS_TOKEN", token); + // Ensure subscription is active before tests run System.out.println("BeforeAll: Ensuring app is subscribed..."); int subscribeExit = ShellScriptRunner.run(TENANT_ENV, SUBSCRIBE_SCRIPT); @@ -55,39 +68,33 @@ static void setup() throws Exception { // Verify repo exists after subscription; if not, onboard it System.out.println("BeforeAll: Checking if repo exists..."); - ShellScriptRunner.Result repoResult = - ShellScriptRunner.runAndCaptureAll( - REPO_MANAGE_SCRIPT, - "check", - "--externalId", - SUBSCRIPTION_REPO_EXTERNAL_ID, - "--subdomain", - consumerSubdomain); + ShellScriptRunner.Result repoResult = repoCheck(SUBSCRIPTION_REPO_EXTERNAL_ID); if (repoResult.getExitCode() != 0) { System.out.println("BeforeAll: Repo not found — onboarding..."); - int onboardExit = - ShellScriptRunner.run( - REPO_MANAGE_SCRIPT, - "onboard", - "--externalId", - SUBSCRIPTION_REPO_EXTERNAL_ID, - "--subdomain", - consumerSubdomain); - assertEquals(0, onboardExit, "Repo onboard should succeed"); + assertEquals(0, repoOnboard(SUBSCRIPTION_REPO_EXTERNAL_ID), "Repo onboard should succeed"); Thread.sleep(10_000); } System.out.println("BeforeAll: Subscription active and repo verified."); } /** Check if a repo exists in the consumer scope. Returns the Result. */ - private ShellScriptRunner.Result repoCheck(String externalId) throws Exception { + private static ShellScriptRunner.Result repoCheck(String externalId) throws Exception { + assertNotNull(cmisEnv, "cmisEnv is null — CMIS token was not fetched in @BeforeAll"); return ShellScriptRunner.runAndCaptureAll( - REPO_MANAGE_SCRIPT, "check", "--externalId", externalId, "--subdomain", consumerSubdomain); + cmisEnv, + REPO_MANAGE_SCRIPT, + "check", + "--externalId", + externalId, + "--subdomain", + consumerSubdomain); } /** Onboard a repo in the consumer scope. Returns exit code. */ - private int repoOnboard(String externalId) throws Exception { + private static int repoOnboard(String externalId) throws Exception { + assertNotNull(cmisEnv, "cmisEnv is null — CMIS token was not fetched in @BeforeAll"); return ShellScriptRunner.run( + cmisEnv, REPO_MANAGE_SCRIPT, "onboard", "--externalId", @@ -97,8 +104,10 @@ private int repoOnboard(String externalId) throws Exception { } /** Offboard a repo in the consumer scope. Returns the Result. */ - private ShellScriptRunner.Result repoOffboard(String externalId) throws Exception { + private static ShellScriptRunner.Result repoOffboard(String externalId) throws Exception { + assertNotNull(cmisEnv, "cmisEnv is null — CMIS token was not fetched in @BeforeAll"); return ShellScriptRunner.runAndCaptureAll( + cmisEnv, REPO_MANAGE_SCRIPT, "offboard", "--externalId", @@ -108,16 +117,68 @@ private ShellScriptRunner.Result repoOffboard(String externalId) throws Exceptio } /** Check if a repo exists in provider scope (no --subdomain). Returns the Result. */ - private ShellScriptRunner.Result repoCheckProviderScope(String externalId) throws Exception { + private static ShellScriptRunner.Result repoCheckProviderScope(String externalId) + throws Exception { return ShellScriptRunner.runAndCaptureAll( REPO_MANAGE_SCRIPT, "check", "--externalId", externalId); } /** Onboard a repo in provider scope (no --subdomain). Returns exit code. */ - private int repoOnboardProviderScope(String externalId) throws Exception { + private static int repoOnboardProviderScope(String externalId) throws Exception { return ShellScriptRunner.run(REPO_MANAGE_SCRIPT, "onboard", "--externalId", externalId); } + /** + * Polls the CMIS API until the repo returns NOT_FOUND (exit 1) in the consumer scope, or the + * timeout is reached. Offboarding is async, so this retries every {@code intervalMs} up to {@code + * maxRetries} times before failing the test. + */ + private static void assertRepoOffboarded(String externalId) throws Exception { + int maxRetries = 6; + int intervalMs = 15_000; + for (int attempt = 1; attempt <= maxRetries; attempt++) { + ShellScriptRunner.Result result = repoCheck(externalId); + if (result.getExitCode() == 1) { + System.out.println( + " ✅ Repo '" + + externalId + + "' confirmed offboarded via CMIS (NOT_FOUND, attempt " + + attempt + + "/" + + maxRetries + + ")"); + return; + } + if (result.getExitCode() != 0) { + fail( + "CMIS check returned unexpected exit code " + + result.getExitCode() + + " for repo '" + + externalId + + "' (expected 0=found or 1=not_found). Output:\n" + + result.getOutput()); + return; + } + if (attempt < maxRetries) { + System.out.println( + " Repo still visible after unsubscribe (attempt " + + attempt + + "/" + + maxRetries + + ") — retrying in " + + (intervalMs / 1000) + + "s..."); + Thread.sleep(intervalMs); + } + } + fail( + "Repo '" + + externalId + + "' still exists in consumer scope " + + (maxRetries * intervalMs / 1000) + + "s after unsubscription"); + } + // ─────────────────────────────────────────────────────────────────────────── // Test 1 — Subscribe when already subscribed → handled gracefully, repo intact // ─────────────────────────────────────────────────────────────────────────── @@ -196,6 +257,10 @@ void testDeleteSubscription_MultipleRepos_OnlyCorrectRepoOffboarded() throws Exc 0, verifyOther.getExitCode(), "Other repo '" + otherRepo + "' should still exist after unsubscription"); + + // Extra check: verify via CMIS API that the subscription repo is no longer accessible + System.out.println(" Verifying subscription repo offboarded via CMIS API..."); + assertRepoOffboarded(SUBSCRIPTION_REPO_EXTERNAL_ID); } // ─────────────────────────────────────────────────────────────────────────── @@ -248,6 +313,10 @@ void testDeleteSubscription_OnlyCorrectRepo_RepoOffboarded() throws Exception { offboarded, "CF logs should confirm repo was offboarded. Logs:\n" + logOutput.substring(0, Math.min(logOutput.length(), 2000))); + + // Extra check: verify via CMIS API that the subscription repo is no longer accessible + System.out.println(" Verifying subscription repo offboarded via CMIS API..."); + assertRepoOffboarded(SUBSCRIPTION_REPO_EXTERNAL_ID); } // ─────────────────────────────────────────────────────────────────────────── @@ -260,7 +329,7 @@ void testDeleteSubscription_RepoDoesNotExist_Logs404() throws Exception { "Test (4) : Unsubscribe when repo doesn't exist — expect logs to indicate 404 from DI"); // Pre-condition: Ensure subscribed but repo does NOT exist - // Wait extra time for test 4's unsubscribe to fully complete + // Wait extra time for test 3's unsubscribe to fully complete Thread.sleep(30_000); System.out.println(" Subscribing..."); @@ -324,6 +393,10 @@ void testDeleteSubscription_RepoDoesNotExist_Logs404() throws Exception { has404Indication, "CF logs should indicate a 404 or 'not found' when offboarding non-existent repo. Logs:\n" + logOutput.substring(0, Math.min(logOutput.length(), 2000))); + + // Extra check: verify via CMIS API that the repo is still absent in consumer scope + System.out.println(" Verifying repo remains absent via CMIS API..."); + assertRepoOffboarded(SUBSCRIPTION_REPO_EXTERNAL_ID); } // ─────────────────────────────────────────────────────────────────────────── @@ -375,4 +448,96 @@ void testCreateSubscription_NoExistingRepo_RepoOnboarded() throws Exception { assertTrue( verifyResult.containsIgnoreCase("FOUND"), "Check output should confirm repo was found"); } + + // ─────────────────────────────────────────────────────────────────────────── + // Test 6 — Register custom CMIS Secondary Types in the subscribed repository + // and verify they are queryable. + // + // After test 5 the consumer is subscribed and SUBSCRIPTION_REPO_EXTERNAL_ID + // is onboarded. We POST two secondary-type definitions (read from JSON + // resources) via the CMIS browser-binding's createType action, then query + // each one back via cmisselector=typeDefinition to confirm registration. + // + // The register-type helper treats "already exists" responses as success so + // re-runs of this test against the same repo are idempotent. + // ─────────────────────────────────────────────────────────────────────────── + @Test + @Order(6) + void testSubscribedTenant_RegisterSecondaryTypes_VerifyAvailable() throws Exception { + System.out.println( + "Test (6) : Register CMIS secondary types in subscribed repo and verify they are queryable"); + + final String typeManageScript = + "src/test/java/integration/com/sap/cds/sdm/utils/sdm-type-manage.sh"; + + final String[][] secondaryTypes = { + {"abc:bo", "src/test/resources/secondary-types/abc-bo-type.json"}, + {"Working:DocumentInfo", "src/test/resources/secondary-types/documentinfo-type.json"} + }; + + // Pre-condition: subscription must be active and the repo must be onboarded + // (left in place by test 5 / @BeforeAll). + assertNotNull(cmisEnv, "cmisEnv is null — CMIS token was not fetched in @BeforeAll"); + System.out.println(" Verifying subscription repo is present before registering types..."); + ShellScriptRunner.Result preCheck = repoCheck(SUBSCRIPTION_REPO_EXTERNAL_ID); + assertEquals( + 0, + preCheck.getExitCode(), + "Pre-condition: repo '" + + SUBSCRIPTION_REPO_EXTERNAL_ID + + "' must exist before type registration"); + + for (String[] entry : secondaryTypes) { + String typeId = entry[0]; + String typeFile = entry[1]; + + // Step 1: Register the secondary type + System.out.println(" Registering secondary type '" + typeId + "' from " + typeFile + "..."); + int registerExit = + ShellScriptRunner.run( + cmisEnv, + typeManageScript, + "register-type", + "--externalId", + SUBSCRIPTION_REPO_EXTERNAL_ID, + "--typeFile", + typeFile, + "--subdomain", + consumerSubdomain); + assertEquals( + 0, + registerExit, + "register-type for '" + + typeId + + "' should succeed (exit 0 = created or already-exists, idempotent)"); + + // Step 2: Verify the type is queryable + System.out.println(" Verifying secondary type '" + typeId + "' is queryable..."); + ShellScriptRunner.Result getResult = + ShellScriptRunner.runAndCaptureAll( + cmisEnv, + typeManageScript, + "get-type", + "--externalId", + SUBSCRIPTION_REPO_EXTERNAL_ID, + "--typeId", + typeId, + "--subdomain", + consumerSubdomain); + assertEquals( + 0, + getResult.getExitCode(), + "get-type for '" + typeId + "' should succeed (HTTP 200 + body contains the typeId)"); + assertTrue( + getResult.containsIgnoreCase("FOUND"), + "get-type output for '" + typeId + "' should contain 'FOUND'"); + } + + System.out.println( + " ✅ All " + + secondaryTypes.length + + " secondary types registered and verified in '" + + SUBSCRIPTION_REPO_EXTERNAL_ID + + "'."); + } } diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/CmisDocumentHelper.java b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/CmisDocumentHelper.java index 02f85eee..b20fc291 100644 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/CmisDocumentHelper.java +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/CmisDocumentHelper.java @@ -13,6 +13,8 @@ public class CmisDocumentHelper { private static final String CREATE_SCRIPT = "src/test/java/integration/com/sap/cds/sdm/utils/create.sh"; + private static final String CREATE_FOLDER_SCRIPT = + "src/test/java/integration/com/sap/cds/sdm/utils/create-folder.sh"; private static final String GET_OBJECT_ID_SCRIPT = "src/test/java/integration/com/sap/cds/sdm/utils/get-object-id.sh"; private static final String DELETE_SCRIPT = @@ -42,10 +44,76 @@ private static Map getCmisEnv() { return null; } + public static Map getCmisEnvPublic() { + return getCmisEnv(); + } + + /** + * Creates a folder in the CMIS repository root via create-folder.sh and returns its object ID. + * + * @param folderName the name of the folder to create + * @return the CMIS object ID of the created folder + */ + public static String createFolderInCmis(String folderName) { + try { + Map env = getCmisEnv(); + String output = ShellScriptRunner.runAndCaptureOutput(env, CREATE_FOLDER_SCRIPT, folderName); + if (output != null && output.contains("Object ID:")) { + return output.substring(output.lastIndexOf("Object ID:") + 11).trim(); + } + fail("create-folder.sh did not return an Object ID. Output: " + output); + return null; + } catch (Exception e) { + fail("Failed to create folder in CMIS: " + e.getMessage()); + return null; + } + } + /** - * Resolves the CMIS parent folder ID from {@code entityId + "__attachments"}, then uploads a - * local file to that folder via create.sh. + * Uploads a local file to a specific CMIS folder (by folder object ID) via create.sh and returns + * the created document's object ID. * + * @param cmisName the name the document will have in the CMIS repository + * @param filePath path to the local file to upload + * @param parentFolderObjectId the CMIS object ID of the parent folder + * @return the CMIS object ID of the created document + */ + public static String createDocumentInFolder( + String cmisName, String filePath, String parentFolderObjectId) { + try { + Map env = getCmisEnv(); + String output = + ShellScriptRunner.runAndCaptureOutput( + env, CREATE_SCRIPT, cmisName, filePath, parentFolderObjectId); + if (output != null && output.contains("Object ID:")) { + return output.substring(output.lastIndexOf("Object ID:") + 11).trim(); + } + fail("create.sh did not return an Object ID. Output: " + output); + return null; + } catch (Exception e) { + fail("Failed to create document in CMIS folder: " + e.getMessage()); + return null; + } + } + + /** + * Deletes a CMIS object (folder or document) by its object ID via delete.sh. + * + * @param objectId the CMIS object ID to delete + */ + public static void deleteObjectFromCmis(String objectId) { + try { + Map env = getCmisEnv(); + int exitCode = ShellScriptRunner.run(env, DELETE_SCRIPT, objectId); + if (exitCode != 0) { + System.out.println("WARNING: delete.sh exited with non-zero code: " + exitCode); + } + } catch (Exception e) { + System.out.println("WARNING: Failed to delete CMIS object: " + e.getMessage()); + } + } + + /** * @param cmisName the name the document will have in the CMIS repository * @param filePath path to the local file to upload * @param entityId the entity ID whose attachments folder is the upload target @@ -152,31 +220,57 @@ public static void readDocumentFromCmis(String entityId, String fileName, String * @return the JSON metadata string returned by the CMIS API */ public static String readDocumentMetadataFromCmis(String entityId, String fileName) { - try { - Map env = getCmisEnv(); - String folderName = entityId + "__attachments"; - String folderLine = - ShellScriptRunner.runAndCaptureOutput(env, GET_OBJECT_ID_SCRIPT, folderName); - String parentFolderObjectId = - folderLine != null && folderLine.contains(": ") - ? folderLine.substring(folderLine.lastIndexOf(": ") + 2).trim() - : folderLine; + int maxRetries = 5; + int retryDelayMs = 15000; + Exception lastException = null; + for (int attempt = 1; attempt <= maxRetries; attempt++) { + try { + Map env = getCmisEnv(); + String folderName = entityId + "__attachments"; + String folderLine = + ShellScriptRunner.runAndCaptureOutput(env, GET_OBJECT_ID_SCRIPT, folderName); + String parentFolderObjectId = + folderLine != null && folderLine.contains(": ") + ? folderLine.substring(folderLine.lastIndexOf(": ") + 2).trim() + : folderLine; - String docLine = - ShellScriptRunner.runAndCaptureOutput( - env, GET_OBJECT_ID_SCRIPT, fileName, parentFolderObjectId, "cmis:document"); - String documentObjectId = - docLine != null && docLine.contains(": ") - ? docLine.substring(docLine.lastIndexOf(": ") + 2).trim() - : docLine; + String docLine = + ShellScriptRunner.runAndCaptureOutput( + env, GET_OBJECT_ID_SCRIPT, fileName, parentFolderObjectId, "cmis:document"); + String documentObjectId = + docLine != null && docLine.contains(": ") + ? docLine.substring(docLine.lastIndexOf(": ") + 2).trim() + : docLine; - String metadata = - ShellScriptRunner.runAndCaptureOutput(env, GET_METADATA_SCRIPT, documentObjectId); - return metadata; - } catch (Exception e) { - fail("Failed to read document metadata from CMIS: " + e.getMessage()); - return null; + String metadata = + ShellScriptRunner.runAndCaptureOutput(env, GET_METADATA_SCRIPT, documentObjectId); + return metadata; + } catch (Exception e) { + lastException = e; + if (attempt < maxRetries + && e.getMessage() != null + && e.getMessage().contains("No cmis:document found")) { + System.out.println( + "CMIS document not found yet (eventual consistency), retrying after " + + retryDelayMs + + "ms... (attempt " + + attempt + + "/" + + maxRetries + + ")"); + try { + Thread.sleep(retryDelayMs); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } else { + break; + } + } } + fail("Failed to read document metadata from CMIS: " + lastException.getMessage()); + return null; } /** @@ -202,4 +296,19 @@ public static String getCmisProperty(String entityId, String fileName, String pr return null; } } + + public static String getCmisPropertyOrNull( + String entityId, String fileName, String propertyName) { + try { + String metadata = readDocumentMetadataFromCmis(entityId, fileName); + JsonNode root = new ObjectMapper().readTree(metadata); + JsonNode valueNode = root.path("properties").path(propertyName).path("value"); + if (valueNode.isMissingNode() || valueNode.isNull()) { + return null; + } + return valueNode.asText(); + } catch (Exception e) { + return null; + } + } } diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/create-folder.sh b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/create-folder.sh new file mode 100755 index 00000000..4944c8f0 --- /dev/null +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/create-folder.sh @@ -0,0 +1,103 @@ +#!/bin/bash +set -euo pipefail + +# --------------------------------------------------------------------------- +# create-folder.sh — Create a folder in SAP Document Management Service via CMIS API +# +# Usage: ./create-folder.sh [parentFolderID] +# +# folderName The name the folder will have inside the CMIS repository +# parentFolderID (Optional) CMIS object ID of the parent folder. +# If not provided, the folder is created at the repository root. +# +# Required config in credentials.properties: +# CMIS_URL, defaultRepositoryID, authUrl, cmisClientID, cmisClientSecret +# --------------------------------------------------------------------------- + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="${SCRIPT_DIR}/../../../../../../../resources/credentials.properties" + +load_props() { + local key val + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue + key="${line%%=*}" + val="${line#*=}" + key="${key//[[:space:]]/}" + [[ -z "$key" ]] && continue + printf -v "$key" '%s' "$val" + done < "$1" +} + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "ERROR: Config file not found at $CONFIG_FILE" + exit 1 +fi +load_props "$CONFIG_FILE" +defaultRepositoryID="${SDM_REPOSITORY_ID:-$defaultRepositoryID}" +authUrl="${SDM_AUTH_URL:-$authUrl}" +CMIS_URL="${CMIS_URL%/}/" + +if [[ $# -lt 1 || $# -gt 2 ]]; then + echo "Usage: $0 [parentFolderID]" + exit 1 +fi + +FOLDER_NAME="$1" +PARENT_FOLDER_ID="${2:-}" + +for var in CMIS_URL defaultRepositoryID authUrl cmisClientID cmisClientSecret username password; do + if [[ -z "${!var:-}" ]]; then + echo "ERROR: $var is not set in $CONFIG_FILE" + exit 1 + fi +done + +TOKEN_RESPONSE=$(curl -s -X POST "${authUrl}/oauth/token" \ + --data-urlencode "grant_type=password" \ + --data-urlencode "client_id=${cmisClientID}" \ + --data-urlencode "client_secret=${cmisClientSecret}" \ + --data-urlencode "username=${username}" \ + --data-urlencode "password=${password}") + +ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" \ + | grep -o '"access_token":"[^"]*"' \ + | sed 's/"access_token":"//;s/"$//' || true) + +if [[ -z "$ACCESS_TOKEN" ]]; then + echo "ERROR: Failed to obtain access token." + echo "Token endpoint response: $TOKEN_RESPONSE" + exit 1 +fi + +if [[ -n "${PARENT_FOLDER_ID}" ]]; then + CMIS_ENDPOINT="${CMIS_URL}browser/${defaultRepositoryID}/root?objectId=${PARENT_FOLDER_ID}" +else + CMIS_ENDPOINT="${CMIS_URL}browser/${defaultRepositoryID}/root" +fi + +RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X POST "$CMIS_ENDPOINT" \ + -H "Authorization: Bearer $ACCESS_TOKEN" \ + -F "cmisaction=createFolder" \ + -F "propertyId[0]=cmis:name" \ + -F "propertyValue[0]=${FOLDER_NAME}" \ + -F "propertyId[1]=cmis:objectTypeId" \ + -F "propertyValue[1]=cmis:folder" \ + -F "succinct=true") + +HTTP_CODE=$(echo "$RESPONSE" | tail -n1) +BODY=$(echo "$RESPONSE" | sed '$d') + +if [[ "$HTTP_CODE" == "201" || "$HTTP_CODE" == "200" ]]; then + OBJECT_ID=$(echo "$BODY" \ + | grep -o '"cmis:objectId":"[^"]*"' \ + | head -1 \ + | sed 's/"cmis:objectId":"//;s/"$//') + echo "SUCCESS: Folder '${FOLDER_NAME}' created." + echo "Object ID: ${OBJECT_ID}" +else + echo "ERROR: Failed to create folder (HTTP ${HTTP_CODE})." + echo "$BODY" + exit 1 +fi diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-repo-manage.sh b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-repo-manage.sh index 9730a06c..dd978057 100755 --- a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-repo-manage.sh +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-repo-manage.sh @@ -88,7 +88,15 @@ if [[ -n "$SUBDOMAIN" ]]; then fi # --- Obtain OAuth2 access token --- +# If CMIS_ACCESS_TOKEN env var is already set (passed by the Java test harness), +# skip the HTTP call and reuse it. This avoids one round-trip per script invocation +# when tests pre-fetch the token once and pass it through ProcessBuilder.environment(). get_token() { + if [[ -n "${CMIS_ACCESS_TOKEN:-}" ]]; then + ACCESS_TOKEN="$CMIS_ACCESS_TOKEN" + return + fi + local TOKEN_RESPONSE if [[ -n "$SUBDOMAIN" ]]; then # Use client_credentials grant for consumer-scoped access @@ -301,17 +309,29 @@ for r in repos: fi } +# =========================================================================== +# ACTION: get-token — Fetch an access token and print it to stdout. +# Used by the Java test harness to obtain the token once and cache it +# in a static field, then pass it back via CMIS_ACCESS_TOKEN env var +# on all subsequent invocations to skip repeated HTTP round-trips. +# =========================================================================== +action_get_token() { + get_token + echo "$ACCESS_TOKEN" +} + # =========================================================================== # Dispatch action # =========================================================================== case "$ACTION" in - check) action_check ;; - onboard) action_onboard ;; - offboard) action_offboard ;; - list) action_list ;; + check) action_check ;; + onboard) action_onboard ;; + offboard) action_offboard ;; + list) action_list ;; + get-token) action_get_token ;; *) echo "Unknown action: $ACTION" - echo "Usage: $0 {check|onboard|offboard|list} [options]" + echo "Usage: $0 {check|onboard|offboard|list|get-token} [options]" exit 2 ;; esac diff --git a/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-type-manage.sh b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-type-manage.sh new file mode 100755 index 00000000..389f4e11 --- /dev/null +++ b/sdm/src/test/java/integration/com/sap/cds/sdm/utils/sdm-type-manage.sh @@ -0,0 +1,284 @@ +#!/bin/bash +set -euo pipefail + +# --------------------------------------------------------------------------- +# sdm-type-manage.sh — Manage CMIS Secondary Types in an SDM repository. +# +# Usage: +# ./sdm-type-manage.sh register-type --externalId --typeFile [--subdomain ] +# ./sdm-type-manage.sh get-type --externalId --typeId [--subdomain ] +# +# Exit codes: +# register-type: 0 = created OR already exists (idempotent), +# 1 = repository not found, +# 2 = error (auth, network, or non-recoverable HTTP) +# get-type: 0 = type exists in repository (HTTP 200, body contains the typeId), +# 1 = type NOT found (HTTP 404 or HTTP 200 but body lacks typeId), +# 2 = error +# +# Required config in credentials.properties: +# CMIS_URL, authUrl, cmisClientID, cmisClientSecret, username, password +# +# When --subdomain is provided: +# - The OAuth token is obtained via client_credentials against the consumer's UAA URL +# (provider subdomain in authUrl is replaced by the given consumer subdomain). +# - This matches the auth pattern used by sdm-repo-manage.sh for consumer-scoped operations. +# +# When the env var CMIS_ACCESS_TOKEN is set, the OAuth fetch is skipped — the test +# harness pre-fetches the token once in @BeforeAll and threads it through here. +# --------------------------------------------------------------------------- + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="${SCRIPT_DIR}/../../../../../../../resources/credentials.properties" + +# --- Load key=value pairs from properties file --- +load_props() { + local key val + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue + key="${line%%=*}" + val="${line#*=}" + key="${key//[[:space:]]/}" + [[ -z "$key" ]] && continue + printf -v "$key" '%s' "$val" + done < "$1" +} + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "ERROR: Config file not found at $CONFIG_FILE" + exit 2 +fi +load_props "$CONFIG_FILE" +CMIS_URL="${CMIS_URL%/}/" + +# --- Parse command --- +if [[ $# -lt 1 ]]; then + echo "Usage: $0 {register-type|get-type} [options]" + exit 2 +fi + +ACTION="$1" +shift + +EXTERNAL_ID="" +TYPE_FILE="" +TYPE_ID="" +SUBDOMAIN="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --externalId) EXTERNAL_ID="$2"; shift 2 ;; + --typeFile) TYPE_FILE="$2"; shift 2 ;; + --typeId) TYPE_ID="$2"; shift 2 ;; + --subdomain) SUBDOMAIN="$2"; shift 2 ;; + *) echo "Unknown argument: $1"; exit 2 ;; + esac +done + +# --- Validate required config --- +for var in CMIS_URL authUrl cmisClientID cmisClientSecret; do + if [[ -z "${!var:-}" ]]; then + echo "ERROR: $var is not set in $CONFIG_FILE" + exit 2 + fi +done + +# --- Resolve token URL (replace provider subdomain with consumer if --subdomain given) --- +RESOLVED_TOKEN_URL="$authUrl" +if [[ -n "$SUBDOMAIN" ]]; then + PROVIDER_SUBDOMAIN=$(echo "$authUrl" | sed -n 's|.*://\([^.]*\)\..*|\1|p') + RESOLVED_TOKEN_URL="${authUrl/$PROVIDER_SUBDOMAIN/$SUBDOMAIN}" + echo "Using consumer subdomain: $SUBDOMAIN (token URL: $RESOLVED_TOKEN_URL)" +fi + +# --- Obtain OAuth2 access token (or reuse pre-fetched one) --- +get_token() { + if [[ -n "${CMIS_ACCESS_TOKEN:-}" ]]; then + ACCESS_TOKEN="$CMIS_ACCESS_TOKEN" + return + fi + + local TOKEN_RESPONSE + if [[ -n "$SUBDOMAIN" ]]; then + TOKEN_RESPONSE=$(curl -s -X POST "${RESOLVED_TOKEN_URL}/oauth/token" \ + --data-urlencode "grant_type=client_credentials" \ + --data-urlencode "client_id=${cmisClientID}" \ + --data-urlencode "client_secret=${cmisClientSecret}") + else + TOKEN_RESPONSE=$(curl -s -X POST "${RESOLVED_TOKEN_URL}/oauth/token" \ + --data-urlencode "grant_type=password" \ + --data-urlencode "client_id=${cmisClientID}" \ + --data-urlencode "client_secret=${cmisClientSecret}" \ + --data-urlencode "username=${username}" \ + --data-urlencode "password=${password}") + fi + + ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" \ + | grep -o '"access_token":"[^"]*"' \ + | sed 's/"access_token":"//;s/"$//' || true) + + if [[ -z "$ACCESS_TOKEN" ]]; then + echo "ERROR: Failed to obtain access token." + echo "Token response: $TOKEN_RESPONSE" + exit 2 + fi +} + +# --- Resolve internal CMIS repo ID from externalId --- +# The repo's internal ID is needed for browser-binding endpoints (browser/{repoId}). +resolve_repo_id() { + if [[ -z "$EXTERNAL_ID" ]]; then + echo "ERROR: --externalId is required" + exit 2 + fi + + local LIST_RESPONSE LIST_HTTP_CODE LIST_BODY + LIST_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X GET "${CMIS_URL}rest/v2/repositories" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -H "Content-Type: application/json") + LIST_HTTP_CODE=$(echo "$LIST_RESPONSE" | tail -n1) + LIST_BODY=$(echo "$LIST_RESPONSE" | sed '$d') + + if [[ "$LIST_HTTP_CODE" != "200" ]]; then + echo "ERROR: Failed to list repositories (HTTP ${LIST_HTTP_CODE})." + echo "$LIST_BODY" + exit 2 + fi + + REPO_ID=$(echo "$LIST_BODY" | python3 -c " +import sys, json +data = json.load(sys.stdin) +repos = data.get('repoAndConnectionInfos', data.get('repositories', [])) +for r in repos: + repo = r.get('repository', r) + if repo.get('externalId') == '${EXTERNAL_ID}': + print(repo.get('id', '')) + break +" 2>/dev/null || true) + + if [[ -z "$REPO_ID" ]]; then + echo "NOT_FOUND: No repository with externalId '${EXTERNAL_ID}'." + exit 1 + fi +} + +# =========================================================================== +# ACTION: register-type — POST a CMIS type definition to the browser binding +# =========================================================================== +# CMIS browser binding accepts createType via: +# POST {CMIS_URL}browser/{repoId} +# form fields: cmisaction=createType, type= +# =========================================================================== +action_register_type() { + if [[ -z "$TYPE_FILE" ]]; then + echo "ERROR: --typeFile is required for register-type" + exit 2 + fi + if [[ ! -f "$TYPE_FILE" ]]; then + echo "ERROR: Type file not found: $TYPE_FILE" + exit 2 + fi + + get_token + resolve_repo_id + echo "Registering CMIS secondary type from '${TYPE_FILE}' in repository '${EXTERNAL_ID}' (id: ${REPO_ID})..." + + # Read type JSON; the CMIS browser binding accepts the type definition as + # the value of the 'type' form field. Whitespace inside is fine. + local TYPE_JSON + TYPE_JSON=$(cat "$TYPE_FILE") + + # Extract typeId for logging / idempotency check. + local INCOMING_TYPE_ID + INCOMING_TYPE_ID=$(echo "$TYPE_JSON" | python3 -c " +import sys, json +print(json.load(sys.stdin).get('id', '')) +" 2>/dev/null || true) + echo "Type id from file: ${INCOMING_TYPE_ID:-}" + + local CREATE_RESPONSE CREATE_HTTP_CODE CREATE_BODY + CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X POST "${CMIS_URL}browser/${REPO_ID}" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -F "cmisaction=createType" \ + -F "type=${TYPE_JSON}") + CREATE_HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1) + CREATE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d') + + case "$CREATE_HTTP_CODE" in + 200|201) + echo "SUCCESS: Type '${INCOMING_TYPE_ID}' registered (HTTP ${CREATE_HTTP_CODE})." + exit 0 + ;; + 409|422|400) + # Idempotency: SDM may return one of these when the type already exists. + # Treat as success only if the response body indicates a duplicate / already-exists. + if echo "$CREATE_BODY" | grep -qiE 'already exist|duplicate|exists already|conflict'; then + echo "ALREADY_EXISTS: Type '${INCOMING_TYPE_ID}' already registered (HTTP ${CREATE_HTTP_CODE}). Treating as success." + exit 0 + fi + echo "ERROR: Failed to register type '${INCOMING_TYPE_ID}' (HTTP ${CREATE_HTTP_CODE})." + echo "$CREATE_BODY" + exit 1 + ;; + *) + echo "ERROR: Failed to register type '${INCOMING_TYPE_ID}' (HTTP ${CREATE_HTTP_CODE})." + echo "$CREATE_BODY" + exit 2 + ;; + esac +} + +# =========================================================================== +# ACTION: get-type — verify a CMIS type definition is registered +# =========================================================================== +# CMIS browser binding lookup: +# GET {CMIS_URL}browser/{repoId}?cmisselector=typeDefinition&typeId= +# =========================================================================== +action_get_type() { + if [[ -z "$TYPE_ID" ]]; then + echo "ERROR: --typeId is required for get-type" + exit 2 + fi + + get_token + resolve_repo_id + echo "Fetching type definition for '${TYPE_ID}' in repository '${EXTERNAL_ID}' (id: ${REPO_ID})..." + + local GET_RESPONSE GET_HTTP_CODE GET_BODY + GET_RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X GET "${CMIS_URL}browser/${REPO_ID}" \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -G \ + --data-urlencode "cmisselector=typeDefinition" \ + --data-urlencode "typeId=${TYPE_ID}") + GET_HTTP_CODE=$(echo "$GET_RESPONSE" | tail -n1) + GET_BODY=$(echo "$GET_RESPONSE" | sed '$d') + + if [[ "$GET_HTTP_CODE" == "200" ]] && echo "$GET_BODY" | grep -q "\"id\":\"${TYPE_ID}\""; then + echo "FOUND: Type '${TYPE_ID}' is registered (HTTP 200)." + exit 0 + fi + + if [[ "$GET_HTTP_CODE" == "404" ]] || [[ "$GET_HTTP_CODE" == "200" ]]; then + echo "NOT_FOUND: Type '${TYPE_ID}' is not registered (HTTP ${GET_HTTP_CODE})." + echo "$GET_BODY" + exit 1 + fi + + echo "ERROR: Unexpected HTTP ${GET_HTTP_CODE} when querying type '${TYPE_ID}'." + echo "$GET_BODY" + exit 2 +} + +# --- Dispatch --- +case "$ACTION" in + register-type) action_register_type ;; + get-type) action_get_type ;; + *) + echo "Unknown action: $ACTION" + echo "Usage: $0 {register-type|get-type} [options]" + exit 2 + ;; +esac diff --git a/sdm/src/test/resources/secondary-types/abc-bo-type.json b/sdm/src/test/resources/secondary-types/abc-bo-type.json new file mode 100644 index 00000000..e54a56f4 --- /dev/null +++ b/sdm/src/test/resources/secondary-types/abc-bo-type.json @@ -0,0 +1,58 @@ +{ + "id": "abc:bo", + "localName": "109871.1", + "localNamespace": "http://opentext.com", + "displayName": "abc:bo", + "queryName": "abc:bo", + "description": "abc:bo", + "baseId": "cmis:secondary", + "parentId": "cmis:secondary", + "creatable": false, + "fileable": false, + "queryable": true, + "fulltextIndexed": false, + "includedInSupertypeQuery": true, + "controllablePolicy": false, + "controllableACL": false, + "typeMutability": { + "create": true, + "update": true, + "delete": true + }, + "propertyDefinitions": { + "abc:myId1": { + "defaultValue": [], + "id": "abc:myId1", + "localName": "abc:myId1", + "localNamespace": "http://apache.org", + "displayName": "abc:myId1", + "queryName": "abc:myId1", + "description": "Hold Identifiers", + "propertyType": "string", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": false, + "openChoice": false + }, + "abc:myId2": { + "defaultValue": [], + "id": "abc:myId2", + "localName": "abc:myId2", + "localNamespace": "http://apache.org", + "displayName": "abc:myId2", + "queryName": "abc:myId2", + "description": "Hold Identifiers", + "propertyType": "string", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": false, + "openChoice": false + } + } + } diff --git a/sdm/src/test/resources/secondary-types/documentinfo-type.json b/sdm/src/test/resources/secondary-types/documentinfo-type.json new file mode 100644 index 00000000..661b6e88 --- /dev/null +++ b/sdm/src/test/resources/secondary-types/documentinfo-type.json @@ -0,0 +1,124 @@ +{ + "id": "Working:DocumentInfo", + "localName": "Document Info", + "localNamespace": "com.sap", + "displayName": "Document Info", + "queryName": "Working:DocumentInfo", + "description": "Document Info", + "baseId": "cmis:secondary", + "parentId": "cmis:secondary", + "creatable": false, + "fileable": false, + "queryable": true, + "fulltextIndexed": false, + "includedInSupertypeQuery": false, + "controllablePolicy": false, + "controllableACL": false, + "typeMutability": { + "create": false, + "update": false, + "delete": false + }, + "propertyDefinitions": { + "Working:DocumentInfoRecordBoolean": { + "maxLength": 35, + "id": "Working:DocumentInfoRecordBoolean", + "localName": "Another Document Info Record Boolean", + "localNamespace": "com.sap", + "displayName": "Another Document Info Record Boolean", + "queryName": "Working:DocumentInfoRecordBoolean", + "description": "Another Document Info Record Boolean", + "propertyType": "boolean", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": true, + "openChoice": true, + "mcm:miscellaneous": { + "isQueryableInUI": "true", + "isPartOfTable": "true", + "positionWeight": "10" + }, + "value": 12345 + }, + "Working:DocumentInfoRecordDate": { + "maxLength": 35, + "id": "Working:DocumentInfoRecordDate", + "localName": "Another Document Info Record Date", + "localNamespace": "com.sap", + "displayName": "Another Document Info Record Date", + "queryName": "Working:DocumentInfoRecordDate", + "description": "Another Document Info Record Date", + "propertyType": "datetime", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": true, + "openChoice": true, + "mcm:miscellaneous": { + "isQueryableInUI": "true", + "isPartOfTable": "true", + "positionWeight": "10" + }, + "value": 12345 + }, + "Working:DocumentInfoRecordString": { + "maxLength": 35, + "id": "Working:DocumentInfoRecordString", + "localName": "Another Document Info Record String", + "localNamespace": "com.sap", + "displayName": "Another Document Info Record String", + "queryName": "Working:DocumentInfoRecordString", + "description": "Another Document Info Record String", + "propertyType": "string", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": true, + "openChoice": true, + "mcm:miscellaneous": { + "isQueryableInUI": "true", + "isPartOfTable": "true", + "positionWeight": "10" + }, + "value": 12345 + }, + "Working:DocumentInfoRecordInt": { + "maxLength": 35, + "id": "Working:DocumentInfoRecordInt", + "localName": "Another Document Info Record Int", + "localNamespace": "com.sap", + "displayName": "Another Document Info Record Int", + "queryName": "Working:DocumentInfoRecordInt", + "description": "Another Document Info Record Int", + "propertyType": "integer", + "cardinality": "single", + "updatability": "readwrite", + "inherited": false, + "required": false, + "queryable": true, + "orderable": true, + "openChoice": true, + "mcm:miscellaneous": { + "isQueryableInUI": "true", + "isPartOfTable": "true", + "positionWeight": "10" + }, + "value": 12345 + } + }, + "mcm:propertyGrouping": { + "Document Additional Data": { + "mcm:title": { + "default": "Document Additional Data" + }, + "mcm:propertyIds": "Working:DocumentInfoRecordDate,Working:DocumentInfoRecordBoolean,Working:Status,Working:Class" + } + } +}