diff --git a/.github/workflows/check-jreleaser.yml b/.github/workflows/check-jreleaser.yml index 708610ec..9730c3db 100644 --- a/.github/workflows/check-jreleaser.yml +++ b/.github/workflows/check-jreleaser.yml @@ -19,4 +19,3 @@ jobs: JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }} - \ No newline at end of file diff --git a/.github/workflows/jreleaser-common.yml b/.github/workflows/jreleaser-common.yml index f3b8030a..9b49d2b7 100644 --- a/.github/workflows/jreleaser-common.yml +++ b/.github/workflows/jreleaser-common.yml @@ -31,19 +31,19 @@ on: jobs: jreleaser: runs-on: ubuntu-latest - + steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for JReleaser - + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - + - name: Cache Gradle dependencies uses: actions/cache@v4 with: @@ -53,25 +53,25 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - + - name: Install JReleaser CLI (manual setup) run: | # Try to install a working version manually JRELEASER_VERSION="1.18.0" mkdir -p ~/.jreleaser/bin - + # Download the tar.gz version which usually contains all dependencies curl -sL "https://github.com/jreleaser/jreleaser/releases/download/v${JRELEASER_VERSION}/jreleaser-tool-provider-${JRELEASER_VERSION}.jar" -o ~/.jreleaser/bin/jreleaser.jar - + # Create a wrapper script cat > ~/.jreleaser/bin/jreleaser << 'EOF' #!/bin/bash java -jar ~/.jreleaser/bin/jreleaser.jar "$@" EOF chmod +x ~/.jreleaser/bin/jreleaser - + echo "$HOME/.jreleaser/bin" >> $GITHUB_PATH - + - name: Print JReleaser version and check Java run: | echo "Java version:" @@ -106,20 +106,20 @@ jobs: exit 1 ;; esac - + # Validate that version is not empty if [ -z "$VERSION" ]; then echo "❌ Error: Could not determine version!" exit 1 fi - + echo "PROJECT_VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT echo "✅ Final version: $VERSION" - + - name: Make publish script executable run: chmod +x scripts/publish-maven-central.sh - + - name: Run JReleaser env: DEPLOY: ${{ inputs.deploy_enabled }} @@ -137,11 +137,11 @@ jobs: echo "🔍 Checking JReleaser configuration for version $PROJECT_VERSION..." fi ./scripts/publish-maven-central.sh - + - name: Upload JReleaser logs if: always() uses: actions/upload-artifact@v4 with: name: jreleaser-logs-${{ github.run_id }} path: out/jreleaser/ - retention-days: 5 \ No newline at end of file + retention-days: 5 \ No newline at end of file diff --git a/.github/workflows/local-test.yml b/.github/workflows/local-test.yml index df59f572..222b9964 100644 --- a/.github/workflows/local-test.yml +++ b/.github/workflows/local-test.yml @@ -16,19 +16,18 @@ jobs: test-gradle: name: gradle testing runs-on: ubuntu-latest - + steps: - name: Checkout adapters-java uses: actions/checkout@v4 - + - name: Setup java uses: actions/setup-java@v4 with: distribution: ${{ env.JAVA_DISTRIBUTION }} java-package: jdk java-version: ${{ env.JAVA_VERSION }} - + - name: Run tests run: | ./gradlew test - \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e3dfff4..a10108fd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ on: workflow_dispatch: inputs: run_tests: - description: 'Запустить тесты' + description: "Запустить тесты" required: true type: boolean default: true @@ -99,7 +99,7 @@ jobs: - project_name: cucumber6-gradle-testng configuration_id: CUCUMBER6_GRADLE_TESTNG_CONFIGURATION_ID project_id: CUCUMBER6_GRADLE_TESTNG_PROJECT_ID - + - project_name: cucumber5-gradle-junit4 configuration_id: CUCUMBER5_GRADLE_JUNIT4_CONFIGURATION_ID project_id: CUCUMBER5_GRADLE_JUNIT4_PROJECT_ID @@ -116,7 +116,7 @@ jobs: - project_name: cucumber4-gradle-testng configuration_id: CUCUMBER4_GRADLE_TESTNG_CONFIGURATION_ID project_id: CUCUMBER4_GRADLE_TESTNG_PROJECT_ID - + - project_name: selenide-gradle-junit5 configuration_id: SELENIDE_GRADLE_JUNIT5_CONFIGURATION_ID project_id: SELENIDE_GRADLE_JUNIT5_PROJECT_ID @@ -163,79 +163,41 @@ jobs: ./gradlew --no-daemon -DdisableSign=true publishToMavenLocal dotnet build --configuration Debug --property WarningLevel=0 api-validator-dotnet pip install testit-cli + + - name: Create TestRun run: | testit testrun create --token ${{ env.TMS_PRIVATE_TOKEN }} --output ${{ env.TEMP_FILE }} echo "TMS_TEST_RUN_ID=$(<${{ env.TEMP_FILE }})" >> $GITHUB_ENV - name: Test run: | + export SYNC_STORAGE_VERSION=$(grep -o 'SYNC_STORAGE_VERSION = "[^"]*"' testit-java-commons/src/main/java/ru/testit/syncstorage/SyncStorageRunner.java | cut -d'"' -f2) + # current location - /adapters-java/ + cd java-examples/${{ matrix.project_name }} + + mkdir -p .caches + wget -O .caches/syncstorage-linux-amd64 \ + "https://github.com/testit-tms/sync-storage-public/releases/download/${SYNC_STORAGE_VERSION}/syncstorage-${SYNC_STORAGE_VERSION}-linux_amd64" + chmod +x .caches/syncstorage-linux-amd64 + nohup .caches/syncstorage-linux-amd64 --testRunId ${{ env.TMS_TEST_RUN_ID }} --port 49152 \ + --baseURL ${{ env.TMS_URL }} --privateToken ${{ env.TMS_PRIVATE_TOKEN }} > service.log 2>&1 & + + sleep 1 + + curl -v http://127.0.0.1:49152/health || true + chmod +x ./gradlew - ./gradlew test --no-daemon -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} -DtmsProjectId=${{ env.TMS_PROJECT_ID }} -DtmsConfigurationId=${{ env.TMS_CONFIGURATION_ID }} -DtmsAdapterMode=${{ env.TMS_ADAPTER_MODE }} -DtmsTestRunId=${{ env.TMS_TEST_RUN_ID }} -DtmsCertValidation=${{ env.TMS_CERT_VALIDATION }} || exit 0 + ./gradlew test --no-daemon -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} \ + -DtmsProjectId=${{ env.TMS_PROJECT_ID }} -DtmsConfigurationId=${{ env.TMS_CONFIGURATION_ID }} \ + -DtmsAdapterMode=${{ env.TMS_ADAPTER_MODE }} -DtmsTestRunId=${{ env.TMS_TEST_RUN_ID }} \ + -DtmsCertValidation=${{ env.TMS_CERT_VALIDATION }} || true # ignore error code + + sleep 1 + cat service.log + + curl -v http://127.0.0.1:49152/wait-completion?testRunId=${{ env.TMS_TEST_RUN_ID }} || true + # cat /home/runner/work/adapters-java/adapters-java/java-examples/gradle-junit5/build/.caches/shutdown.log - name: Validate run: | dotnet test --configuration Debug --no-build --logger:"console;verbosity=detailed" api-validator-dotnet - - # test-maven: - # needs: check-label - # if: ${{ needs.check-label.outputs.should_run == 'true' || github.event_name == 'workflow_dispatch' }} - # name: ${{ matrix.project_name }} - # runs-on: ubuntu-latest - # strategy: - # fail-fast: false - # matrix: - # include: - # - project_name: maven-junit4 - # configuration_id: MAVEN_JUNIT4_CONFIGURATION_ID - # project_id: MAVEN_JUNIT4_PROJECT_ID - # - project_name: maven-junit5 - # configuration_id: MAVEN_JUNIT5_CONFIGURATION_ID - # project_id: MAVEN_JUNIT5_PROJECT_ID - # env: - # TMS_CONFIGURATION_ID: ${{ secrets[matrix.configuration_id] }} - # TMS_PROJECT_ID: ${{ secrets[matrix.project_id] }} - # TMS_TEST_RUN_NAME: ${{ matrix.project_name }} TestRun - # steps: - # - name: Checkout adapters-java - # uses: actions/checkout@v4 - # - name: Checkout api-validator-dotnet - # uses: actions/checkout@v4 - # with: - # repository: testit-tms/api-validator-dotnet - # token: ${{ env.GITHUB_PAT }} - # path: api-validator-dotnet - # - name: Checkout java-examples - # uses: actions/checkout@v4 - # with: - # repository: testit-tms/java-examples - # path: java-examples - # - name: Setup dotnet - # uses: actions/setup-dotnet@v4 - # with: - # dotnet-version: ${{ env.DOTNET_VERSION }} - # - name: Setup java - # uses: actions/setup-java@v4 - # with: - # distribution: ${{ env.JAVA_DISTRIBUTION }} - # java-package: jdk - # java-version: ${{ env.JAVA_VERSION }} - # - name: Setup python - # uses: actions/setup-python@v5 - # with: - # python-version: ${{ env.PYTHON_VERSION }} - # - name: Setup environment - # run: | - # ./gradlew --no-daemon -DdisableSign=true publishToMavenLocal - # dotnet build --configuration Debug --property WarningLevel=0 api-validator-dotnet - # pip install testit-cli - # - name: Create TestRun - # run: | - # testit testrun create --token ${{ env.TMS_PRIVATE_TOKEN }} --output ${{ env.TEMP_FILE }} - # echo "TMS_TEST_RUN_ID=$(<${{ env.TEMP_FILE }})" >> $GITHUB_ENV - # - name: Test - # run: | - # cd java-examples/${{ matrix.project_name }} - # mvn test -DtmsUrl=${{ env.TMS_URL }} -DtmsPrivateToken=${{ env.TMS_PRIVATE_TOKEN }} -DtmsProjectId=${{ env.TMS_PROJECT_ID }} -DtmsConfigurationId=${{ env.TMS_CONFIGURATION_ID }} -DtmsAdapterMode=${{ env.TMS_ADAPTER_MODE }} -DtmsTestRunId=${{ env.TMS_TEST_RUN_ID }} -DtmsCertValidation=${{ env.TMS_CERT_VALIDATION }} || exit 0 - # - name: Validate - # run: | - # dotnet test --configuration Debug --no-build --logger:"console;verbosity=detailed" api-validator-dotnet diff --git a/build.gradle.kts b/build.gradle.kts index 813363ec..9eb6c2e6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -97,6 +97,7 @@ configure(subprojects) { options.encoding = "utf-8" options.setIncremental(true) options.isFork = true + //options.compilerArgs.add("-Xlint:-comment") // not supported in java 8 } tasks.jar { diff --git a/gradle.properties b/gradle.properties index e49b5924..636292d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=2.10.2 +version=3.0.0 org.gradle.daemon=true org.gradle.parallel=true diff --git a/testit-adapter-cucumber4/Readme.md b/testit-adapter-cucumber4/Readme.md index c631f3b7..45033c71 100644 --- a/testit-adapter-cucumber4/Readme.md +++ b/testit-adapter-cucumber4/Readme.md @@ -223,7 +223,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-cucumber5/Readme.md b/testit-adapter-cucumber5/Readme.md index 99bf01ed..28062eb4 100644 --- a/testit-adapter-cucumber5/Readme.md +++ b/testit-adapter-cucumber5/Readme.md @@ -341,7 +341,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-cucumber6/Readme.md b/testit-adapter-cucumber6/Readme.md index f6b55521..6903cf8e 100644 --- a/testit-adapter-cucumber6/Readme.md +++ b/testit-adapter-cucumber6/Readme.md @@ -335,7 +335,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-cucumber7/Readme.md b/testit-adapter-cucumber7/Readme.md index 683fccdf..16e85357 100644 --- a/testit-adapter-cucumber7/Readme.md +++ b/testit-adapter-cucumber7/Readme.md @@ -346,7 +346,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-jbehave/Readme.md b/testit-adapter-jbehave/Readme.md index 885c8308..4e967c7b 100644 --- a/testit-adapter-jbehave/Readme.md +++ b/testit-adapter-jbehave/Readme.md @@ -274,7 +274,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-junit4/Readme.md b/testit-adapter-junit4/Readme.md index 9ac4582f..dcb28b45 100644 --- a/testit-adapter-junit4/Readme.md +++ b/testit-adapter-junit4/Readme.md @@ -187,7 +187,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-junit5/Readme.md b/testit-adapter-junit5/Readme.md index 3334f5c3..457cee53 100644 --- a/testit-adapter-junit5/Readme.md +++ b/testit-adapter-junit5/Readme.md @@ -202,7 +202,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-junit5/src/main/java/ru/testit/listener/BaseJunit5Listener.java b/testit-adapter-junit5/src/main/java/ru/testit/listener/BaseJunit5Listener.java index 7bb5031a..4e3d88f8 100644 --- a/testit-adapter-junit5/src/main/java/ru/testit/listener/BaseJunit5Listener.java +++ b/testit-adapter-junit5/src/main/java/ru/testit/listener/BaseJunit5Listener.java @@ -180,17 +180,22 @@ private Map getParameters( Map testParameters = new HashMap<>(); for (int i = 0; i < parameters.length; i++) { - final Parameter parameter = parameters[i]; - final Class parameterType = parameter.getType(); + try { + final Parameter parameter = parameters[i]; + final Class parameterType = parameter.getType(); - if (parameterType.getCanonicalName().startsWith("org.junit.jupiter.api")) { - continue; - } + if (parameterType.getCanonicalName().startsWith("org.junit.jupiter.api")) { + continue; + } - String name = parameter.getName(); - String value = invocationContext.getArguments().get(i).toString(); + String name = parameter.getName(); + String value = invocationContext.getArguments().get(i).toString(); - testParameters.put(name, value); + testParameters.put(name, value); + } + catch (NullPointerException e) { + LOGGER.warn("Exception on parsing junit test parameter: {}", e.getMessage()); + } } return testParameters; diff --git a/testit-adapter-junit5/src/test/java/ru/testit/samples/failed/FullBeforeAfterTests.java b/testit-adapter-junit5/src/test/java/ru/testit/samples/failed/FullBeforeAfterTests.java index 38786dd8..3fc2648a 100644 --- a/testit-adapter-junit5/src/test/java/ru/testit/samples/failed/FullBeforeAfterTests.java +++ b/testit-adapter-junit5/src/test/java/ru/testit/samples/failed/FullBeforeAfterTests.java @@ -105,9 +105,7 @@ void allAnnotationsTest() { @Test @ExternalId("failed_full_before_after_with_required_annotations") @DisplayName("Failed test with required annotations") - void requiredAnnotationsTest() { - Assertions.fail(); - } + void requiredAnnotationsTest() { Assertions.fail(); } @AfterEach @Title("Log out the system") diff --git a/testit-adapter-selenide/Readme.md b/testit-adapter-selenide/Readme.md index 3a58d117..e89a2ed7 100644 --- a/testit-adapter-selenide/Readme.md +++ b/testit-adapter-selenide/Readme.md @@ -208,7 +208,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-adapter-testng/Readme.md b/testit-adapter-testng/Readme.md index 0a3ea64c..ac5c38cc 100644 --- a/testit-adapter-testng/Readme.md +++ b/testit-adapter-testng/Readme.md @@ -177,7 +177,7 @@ test { | It enables/disables TMS integration (**It's optional**). Default value - true | testIt | TMS_TEST_IT | tmsTestIt | | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)
false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES | tmsAutomaticCreationTestCases | | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will update links to test cases
false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES | tmsAutomaticUpdationLinksToTestCases | -| Mode of import type selection when launching autotests (**It's optional**). Default value - true. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | +| Mode of import type selection when launching autotests (**It's optional**). Default value - false. The adapter supports following modes:
true - in this mode, the adapter will create/update each autotest in real time
false - in this mode, the adapter will create/update multiple autotests | importRealtime | TMS_IMPORT_REALTIME | tmsImportRealtime | | Name of the configuration file If it is not provided, it is used default file name (**It's optional**) | - | TMS_CONFIG_FILE | tmsConfigFile | #### File diff --git a/testit-java-commons/build.gradle.kts b/testit-java-commons/build.gradle.kts index 7c9e5ddc..cf1f832f 100644 --- a/testit-java-commons/build.gradle.kts +++ b/testit-java-commons/build.gradle.kts @@ -12,6 +12,8 @@ val commonsLang3Version = "3.18.0" val jakartaWsRsVersion = "3.0.0" val junitJupiterVersion = "5.8.2" val mockitoInlineVersion = "4.4.0" +val jacksonDatabindNullable = "0.2.9" +val jacksonDatatypeJsr310 = "2.15.0" dependencies { implementation("org.aspectj:aspectjrt:$aspectjrtVersion") @@ -22,10 +24,14 @@ dependencies { implementation("org.slf4j:slf4j-simple:$slf4jVersion") implementation("ru.testit:testit-api-client:$apiClientVersion") implementation("jakarta.ws.rs:jakarta.ws.rs-api:$jakartaWsRsVersion") + // Source: https://mvnrepository.com/artifact/org.openapitools/jackson-databind-nullable + implementation("org.openapitools:jackson-databind-nullable:$jacksonDatabindNullable") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonDatatypeJsr310") testImplementation("org.junit.jupiter:junit-jupiter:$junitJupiterVersion") testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") testImplementation("org.mockito:mockito-inline:$mockitoInlineVersion") + } tasks.test { diff --git a/testit-java-commons/src/main/java/ru/testit/clients/ClientConfiguration.java b/testit-java-commons/src/main/java/ru/testit/clients/ClientConfiguration.java index 4279ae46..e43fa805 100644 --- a/testit-java-commons/src/main/java/ru/testit/clients/ClientConfiguration.java +++ b/testit-java-commons/src/main/java/ru/testit/clients/ClientConfiguration.java @@ -43,7 +43,8 @@ public ClientConfiguration(Properties properties) { String importRealtime = String.valueOf(properties.get(AppProperties.TMS_IMPORT_REALTIME)); this.tmsImportRealtime = Objects.equals(importRealtime, "true"); } catch (NullPointerException ignored) { - this.tmsImportRealtime = true; + // false by default + this.tmsImportRealtime = false; } this.certValidation = Boolean.parseBoolean(validationCert); diff --git a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java index 18a9be52..a8d4ed02 100644 --- a/testit-java-commons/src/main/java/ru/testit/clients/Converter.java +++ b/testit-java-commons/src/main/java/ru/testit/clients/Converter.java @@ -95,6 +95,7 @@ public static AutoTestResultsForTestRunModel testResultToAutoTestResultsForTestR model.attachments(convertAttachments(result.getAttachments())); model.setMessage(result.getMessage()); model.setParameters(result.getParameters()); + model.setOutcome(AvailableTestResultOutcome.fromValue(result.getItemStatus().value())); Throwable throwable = result.getThrowable(); if (throwable != null) { @@ -278,16 +279,6 @@ private static List convertResultSte }).collect(Collectors.toList()); } - private static List labelsConvert(List labels) { - return labels.stream().map(label -> { - LabelApiModel model = new LabelApiModel(); - - model.setName(label.getName()); - - return model; - }).collect(Collectors.toList()); - } - private static List labelsConvertFromApi(List labels) { return labels.stream().map(label -> { LabelApiModel model = new LabelApiModel(); @@ -336,32 +327,6 @@ private static List convertAttachmentsFromModel(List convertAutoTestStepApiResultsToModels(List steps) { if (steps == null) { return new ArrayList<>(); @@ -487,23 +452,6 @@ public static TestResultsFilterApiModel buildTestResultsFilterApiModelWithInProg return model; } - public static AutoTestSearchApiModel buildAutoTestSearchApiModel(Set globalIds) { - AutoTestFilterApiModel filter = new AutoTestFilterApiModel(); - - filter.setGlobalIds(globalIds); - - AutoTestSearchIncludeApiModel includes = new AutoTestSearchIncludeApiModel(); - includes.setIncludeLabels(false); - includes.setIncludeSteps(false); - includes.setIncludeLinks(false); - - AutoTestSearchApiModel model = new AutoTestSearchApiModel(); - model.setFilter(filter); - model.setIncludes(includes); - - return model; - } - public static UpdateEmptyTestRunApiModel buildUpdateEmptyTestRunApiModel(TestRunV2ApiResult testRun) { UpdateEmptyTestRunApiModel model = new UpdateEmptyTestRunApiModel(); @@ -571,77 +519,4 @@ private static List listOf(T... elements) { return Arrays.asList(elements); } - public static Map> getRelationWorkItemIdsToAutotestIdsByExternalIds( - Map> relationWorkItemIdsToAutotestExternalIdsBeingCreated, - List AutoTestApiResults) { - Map> relationWorkItemIdsToAutotestIds = new HashMap<>(); - Set externalIds = relationWorkItemIdsToAutotestExternalIdsBeingCreated.keySet(); - - for (String externalId : externalIds) { - AutoTestApiResults.stream() - .filter(m -> Objects.equals(m.getExternalId(), externalId)) - .findFirst().ifPresent(autotest -> relationWorkItemIdsToAutotestIds.put( - autotest.getId().toString(), - relationWorkItemIdsToAutotestExternalIdsBeingCreated.get(externalId))); - - } - - return relationWorkItemIdsToAutotestIds; - } - - private static List convertAutoTestStepApiResultsToSteps(List steps) { - if (steps == null) { - return new ArrayList<>(); - } - - return steps.stream().map(step -> { - AutoTestStepModel model = new AutoTestStepModel(); - model.setTitle(step.getTitle()); - model.setDescription(step.getDescription()); - - if (step.getSteps() != null) { - model.setSteps(convertAutoTestStepApiResultsToSteps(step.getSteps())); - } - - return model; - }).collect(Collectors.toList()); - } - - public static List convertLinkApiResultsToPutLinks(List links) { - if (links == null) { - return new ArrayList<>(); - } - - return links.stream().map( - link -> { - LinkPutModel model = new LinkPutModel(); - - model.setTitle(link.getTitle()); - model.setDescription(link.getDescription()); - model.setUrl(link.getUrl()); - model.setHasInfo(false); - - if (link.getType() != null) { - model.setType(LinkType.fromValue(link.getType().getValue())); - } - - return model; - } - ).collect(Collectors.toList()); - } - - private static List convertLabelApiResultsToLabelShortModels(List labels) { - if (labels == null) { - return new ArrayList<>(); - } - - return labels.stream().map(label -> { - LabelShortModel model = new LabelShortModel(); - - model.setName(label.getName()); - - return model; - }).collect(Collectors.toList()); - } - } diff --git a/testit-java-commons/src/main/java/ru/testit/properties/AdapterConfig.java b/testit-java-commons/src/main/java/ru/testit/properties/AdapterConfig.java index c7a2c54f..3c6eb275 100644 --- a/testit-java-commons/src/main/java/ru/testit/properties/AdapterConfig.java +++ b/testit-java-commons/src/main/java/ru/testit/properties/AdapterConfig.java @@ -10,6 +10,8 @@ public class AdapterConfig implements Serializable { private AdapterMode mode; private boolean automaticCreationTestCases; private boolean tmsIntegration; + private String syncStoragePath; + private String syncStoragePort; public AdapterConfig(Properties properties) { @@ -33,6 +35,24 @@ public AdapterConfig(Properties properties) { } catch (NullPointerException ignored) { this.tmsIntegration = true; } + + try { + this.syncStoragePath = String.valueOf(properties.get(AppProperties.SYNC_STORAGE_PATH)); + if ("null".equals(this.syncStoragePath)) { + this.syncStoragePath = null; + } + } catch (NullPointerException ignored) { + this.syncStoragePath = null; + } + + try { + this.syncStoragePort = String.valueOf(properties.get(AppProperties.SYNC_STORAGE_PORT)); + if ("null".equals(this.syncStoragePort)) { + this.syncStoragePort = "8080"; // Порт по умолчанию + } + } catch (NullPointerException ignored) { + this.syncStoragePort = "8080"; // Порт по умолчанию + } } public AdapterMode getMode() { @@ -54,9 +74,18 @@ public String toString() { sb.append(" mode: ").append(Utils.toIndentedString(this.mode)).append("\n"); sb.append(" automaticCreationTestCases: ").append(Utils.toIndentedString(this.automaticCreationTestCases)).append("\n"); sb.append(" tmsIntegration: ").append(Utils.toIndentedString(this.tmsIntegration)).append("\n"); + sb.append(" syncStoragePath: ").append(Utils.toIndentedString(this.syncStoragePath)).append("\n"); + sb.append(" syncStoragePort: ").append(Utils.toIndentedString(this.syncStoragePort)).append("\n"); sb.append("}"); return sb.toString(); } -} + public String getSyncStoragePath() { + return syncStoragePath; + } + + public String getSyncStoragePort() { + return syncStoragePort; + } +} diff --git a/testit-java-commons/src/main/java/ru/testit/properties/AppProperties.java b/testit-java-commons/src/main/java/ru/testit/properties/AppProperties.java index e909e4aa..14f496bf 100644 --- a/testit-java-commons/src/main/java/ru/testit/properties/AppProperties.java +++ b/testit-java-commons/src/main/java/ru/testit/properties/AppProperties.java @@ -24,6 +24,10 @@ public class AppProperties { public static final String TMS_INTEGRATION = "testIt"; public static final String TMS_IMPORT_REALTIME = "importRealtime"; + // SyncStorage properties + public static final String SYNC_STORAGE_PATH = "syncStoragePath"; + public static final String SYNC_STORAGE_PORT = "syncStoragePort"; + private static final String PROPERTIES_FILE = "testit.properties"; private static final Logger log = LoggerFactory.getLogger(AppProperties.class); private static final HashMap> envVarsNames = new HashMap>() {{ @@ -40,6 +44,8 @@ public class AppProperties { put(CERT_VALIDATION, "TMS_CERT_VALIDATION"); put(TMS_INTEGRATION, "TMS_TEST_IT"); put(TMS_IMPORT_REALTIME, "TMS_IMPORT_REALTIME"); + put(SYNC_STORAGE_PATH, "SYNC_STORAGE_PATH"); + put(SYNC_STORAGE_PORT, "SYNC_STORAGE_PORT"); }}); put("cli", new HashMap() {{ put(URL, "tmsUrl"); @@ -54,6 +60,8 @@ public class AppProperties { put(CERT_VALIDATION, "tmsCertValidation"); put(TMS_INTEGRATION, "tmsTestIt"); put(TMS_IMPORT_REALTIME, "tmsImportRealtime"); + put(SYNC_STORAGE_PATH, "syncStoragePath"); + put(SYNC_STORAGE_PORT, "syncStoragePort"); }}); }}; @@ -224,6 +232,24 @@ private static Map loadPropertiesFromEnv(Properties properties, // empty } + try { + String syncStoragePath = properties.getProperty(varNames.get(SYNC_STORAGE_PATH), null); + if (syncStoragePath != null && !syncStoragePath.isEmpty() && !syncStoragePath.equals("null")) { + result.put(SYNC_STORAGE_PATH, syncStoragePath); + } + } catch (SecurityException | NullPointerException | IllegalArgumentException ignored) { + // empty + } + + try { + String syncStoragePort = properties.getProperty(varNames.get(SYNC_STORAGE_PORT), null); + if (syncStoragePort != null && !syncStoragePort.isEmpty() && !syncStoragePort.equals("null")) { + result.put(SYNC_STORAGE_PORT, syncStoragePort); + } + } catch (SecurityException | NullPointerException | IllegalArgumentException ignored) { + // empty + } + return result; } diff --git a/testit-java-commons/src/main/java/ru/testit/services/AdapterManager.java b/testit-java-commons/src/main/java/ru/testit/services/AdapterManager.java index c0df8854..81ff3be4 100644 --- a/testit-java-commons/src/main/java/ru/testit/services/AdapterManager.java +++ b/testit-java-commons/src/main/java/ru/testit/services/AdapterManager.java @@ -1,14 +1,27 @@ package ru.testit.services; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.util.StdDateFormat; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; + +import org.openapitools.jackson.nullable.JsonNullableModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.testit.client.invoker.ApiException; +import ru.testit.client.model.AutoTestResultsForTestRunModel; import ru.testit.client.model.TestRunV2ApiResult; -import ru.testit.client.model.TestStatusApiResult; -import ru.testit.client.model.TestStatusApiType; +import ru.testit.clients.ClientConfiguration; import ru.testit.clients.Converter; import ru.testit.clients.ITmsApiClient; -import ru.testit.clients.ClientConfiguration; import ru.testit.clients.TmsApiClient; import ru.testit.listener.AdapterListener; import ru.testit.listener.ListenerManager; @@ -16,17 +29,18 @@ import ru.testit.models.*; import ru.testit.properties.AdapterConfig; import ru.testit.properties.AdapterMode; +import ru.testit.syncstorage.SyncStorageRunner; import ru.testit.writers.HttpWriter; import ru.testit.writers.Writer; -import java.util.*; -import java.util.function.Consumer; - /** * This class manages data from a test framework. */ public class AdapterManager { - private static final Logger LOGGER = LoggerFactory.getLogger(AdapterManager.class); + + private static final Logger LOGGER = LoggerFactory.getLogger( + AdapterManager.class + ); private final ThreadContext threadContext; private final ResultStorage storage; private final Writer writer; @@ -35,12 +49,20 @@ public class AdapterManager { private final AdapterConfig adapterConfig; private final ListenerManager listenerManager; + private final SyncStorageRunner syncStorageRunner; - public AdapterManager(ClientConfiguration clientConfiguration, AdapterConfig adapterConfig) { + public AdapterManager( + ClientConfiguration clientConfiguration, + AdapterConfig adapterConfig + ) { this(clientConfiguration, adapterConfig, getDefaultListenerManager()); } - public AdapterManager(ClientConfiguration clientConfiguration, AdapterConfig adapterConfig, ListenerManager listenerManager) { + public AdapterManager( + ClientConfiguration clientConfiguration, + AdapterConfig adapterConfig, + ListenerManager listenerManager + ) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Client configurations: {}", clientConfiguration); LOGGER.debug("Adapter configurations: {}", adapterConfig); @@ -51,8 +73,13 @@ public AdapterManager(ClientConfiguration clientConfiguration, AdapterConfig ada this.storage = Adapter.getResultStorage(); this.threadContext = new ThreadContext(); this.client = new TmsApiClient(this.clientConfiguration); - this.writer = new HttpWriter(this.clientConfiguration, this.client, this.storage); + this.writer = new HttpWriter( + this.clientConfiguration, + this.client, + this.storage + ); this.listenerManager = listenerManager; + this.syncStorageRunner = initializeSyncStorage(); } public AdapterManager( @@ -71,6 +98,7 @@ public AdapterManager( this.writer = writer; this.client = client; this.listenerManager = listenerManager; + this.syncStorageRunner = initializeSyncStorage(); } public void startTests() { @@ -81,25 +109,22 @@ public void startTests() { LOGGER.debug("Start launch"); synchronized (this.clientConfiguration) { - if (!Objects.equals(this.clientConfiguration.getTestRunId(), "null")) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Test run is exist."); - } - - try { - this.updateTestRunName(); - } catch (ApiException e) { - LOGGER.error("Can not update the launch: ".concat(e.getMessage())); - } + if ( + Objects.equals(this.clientConfiguration.getTestRunId(), "null") + ) { return; } - try { - TestRunV2ApiResult response = this.client.createTestRun(); - this.clientConfiguration.setTestRunId(response.getId().toString()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Test run is exist."); + } + try { + this.updateTestRunName(); } catch (ApiException e) { - LOGGER.error("Can not start the launch: ".concat(e.getMessage())); + LOGGER.error( + "Can not update the launch: ".concat(e.getMessage()) + ); } } } @@ -111,7 +136,9 @@ private void updateTestRunName() throws ApiException { return; } - TestRunV2ApiResult testRun = this.client.getTestRun(this.clientConfiguration.getTestRunId()); + TestRunV2ApiResult testRun = this.client.getTestRun( + this.clientConfiguration.getTestRunId() + ); if (testRun.getName().equals(testRunName)) { return; @@ -122,30 +149,39 @@ private void updateTestRunName() throws ApiException { this.client.updateTestRun(Converter.buildUpdateEmptyTestRunApiModel(testRun)); } - public void stopTests() { - if (!adapterConfig.shouldEnableTmsIntegration()) { - return; - } - - LOGGER.debug("Stop launch"); - + /** + * Initialize SyncStorageRunner + */ + private SyncStorageRunner initializeSyncStorage() { try { - TestRunV2ApiResult testRun = this.client.getTestRun(this.clientConfiguration.getTestRunId()); + // TODO: Получаем настройки из конфигурации + String port = "49152"; - TestStatusApiResult status = testRun.getStatus(); - TestStatusApiType type = status.getType(); - if (type != TestStatusApiType.SUCCEEDED) { - this.client.completeTestRun(this.clientConfiguration.getTestRunId()); - } - // disabled deprecated and testing - // if (testRun.getStateName() != TestRunState.COMPLETED) { - // this.client.completeTestRun(this.clientConfiguration.getTestRunId()); - // } - } catch (ApiException e) { - if (e.getResponseBody().contains("the StateName is already Completed")) { - return; + // create testrun if there are no existing one + String testRunId = clientConfiguration.getTestRunId(); + if (testRunId == null || "null".equals(testRunId)) { + TestRunV2ApiResult response = this.client.createTestRun(); + this.clientConfiguration.setTestRunId( + response.getId().toString() + ); + testRunId = response.getId().toString(); } - LOGGER.error("Can not finish the launch: ".concat(e.getMessage())); + + SyncStorageRunner runner = new SyncStorageRunner( + testRunId, + port, + clientConfiguration.getUrl(), + clientConfiguration.getPrivateToken() + ); + runner.start(); + return runner; + } catch (Exception e) { + LOGGER.warn( + "Failed to initialize SyncStorage: {}", + e.getMessage(), + e + ); + return null; } } @@ -165,6 +201,8 @@ public void startMainContainer(final MainContainer container) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Start new main container {}", container); } + LOGGER.info("Set in progress worker status"); + setWorkerStatus("in_progress"); } /** @@ -179,7 +217,10 @@ public void stopMainContainer(final String uuid) { final Optional found = storage.getTestsContainer(uuid); if (!found.isPresent()) { - LOGGER.error("Could not stop main container: container with uuid {} not found", uuid); + LOGGER.error( + "Could not stop main container: container with uuid {} not found", + uuid + ); return; } final MainContainer container = found.get(); @@ -190,6 +231,9 @@ public void stopMainContainer(final String uuid) { } writer.writeTests(container); + + LOGGER.info("End of main container, set completed"); + setWorkerStatus("completed"); } /** @@ -198,7 +242,10 @@ public void stopMainContainer(final String uuid) { * @param parentUuid the uuid of parent container. * @param container the class container. */ - public void startClassContainer(final String parentUuid, final ClassContainer container) { + public void startClassContainer( + final String parentUuid, + final ClassContainer container + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } @@ -210,8 +257,13 @@ public void startClassContainer(final String parentUuid, final ClassContainer co storage.put(container.getUuid(), container); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start new class container {} for parent {}", container, parentUuid); + LOGGER.debug( + "Start new class container {} for parent {}", + container, + parentUuid + ); } + setWorkerStatus("in_progress"); } /** @@ -226,7 +278,10 @@ public void stopClassContainer(final String uuid) { final Optional found = storage.getClassContainer(uuid); if (!found.isPresent()) { - LOGGER.debug("Could not stop class container: container with uuid {} not found", uuid); + LOGGER.debug( + "Could not stop class container: container with uuid {} not found", + uuid + ); return; } final ClassContainer container = found.get(); @@ -237,6 +292,8 @@ public void stopClassContainer(final String uuid) { } writer.writeClass(container); + + LOGGER.info("End of class container"); } /** @@ -245,7 +302,10 @@ public void stopClassContainer(final String uuid) { * @param uuid the uuid of container. * @param update the update function. */ - public void updateClassContainer(final String uuid, final Consumer update) { + public void updateClassContainer( + final String uuid, + final Consumer update + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } @@ -256,7 +316,10 @@ public void updateClassContainer(final String uuid, final Consumer found = storage.getClassContainer(uuid); if (!found.isPresent()) { - LOGGER.debug("Could not update class container: container with uuid {} not found", uuid); + LOGGER.debug( + "Could not update class container: container with uuid {} not found", + uuid + ); return; } final ClassContainer container = found.get(); @@ -276,12 +339,16 @@ public void startTestCase(final String uuid) { threadContext.clear(); final Optional found = storage.getTestResult(uuid); if (!found.isPresent()) { - LOGGER.error("Could not start test case: test case with uuid {} is not scheduled", uuid); + LOGGER.error( + "Could not start test case: test case with uuid {} is not scheduled", + uuid + ); return; } final TestResult testResult = found.get(); - testResult.setItemStage(ItemStage.RUNNING) + testResult + .setItemStage(ItemStage.RUNNING) .setStart(System.currentTimeMillis()); threadContext.start(uuid); @@ -301,8 +368,11 @@ public void scheduleTestCase(final TestResult result) { return; } - result.setItemStage(ItemStage.SCHEDULED) - .setAutomaticCreationTestCases(adapterConfig.shouldAutomaticCreationTestCases()); + result + .setItemStage(ItemStage.SCHEDULED) + .setAutomaticCreationTestCases( + adapterConfig.shouldAutomaticCreationTestCases() + ); storage.put(result.getUuid(), result); if (LOGGER.isDebugEnabled()) { @@ -336,7 +406,10 @@ public void updateTestCase(final Consumer update) { * @param uuid the uuid of test case to update. * @param update the update function. */ - public void updateTestCase(final String uuid, final Consumer update) { + public void updateTestCase( + final String uuid, + final Consumer update + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } @@ -347,7 +420,10 @@ public void updateTestCase(final String uuid, final Consumer update) final Optional found = storage.getTestResult(uuid); if (!found.isPresent()) { - LOGGER.error("Could not update test case: test case with uuid {} not found", uuid); + LOGGER.error( + "Could not update test case: test case with uuid {} not found", + uuid + ); return; } final TestResult testResult = found.get(); @@ -367,14 +443,18 @@ public void stopTestCase(final String uuid) { final Optional found = storage.getTestResult(uuid); if (!found.isPresent()) { - LOGGER.error("Could not stop test case: test case with uuid {} not found", uuid); + LOGGER.error( + "Could not stop test case: test case with uuid {} not found", + uuid + ); return; } final TestResult testResult = found.get(); listenerManager.beforeTestStop(testResult); - testResult.setItemStage(ItemStage.FINISHED) + testResult + .setItemStage(ItemStage.FINISHED) .setStop(System.currentTimeMillis()); threadContext.clear(); @@ -383,6 +463,24 @@ public void stopTestCase(final String uuid) { LOGGER.debug("Stop test case {}", testResult); } + // Проверяем, если текущий раннер - мастер, и в раннере стоит флаг (no in progress) + // если да - мы отправляем тест-результат в sync storage с финальным статусом + // а в test it пишем его как in progress + if ( + syncStorageRunner != null && + syncStorageRunner.isMaster() && + !syncStorageRunner.isAlreadyInProgress() + ) { + // Отправляем тест-результат в SyncStorage + sendTestResultToSyncStorage(testResult); + syncStorageRunner.setIsAlreadyInProgress(true); + // Помечаем тест как in progress для Test IT + markTestAsInProgress(testResult); + // Первый всегда прольется в реалтайме либо же фича будет работать криво + writer.writeTestRealtime(testResult); + return; + } + writer.writeTest(testResult); } @@ -393,13 +491,21 @@ public void stopTestCase(final String uuid) { * @param uuid the fixture uuid. * @param result the fixture. */ - public void startPrepareFixtureAll(final String parentUuid, final String uuid, final FixtureResult result) { + public void startPrepareFixtureAll( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start prepare all fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start prepare all fixture {} for parent {}", + result, + parentUuid + ); } storage.getTestsContainer(parentUuid).ifPresent(container -> { @@ -415,13 +521,21 @@ public void startPrepareFixtureAll(final String parentUuid, final String uuid, f * @param uuid the fixture uuid. * @param result the fixture. */ - public void startTearDownFixtureAll(final String parentUuid, final String uuid, final FixtureResult result) { + public void startTearDownFixtureAll( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start tear down all fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start tear down all fixture {} for parent {}", + result, + parentUuid + ); } storage.getTestsContainer(parentUuid).ifPresent(container -> { @@ -438,13 +552,21 @@ public void startTearDownFixtureAll(final String parentUuid, final String uuid, * @param uuid the fixture uuid. * @param result the fixture. */ - public void startPrepareFixture(final String parentUuid, final String uuid, final FixtureResult result) { + public void startPrepareFixture( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start prepare fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start prepare fixture {} for parent {}", + result, + parentUuid + ); } storage.getClassContainer(parentUuid).ifPresent(container -> { @@ -461,13 +583,21 @@ public void startPrepareFixture(final String parentUuid, final String uuid, fina * @param uuid the fixture uuid. * @param result the fixture. */ - public void startTearDownFixture(final String parentUuid, final String uuid, final FixtureResult result) { + public void startTearDownFixture( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start tear down fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start tear down fixture {} for parent {}", + result, + parentUuid + ); } storage.getClassContainer(parentUuid).ifPresent(container -> { @@ -484,13 +614,21 @@ public void startTearDownFixture(final String parentUuid, final String uuid, fin * @param uuid the fixture uuid. * @param result the fixture. */ - public void startPrepareFixtureEachTest(final String parentUuid, final String uuid, final FixtureResult result) { + public void startPrepareFixtureEachTest( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start prepare for each fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start prepare for each fixture {} for parent {}", + result, + parentUuid + ); } storage.getClassContainer(parentUuid).ifPresent(container -> { @@ -507,13 +645,21 @@ public void startPrepareFixtureEachTest(final String parentUuid, final String uu * @param uuid the fixture uuid. * @param result the fixture. */ - public void startTearDownFixtureEachTest(final String parentUuid, final String uuid, final FixtureResult result) { + public void startTearDownFixtureEachTest( + final String parentUuid, + final String uuid, + final FixtureResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Start tear down for each fixture {} for parent {}", result, parentUuid); + LOGGER.debug( + "Start tear down for each fixture {} for parent {}", + result, + parentUuid + ); } storage.getClassContainer(parentUuid).ifPresent(container -> { @@ -536,8 +682,9 @@ private void startFixture(final String uuid, final FixtureResult result) { storage.put(uuid, result); - result.setItemStage(ItemStage.RUNNING). - setStart(System.currentTimeMillis()); + result + .setItemStage(ItemStage.RUNNING) + .setStart(System.currentTimeMillis()); threadContext.clear(); threadContext.start(uuid); @@ -549,7 +696,10 @@ private void startFixture(final String uuid, final FixtureResult result) { * @param uuid the uuid of fixture. * @param update the update function. */ - public void updateFixture(final String uuid, final Consumer update) { + public void updateFixture( + final String uuid, + final Consumer update + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } @@ -560,7 +710,10 @@ public void updateFixture(final String uuid, final Consumer updat final Optional found = storage.getFixture(uuid); if (!found.isPresent()) { - LOGGER.error("Could not update test fixture: test fixture with uuid {} not found", uuid); + LOGGER.error( + "Could not update test fixture: test fixture with uuid {} not found", + uuid + ); return; } final FixtureResult fixture = found.get(); @@ -580,12 +733,16 @@ public void stopFixture(final String uuid) { final Optional found = storage.getFixture(uuid); if (!found.isPresent()) { - LOGGER.error("Could not stop test fixture: test fixture with uuid {} not found", uuid); + LOGGER.error( + "Could not stop test fixture: test fixture with uuid {} not found", + uuid + ); return; } final FixtureResult fixture = found.get(); - fixture.setItemStage(ItemStage.FINISHED) + fixture + .setItemStage(ItemStage.FINISHED) .setStop(System.currentTimeMillis()); storage.remove(uuid); @@ -609,7 +766,10 @@ public void startStep(final String uuid, final StepResult result) { final Optional current = threadContext.getCurrent(); if (!current.isPresent()) { - LOGGER.debug("Could not start step {}: no test case running", result); + LOGGER.debug( + "Could not start step {}: no test case running", + result + ); return; } final String parentUuid = current.get(); @@ -623,12 +783,17 @@ public void startStep(final String uuid, final StepResult result) { * @param uuid the uuid of step. * @param result the step. */ - public void startStep(final String parentUuid, final String uuid, final StepResult result) { + public void startStep( + final String parentUuid, + final String uuid, + final StepResult result + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } - result.setItemStage(ItemStage.RUNNING) + result + .setItemStage(ItemStage.RUNNING) .setStart(System.currentTimeMillis()); threadContext.start(uuid); @@ -668,7 +833,10 @@ public void updateStep(final Consumer update) { * @param uuid the uuid of step. * @param update the update function. */ - public void updateStep(final String uuid, final Consumer update) { + public void updateStep( + final String uuid, + final Consumer update + ) { if (!adapterConfig.shouldEnableTmsIntegration()) { return; } @@ -679,7 +847,10 @@ public void updateStep(final String uuid, final Consumer update) { final Optional found = storage.getStep(uuid); if (!found.isPresent()) { - LOGGER.error("Could not update step: step with uuid {} not found", uuid); + LOGGER.error( + "Could not update step: step with uuid {} not found", + uuid + ); return; } @@ -697,7 +868,8 @@ public void stopStep() { } final String root = threadContext.getRoot().orElse(null); - final Optional current = threadContext.getCurrent() + final Optional current = threadContext + .getCurrent() .filter(uuid -> !Objects.equals(uuid, root)); if (!current.isPresent()) { LOGGER.debug("Could not stop step: no step running"); @@ -719,7 +891,10 @@ public void stopStep(final String uuid) { final Optional found = storage.getStep(uuid); if (!found.isPresent()) { - LOGGER.error("Could not stop step: step with uuid {} not found", uuid); + LOGGER.error( + "Could not stop step: step with uuid {} not found", + uuid + ); return; } @@ -744,7 +919,7 @@ public void addAttachments(List attachments) { List uuids = new ArrayList<>(); for (final String attachment : attachments) { String attachmentsId = writer.writeAttachment(attachment); - if (attachmentsId.isEmpty()){ + if (attachmentsId.isEmpty()) { return; } uuids.add(attachmentsId); @@ -826,10 +1001,14 @@ public boolean isFilteredMode() { public List getTestFromTestRun() { if (adapterConfig.shouldEnableTmsIntegration()) { try { - List externalIds = client.getAutotestExternalIdsFromTestRun(); + List externalIds = + client.getAutotestExternalIdsFromTestRun(); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("List of tests from test run: {}", externalIds); + LOGGER.debug( + "List of tests from test run: {}", + externalIds + ); } return externalIds; @@ -846,7 +1025,151 @@ public Optional getCurrentTestCaseOrStep() { } private static ListenerManager getDefaultListenerManager() { - final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - return new ListenerManager(ServiceLoaderListener.load(AdapterListener.class, classLoader)); + final ClassLoader classLoader = + Thread.currentThread().getContextClassLoader(); + return new ListenerManager( + ServiceLoaderListener.load(AdapterListener.class, classLoader) + ); + } + + private void sendTestResultToSyncStorage(TestResult testResult) { + if (syncStorageRunner == null || !syncStorageRunner.isRunning()) { + return; + } + + try { + // Создаем объект AutoTestResultsForTestRunModel из TestResult + AutoTestResultsForTestRunModel payload = + Converter.testResultToAutoTestResultsForTestRunModel( + testResult + ); + + ObjectMapper objectMapper = new ObjectMapper(); + + LOGGER.info("trying to send testResult to sync storage"); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.setDateFormat( + new StdDateFormat().withColonInTimeZone(true) + ); + objectMapper.registerModule(new JsonNullableModule()); + String jsonPayload; + try { + jsonPayload = objectMapper.writeValueAsString(payload); + LOGGER.debug("Serialized payload: {}", jsonPayload); + } catch (JsonProcessingException e) { + LOGGER.warn( + "Failed to serialize AutoTestResultsForTestRunModel to JSON: {}", + e.getMessage() + ); + return; + } + + // Send POST to /in_progress_test_result + String url = + syncStorageRunner.getUrl() + + "/in_progress_test_result?testRunId=" + + syncStorageRunner.getTestRunId(); + int responseCode = postRequest(url, 10000, jsonPayload); + if (responseCode == 200) { + LOGGER.info( + "Successfully sent test result to SyncStorage for test: {}", + testResult.getExternalId() + ); + } else { + LOGGER.warn( + "Failed to send test result to SyncStorage. Response code: {}", + responseCode + ); + } + } catch (Exception e) { + LOGGER.warn( + "Failed to send test result to SyncStorage: {}", + e.getMessage() + ); + } + } + + private int postRequest(String url, int timeout, String jsonPayload) + throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL( + url + ).openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + connection.setConnectTimeout(timeout); + connection.setReadTimeout(timeout); + + try (java.io.OutputStream os = connection.getOutputStream()) { + byte[] input = jsonPayload.getBytes("utf-8"); + os.write(input, 0, input.length); + } + + int responseCode = connection.getResponseCode(); + return responseCode; + } + + /** + * Mark test as "in progress" for Test IT + */ + private void markTestAsInProgress(TestResult testResult) { + // Меняем статус теста на InProgress перед отправкой в Test IT + testResult.setItemStatus(ItemStatus.INPROGRESS); + } + + public void setWorkerStatus(String status) { + LOGGER.info("Set worker status to " + status); + if (syncStorageRunner == null) { + LOGGER.warn("No runner"); + return; + } + setWorkerStatus(syncStorageRunner.getWorkerPid(), status); + } + + /** + * Sets the status of a worker by its PID + * + * @param pid the PID of the worker + * @param status the status to set + */ + public void setWorkerStatus(String pid, String status) { + if (syncStorageRunner == null || !syncStorageRunner.isRunning()) { + LOGGER.info("not running???"); + return; + } + + try { + LOGGER.info(pid + ":" + status); + // make JSON payload + String jsonPayload = + "{\"pid\": \"" + + pid + + "\", \"status\": \"" + + status + + "\", \"testRunId\": \"" + + syncStorageRunner.getTestRunId() + + "\"" + + "}"; + + // Send POST to /set_worker_status + LOGGER.info(jsonPayload); + String url = syncStorageRunner.getUrl() + "/set_worker_status"; + LOGGER.info("url: " + url.toString()); + int responseCode = postRequest(url, 15000, jsonPayload); + if (responseCode == 200) { + LOGGER.info( + "Successfully set status {} for worker with PID: {}", + status, + pid + ); + } else { + LOGGER.warn( + "Failed to set status for worker. Response code: {}", + responseCode + ); + } + } catch (Exception e) { + LOGGER.warn("Failed to set worker status: {}", e.getMessage()); + } } } diff --git a/testit-java-commons/src/main/java/ru/testit/syncstorage/SyncStorageRunner.java b/testit-java-commons/src/main/java/ru/testit/syncstorage/SyncStorageRunner.java new file mode 100644 index 00000000..fd71bc13 --- /dev/null +++ b/testit-java-commons/src/main/java/ru/testit/syncstorage/SyncStorageRunner.java @@ -0,0 +1,552 @@ +package ru.testit.syncstorage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.testit.services.AdapterManager; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + + +public class SyncStorageRunner { + + private static final Logger LOGGER = LoggerFactory.getLogger( + AdapterManager.class + ); + + private Process syncStorageProcess; + + private final String testRunId; + + private final String port; + + private final String baseURL; + + private final String privateToken; + + private String workerPid; + + private boolean isMaster = false; + private boolean isAlreadyInProgress = false; + // running in process + private boolean isRunning = false; + // running outside of this process + private boolean isExternal = false; + + + private static final String SYNC_STORAGE_VERSION = "v0.1.9"; + + private static final String SYNC_STORAGE_REPO_URL ="https://github.com/testit-tms/sync-storage-public/releases/download/"; + private static final String AMD64 = "amd64"; + private static final String ARM64 = "arm64"; + + public SyncStorageRunner( + String testRunId, + String port, + String baseURL, + String privateToken + ) { + this.testRunId = testRunId; + this.port = port; + this.baseURL = baseURL; + this.privateToken = privateToken; + } + + /** + * Prepare executable file: checks build/.caches, if not - download from GitHub Releases + * + * @param originalExecutablePath file name + * @return path to correct file + * @throws IOException FS / Network errors + */ + private String prepareExecutableFile(String originalExecutablePath) + throws IOException { + String currentDir = System.getProperty("user.dir"); + Path cachesDir = Paths.get(currentDir, "build/.caches"); + + if (!Files.exists(cachesDir)) { + Files.createDirectories(cachesDir); + } + + Path originalPath = Paths.get(originalExecutablePath); + String fileName = originalPath.getFileName().toString(); + Path targetPath = cachesDir.resolve(fileName); + + if (Files.exists(targetPath)) { + LOGGER.info( + "Using existing file: " + + targetPath.toString() + ); + + // Make file executable for Unix-based systems + if ( + !System.getProperty("os.name").toLowerCase().contains("windows") + ) { + targetPath.toFile().setExecutable(true); + } + + return targetPath.toString(); + } + + LOGGER.info( + "File not present, downloading from GitHub Releases" + ); + downloadExecutableFromGitHub(targetPath); + + return targetPath.toString(); + } + + /** + * Download file from GitHub Releases + * + * @param targetPath path for saving file + * @throws IOException Network or OS errors + */ + private void downloadExecutableFromGitHub(Path targetPath) + throws IOException { + // Determine OS and arch + String downloadUrl = getDownloadUrlForCurrentPlatform(); + + LOGGER.info("Downloading file from: " + downloadUrl); + LOGGER.info("Saving in: " + targetPath.toString()); + + URL url = new URL(downloadUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(30000); // 30 sec + connection.setReadTimeout(30000); // 30 sec + + try { + int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new IOException( + "Error while downloading file, error code: " + responseCode + ); + } + + // Скачиваем файл + try ( + InputStream inputStream = connection.getInputStream(); + FileOutputStream outputStream = new FileOutputStream( + targetPath.toFile() + ) + ) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + + LOGGER.info("File downloaded successfully: " + targetPath.toString()); + + // Делаем файл исполняемым (для Unix-систем) + if ( + !System.getProperty("os.name").toLowerCase().contains("windows") + ) { + targetPath.toFile().setExecutable(true); + } + } finally { + connection.disconnect(); + } + } + + private String getFileNameByArchAndOS() { + String osName = System.getProperty("os.name").toLowerCase(); + String osArch = System.getProperty("os.arch").toLowerCase(); + + // Определяем ОС + String osPart; + if (osName.contains("win")) { + osPart = "windows"; + } else if (osName.contains("mac") || osName.contains("darwin")) { + osPart = "darwin"; + } else if (osName.contains("linux")) { + osPart = "linux"; + } else { + throw new RuntimeException( + "Unsupported OS, please contact dev team: " + osName + ); + } + + return makeFileName(osArch, osPart); + } + + private boolean isMacOs(String osName) { + return osName.contains("mac") || osName.contains("darwin"); + } + + private boolean isLinux(String osName) { + return osName.contains("linux"); + } + + /** + * Возвращает URL для скачивания файла в зависимости от текущей платформы + * + * @return URL для скачивания + */ + private String getDownloadUrlForCurrentPlatform() { + String fileName = getFileNameByArchAndOS(); + + // Returns URL for GitHub Releases + return ( + SYNC_STORAGE_REPO_URL + SYNC_STORAGE_VERSION + "/" + + fileName + ); + } + + private static String makeFileName(String osArch, String osPart) { + String archPart; + if (osArch.contains(AMD64) || osArch.contains("x86_64")) { + archPart = AMD64; + } else if (osArch.contains(ARM64) || osArch.contains("aarch64")) { + archPart = ARM64; + } else { + throw new RuntimeException( + "Unsupported architecture: " + osArch + ); + } + + // Формируем имя файла + String fileName = "syncstorage-" + SYNC_STORAGE_VERSION + "-" + osPart + "_" + archPart; + if (osPart.equals("windows")) { + fileName += ".exe"; + } + return fileName; + } + + /** + * Start SyncStorage process or connect to existing one + */ + public void start() throws IOException, InterruptedException { + if (isRunning) { + System.out.println("SyncStorage уже запущен"); + return; + } + + // check if SyncStorage running on selected port + if (isSyncStorageAlreadyRunning()) { + System.out.println( + "SyncStorage already started " + + port + + ". Connecting to existing one..." + ); + isRunning = true; + isExternal = true; + + try { + registerWorkerWithRetry(); + } + catch (Exception e) { + LOGGER.error(e.getMessage()); + } + + return; + } + + String executablePath = getFileNameByArchAndOS(); + + List command = new ArrayList<>(); + command.add(executablePath); + + if (testRunId != null && !testRunId.isEmpty()) { + command.add("--testRunId"); + command.add(testRunId); + } + + if (port != null && !port.isEmpty()) { + command.add("--port"); + command.add(port); + } + + // Add baseURL if defined + if (baseURL != null && !baseURL.isEmpty()) { + command.add("--baseURL"); + + command.add(baseURL); + } + + // Add privateToken if defined + if (privateToken != null && !privateToken.isEmpty()) { + command.add("--privateToken"); + command.add(privateToken); + } + + + + // prepare executable file + String preparedExecutablePath = prepareExecutableFile(executablePath); + + // Update command with selected file + command.set(0, preparedExecutablePath); + + String osName = System.getProperty("os.name").toLowerCase(); +// if (isMacOs(osName)) { +// String command1 = String.join(" ", command); +// command1 += "& disown -h %1"; +// +// command = new ArrayList<>(); +// command.add("zsh"); +// command.add("-c"); +// command.add(command1); +// } +// if (isLinux(osName)) { +// String command1 = String.join(" ", command); +// command1 = "nohup " + command1 + " > service.log 2>&1"; +// command1 += " & disown -h %1"; +// +// command = new ArrayList<>(); +// command.add("bash"); +// command.add("-c"); +// command.add(command1); +// } + + System.out.println( + "Starting SyncStorage with command: " + String.join(" ", command) + ); + + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.directory( + new File(new File(preparedExecutablePath).getParent()) + ); + + processBuilder.redirectErrorStream(true); + + syncStorageProcess = processBuilder.start(); + + // Read output as different thread + startOutputReader(); + + if (waitForServerStartup(30)) { + isRunning = true; + System.out.println("SyncStorage started successfully on port " + port); + Thread.sleep(2000); + try { + registerWorkerWithRetry(); + } + catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } else { + throw new RuntimeException( + "Cannot start the SyncStorage until timeout" + ); + } + } + + private void registerWorkerWithRetry() { + // Register current process as worker + // try 5 times in a row + for (int i = 0; i < 5; i++) { + boolean isRegistered = registerWorker(); + if (isRegistered) break; + } + } + + /** + * Run output reading thread + */ + private void startOutputReader() { + Thread readerThread = new Thread(() -> { + try ( + BufferedReader reader = new BufferedReader( + new InputStreamReader(syncStorageProcess.getInputStream()) + ) + ) { + String line; + while ( + (line = reader.readLine()) != null && + !Thread.currentThread().isInterrupted() + ) { + System.out.println("[SyncStorage] " + line); + } + } catch (IOException e) { + if (!Thread.currentThread().isInterrupted()) { + System.err.println( + "Ошибка чтения вывода SyncStorage: " + e.getMessage() + ); + } + } + }); + readerThread.setDaemon(true); + readerThread.start(); + } + + private boolean waitForServerStartup(int timeoutSeconds) { + long startTime = System.currentTimeMillis(); + long timeoutMillis = timeoutSeconds * 1000L; + + while (System.currentTimeMillis() - startTime < timeoutMillis) { + if (isSyncStorageAlreadyRunning()) { + return true; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } + + /** + * Check if SyncStorage running on port + */ + private boolean isSyncStorageAlreadyRunning() { + try { + URL url = new URL("http://localhost:" + port + "/health"); + HttpURLConnection connection = + (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(1000); + connection.setReadTimeout(1000); + + int responseCode = connection.getResponseCode(); + return responseCode == 200; + } catch (Exception e) { + return false; + } + } + + /** + * Register current process as worker SyncStorage + * API CALL + */ + private boolean registerWorker() { + try { + workerPid = + "worker-" + + Thread.currentThread().getId() + + "-" + + System.currentTimeMillis(); + + URL url = new URL(this.getUrl() + "/register"); + LOGGER.info("register on " + url.toString()); + HttpURLConnection connection = + (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setConnectTimeout(2000); + connection.setReadTimeout(2000); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + + String jsonInputString = + "{\"pid\": \"" + + workerPid + + "\", \"testRunId\": \"" + + testRunId + + "\"}"; + + java.io.OutputStream os = connection.getOutputStream(); + try { + byte[] input = jsonInputString.getBytes("utf-8"); + os.write(input, 0, input.length); + } finally { + os.close(); + } + + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + BufferedReader reader = new BufferedReader( + new InputStreamReader(connection.getInputStream()) + ); + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + // Parse JSON answer to check is_master + String responseStr = response.toString(); + if ( + responseStr.contains("\"is_master\":true") || + responseStr.contains("\"is_master\": true") + ) { + isMaster = true; + LOGGER.info( + "Master worker registered, PID: " + + workerPid + ); + } else { + LOGGER.info( + "Worker registered successfully, PID: " + workerPid + ); + } + return true; + } else { + LOGGER.warn( + "Error worker register. Response code: " + responseCode + ); + } + } catch (Exception e) { + LOGGER.error( + "Error on worker registering: " + e.getMessage() + ); + } + return false; + } + + /** + * Get worker PID + */ + public String getWorkerPid() { + return workerPid; + } + + public String getTestRunId() { + return testRunId; + } + + /** + * Check is current process master + */ + public boolean isMaster() { + return isMaster; + } + + public boolean isAlreadyInProgress() { + return isAlreadyInProgress; + } + + public void setIsAlreadyInProgress(boolean value) { + this.isAlreadyInProgress = value; + } + + /** + * Check is SyncStorage running + */ + public boolean isRunning() { +// LOGGER.info("isRunning" + isRunning); +// LOGGER.info("not null" + (syncStorageProcess != null)); + + return ( + isRunning + ); + } + + public boolean isRunningAsProcess() { + LOGGER.info("isRunning" + isRunning); + LOGGER.info("not null" + (syncStorageProcess != null)); + + return ( + isRunning && + syncStorageProcess != null && + syncStorageProcess.isAlive() + ); + } + + /** + * Get SyncStorage URL + */ + public String getUrl() { + return "http://localhost:" + port; + } +} diff --git a/testit-java-commons/src/main/java/ru/testit/writers/HttpWriter.java b/testit-java-commons/src/main/java/ru/testit/writers/HttpWriter.java index 4d608f0d..741ee286 100644 --- a/testit-java-commons/src/main/java/ru/testit/writers/HttpWriter.java +++ b/testit-java-commons/src/main/java/ru/testit/writers/HttpWriter.java @@ -31,24 +31,23 @@ public HttpWriter(ClientConfiguration config, ITmsApiClient client, ResultStorag @Override public void writeTest(TestResult testResult) { - if (!config.shouldImportRealtime()) - { + if (!config.shouldImportRealtime()) { return; } writeTestRealtime(testResult); } - private void writeTestRealtime(TestResult testResult) { + @Override + public void writeTestRealtime(TestResult testResult) { try { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Write the auto test {}", testResult.getExternalId()); } - AutoTestApiResult autoTestApiResult = apiClient.getAutoTestByExternalId(testResult.getExternalId()); + AutoTestApiResult autotest = apiClient.getAutoTestByExternalId(testResult.getExternalId()); String autoTestId; - AutoTestApiResult autotest = Converter.convertAutoTestApiResultToAutoTestApiResult(autoTestApiResult); if (autotest != null) { if (LOGGER.isDebugEnabled()) { @@ -127,8 +126,7 @@ private void updateTestLinkToWorkItems(String autoTestId, List workItemI @Override public void writeClass(ClassContainer container) { - if (!config.shouldImportRealtime()) - { + if (!config.shouldImportRealtime()) { return; } @@ -137,13 +135,11 @@ public void writeClass(ClassContainer container) { try { AutoTestApiResult autoTestApiResult = apiClient.getAutoTestByExternalId(test.getExternalId()); - AutoTestApiResult AutoTestApiResult = Converter.convertAutoTestApiResultToAutoTestApiResult(autoTestApiResult); - - if (AutoTestApiResult == null) { + if (autoTestApiResult == null) { return; } - AutoTestUpdateApiModel autoTestUpdateApiModel = Converter.AutoTestApiResultToAutoTestUpdateApiModel(AutoTestApiResult); + AutoTestUpdateApiModel autoTestUpdateApiModel = Converter.AutoTestApiResultToAutoTestUpdateApiModel(autoTestApiResult); List beforeClass = Converter.convertFixtureToApi(container.getBeforeClassMethods(), null); List beforeEach = Converter.convertFixtureToApi(container.getBeforeEachTest(), testUuid); @@ -156,9 +152,15 @@ public void writeClass(ClassContainer container) { autoTestUpdateApiModel.setSetup(beforeClass); autoTestUpdateApiModel.setTeardown(afterClass); - autoTestUpdateApiModel.setIsFlaky(AutoTestApiResult.getIsFlaky()); + autoTestUpdateApiModel.setIsFlaky(autoTestApiResult.getIsFlaky()); - apiClient.updateAutoTest(autoTestUpdateApiModel); + // Оптимизация: сравниваем модель с сервера с той, которую хотим отправить + if (hasAutoTestChanged(autoTestApiResult, autoTestUpdateApiModel)) { + apiClient.updateAutoTest(autoTestUpdateApiModel); + LOGGER.debug("AutoTest {} updated", test.getExternalId()); + } else { + LOGGER.debug("AutoTest {} has not changed, skipping update", test.getExternalId()); + } } catch (ApiException e) { LOGGER.error("Can not write the class: {}", (e.getMessage())); } @@ -168,13 +170,13 @@ public void writeClass(ClassContainer container) { @Override public void writeTests(MainContainer container) { - if (config.shouldImportRealtime()) - { + if (config.shouldImportRealtime()) { updateTestResults(container); return; } + // realtime false: all in one writeTestsAfterAll(container); } @@ -195,13 +197,11 @@ private void updateTestResults(MainContainer container) { try { AutoTestApiResult autoTestApiResult = apiClient.getAutoTestByExternalId(test.getExternalId()); - AutoTestApiResult AutoTestApiResult = Converter.convertAutoTestApiResultToAutoTestApiResult(autoTestApiResult); - - if (AutoTestApiResult == null) { + if (autoTestApiResult == null) { return; } - AutoTestUpdateApiModel autoTestUpdateApiModel = Converter.AutoTestApiResultToAutoTestUpdateApiModel(AutoTestApiResult); + AutoTestUpdateApiModel autoTestUpdateApiModel = Converter.AutoTestApiResultToAutoTestUpdateApiModel(autoTestApiResult); List beforeFinish = new ArrayList<>(beforeAll); beforeFinish.addAll(autoTestUpdateApiModel.getSetup()); @@ -212,9 +212,14 @@ private void updateTestResults(MainContainer container) { afterFinish.addAll(afterAll); autoTestUpdateApiModel.setTeardown(afterFinish); - autoTestUpdateApiModel.setIsFlaky(AutoTestApiResult.getIsFlaky()); + autoTestUpdateApiModel.setIsFlaky(autoTestApiResult.getIsFlaky()); - apiClient.updateAutoTest(autoTestUpdateApiModel); + // Оптимизация: сравниваем модель с сервера с той, которую хотим отправить + if (hasAutoTestChanged(autoTestApiResult, autoTestUpdateApiModel)) { + apiClient.updateAutoTest(autoTestUpdateApiModel); + } else { + LOGGER.debug("AutoTest {} has not changed, skipping update", test.getExternalId()); + } AutoTestResultsForTestRunModel autoTestResultsForTestRunModel = Converter.testResultToAutoTestResultsForTestRunModel(test); @@ -243,7 +248,7 @@ private void updateTestResults(MainContainer container) { apiClient.updateTestResult(testResultId, model); } catch (ApiException e) { - LOGGER.error("Can not update the autotest: {}",(e.getMessage())); + LOGGER.error("Can not update the autotest: {}", (e.getMessage())); } }); } @@ -295,8 +300,6 @@ private void writeTestsAfterAll(MainContainer container) { AutoTestApiResult autoTestApiResult = apiClient.getAutoTestByExternalId(test.getExternalId()); - AutoTestApiResult AutoTestApiResult = Converter.convertAutoTestApiResultToAutoTestApiResult(autoTestApiResult); - AutoTestResultsForTestRunModel autoTestResultsForTestRunModel = Converter.prepareTestResultForTestRun( test, config.getConfigurationId() @@ -305,7 +308,7 @@ private void writeTestsAfterAll(MainContainer container) { autoTestResultsForTestRunModel.setSetupResults(beforeResultFinish); autoTestResultsForTestRunModel.setTeardownResults(afterResultFinish); - if (AutoTestApiResult == null) { + if (autoTestApiResult == null) { AutoTestCreateApiModel model = Converter.prepareToCreateAutoTest( test, config.getProjectId() @@ -317,14 +320,14 @@ private void writeTestsAfterAll(MainContainer container) { } else { AutoTestUpdateApiModel model = Converter.prepareToUpdateAutoTest( test, - AutoTestApiResult, + autoTestApiResult, config.getProjectId() ); model.setSetup(beforeFinish); model.setTeardown(afterFinish); - String id = AutoTestApiResult.getGlobalId().toString(); + String id = autoTestApiResult.getGlobalId().toString(); List wi = test.getWorkItemIds(); Map> autotestLinksToWIForUpdate = new HashMap<>(); @@ -344,6 +347,7 @@ private void writeTestsAfterAll(MainContainer container) { }); } + try { bulkHelper.teardown(); } catch (ApiException e) { @@ -365,4 +369,85 @@ public String writeAttachment(String path) { void addUuid(String key, UUID uuid) { this.testResults.put(key, uuid); } + + + /** + * Сравнивает авто-тест с сервера с моделью для обновления + * + * @param serverModel модель с сервера + * @param updateModel модель для обновления + * @return true, если модели отличаются и нужно выполнить обновление + */ + private boolean hasAutoTestChanged(AutoTestApiResult serverModel, AutoTestUpdateApiModel updateModel) { + if (serverModel == null || updateModel == null) { + return true; + } + // Сравниваем основные поля + if (!Objects.equals(serverModel.getExternalId(), updateModel.getExternalId())) { + return true; + } + if (!Objects.equals(serverModel.getName(), updateModel.getName())) { + return true; + } + if (!Objects.equals(serverModel.getNamespace(), updateModel.getNamespace())) { + return true; + } + if (!Objects.equals(serverModel.getClassname(), updateModel.getClassname())) { + return true; + } + if (!Objects.equals(serverModel.getTitle(), updateModel.getTitle())) { + return true; + } + if (!Objects.equals(serverModel.getDescription(), updateModel.getDescription())) { + return true; + } + // Сравниваем setup методы + if (!areStepListsEqual(serverModel.getSetup(), updateModel.getSetup())) { + return true; + } + // Сравниваем teardown методы + if (!areStepListsEqual(serverModel.getTeardown(), updateModel.getTeardown())) { + return true; + } + // Если дошли до этого места, модели идентичны + return false; + } + + /** + * Сравнивает два списка шагов авто-теста + * + * @param serverSteps шаги с сервера + * @param updateSteps шаги для обновления + * @return true, если списки отличаются + */ + private boolean areStepListsEqual(List serverSteps, List updateSteps) { + + if (serverSteps == null && updateSteps == null) { + return false; // Если оба null, то они равны + } + if (serverSteps == null || updateSteps == null) { + return true; // Если один null, а другой нет, то они разные + } + + if (serverSteps.size() != updateSteps.size()) { + return true; + } + + for (int i = 0; i < serverSteps.size(); i++) { + AutoTestStepApiResult serverStep = serverSteps.get(i); + AutoTestStepApiModel updateStep = updateSteps.get(i); + + if (!Objects.equals(serverStep.getTitle(), updateStep.getTitle())) { + return true; + } + + if (!Objects.equals(serverStep.getDescription(), updateStep.getDescription())) { + return true; + } + + } + + return false; + } + } diff --git a/testit-java-commons/src/main/java/ru/testit/writers/Writer.java b/testit-java-commons/src/main/java/ru/testit/writers/Writer.java index b589abc0..8a484088 100644 --- a/testit-java-commons/src/main/java/ru/testit/writers/Writer.java +++ b/testit-java-commons/src/main/java/ru/testit/writers/Writer.java @@ -5,6 +5,9 @@ import ru.testit.models.TestResult; public interface Writer { + + void writeTestRealtime(TestResult testResult); + void writeTest(TestResult testResult); void writeClass(ClassContainer container);