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);