commit 8ffb0acb2672cd200c20a4c809f5c20a836dae45
Author: Radek Davidek
Date: Thu Feb 26 15:03:19 2026 +0100
initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..15ad8ef
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,51 @@
+### Eclipse
+.metadata
+.classpath
+.project
+.settings/
+
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.mvn
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+mvnw
+mvnw.cmd
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
+
+### Log4j2 ###
+logs
+*.log
+
+# Ignore Mac DS_Store files
+.DS_Store
+
+dependency-reduced-pom.xml
+
+cert.pem
\ No newline at end of file
diff --git a/test-harness/.gitignore b/test-harness/.gitignore
new file mode 100644
index 0000000..ff7a25f
--- /dev/null
+++ b/test-harness/.gitignore
@@ -0,0 +1,6 @@
+.idea
+target
+*.iml
+.classpath
+.project
+.settings
diff --git a/test-harness/README.md b/test-harness/README.md
new file mode 100644
index 0000000..d7f51bf
--- /dev/null
+++ b/test-harness/README.md
@@ -0,0 +1,3 @@
+# Test Harness
+
+This repo conatins Test Harness, code name "Hercules".
diff --git a/test-harness/lib/ST4-4.3.4.jar b/test-harness/lib/ST4-4.3.4.jar
new file mode 100644
index 0000000..21a2f61
Binary files /dev/null and b/test-harness/lib/ST4-4.3.4.jar differ
diff --git a/test-harness/lib/accessors-smart-2.5.0.jar b/test-harness/lib/accessors-smart-2.5.0.jar
new file mode 100644
index 0000000..2acd43b
Binary files /dev/null and b/test-harness/lib/accessors-smart-2.5.0.jar differ
diff --git a/test-harness/lib/angus-activation-2.0.1.jar b/test-harness/lib/angus-activation-2.0.1.jar
new file mode 100644
index 0000000..9d56221
Binary files /dev/null and b/test-harness/lib/angus-activation-2.0.1.jar differ
diff --git a/test-harness/lib/angus-mail-1.0.0.jar b/test-harness/lib/angus-mail-1.0.0.jar
new file mode 100644
index 0000000..6a14b6f
Binary files /dev/null and b/test-harness/lib/angus-mail-1.0.0.jar differ
diff --git a/test-harness/lib/antlr-runtime-3.5.3.jar b/test-harness/lib/antlr-runtime-3.5.3.jar
new file mode 100644
index 0000000..0b06a7a
Binary files /dev/null and b/test-harness/lib/antlr-runtime-3.5.3.jar differ
diff --git a/test-harness/lib/aopalliance-repackaged-3.0.3.jar b/test-harness/lib/aopalliance-repackaged-3.0.3.jar
new file mode 100644
index 0000000..b774de5
Binary files /dev/null and b/test-harness/lib/aopalliance-repackaged-3.0.3.jar differ
diff --git a/test-harness/lib/apiguardian-api-1.1.0.jar b/test-harness/lib/apiguardian-api-1.1.0.jar
new file mode 100644
index 0000000..e6fcead
Binary files /dev/null and b/test-harness/lib/apiguardian-api-1.1.0.jar differ
diff --git a/test-harness/lib/asm-9.5.jar b/test-harness/lib/asm-9.5.jar
new file mode 100644
index 0000000..f5701dc
Binary files /dev/null and b/test-harness/lib/asm-9.5.jar differ
diff --git a/test-harness/lib/assertj-core-3.25.3.jar b/test-harness/lib/assertj-core-3.25.3.jar
new file mode 100644
index 0000000..5bbac20
Binary files /dev/null and b/test-harness/lib/assertj-core-3.25.3.jar differ
diff --git a/test-harness/lib/async-http-client-2.12.3.jar b/test-harness/lib/async-http-client-2.12.3.jar
new file mode 100644
index 0000000..3bed21d
Binary files /dev/null and b/test-harness/lib/async-http-client-2.12.3.jar differ
diff --git a/test-harness/lib/async-http-client-netty-utils-2.12.3.jar b/test-harness/lib/async-http-client-netty-utils-2.12.3.jar
new file mode 100644
index 0000000..f072e8f
Binary files /dev/null and b/test-harness/lib/async-http-client-netty-utils-2.12.3.jar differ
diff --git a/test-harness/lib/auto-service-annotations-1.1.1.jar b/test-harness/lib/auto-service-annotations-1.1.1.jar
new file mode 100644
index 0000000..b9ceaf7
Binary files /dev/null and b/test-harness/lib/auto-service-annotations-1.1.1.jar differ
diff --git a/test-harness/lib/bcpkix-jdk18on-1.72.jar b/test-harness/lib/bcpkix-jdk18on-1.72.jar
new file mode 100644
index 0000000..b4902c8
Binary files /dev/null and b/test-harness/lib/bcpkix-jdk18on-1.72.jar differ
diff --git a/test-harness/lib/bcprov-jdk18on-1.71.jar b/test-harness/lib/bcprov-jdk18on-1.71.jar
new file mode 100644
index 0000000..c996730
Binary files /dev/null and b/test-harness/lib/bcprov-jdk18on-1.71.jar differ
diff --git a/test-harness/lib/bcutil-jdk18on-1.72.jar b/test-harness/lib/bcutil-jdk18on-1.72.jar
new file mode 100644
index 0000000..e9be74d
Binary files /dev/null and b/test-harness/lib/bcutil-jdk18on-1.72.jar differ
diff --git a/test-harness/lib/byte-buddy-1.14.5.jar b/test-harness/lib/byte-buddy-1.14.5.jar
new file mode 100644
index 0000000..409c3c3
Binary files /dev/null and b/test-harness/lib/byte-buddy-1.14.5.jar differ
diff --git a/test-harness/lib/cache-api-1.1.0.jar b/test-harness/lib/cache-api-1.1.0.jar
new file mode 100644
index 0000000..4640137
Binary files /dev/null and b/test-harness/lib/cache-api-1.1.0.jar differ
diff --git a/test-harness/lib/checker-qual-3.41.0.jar b/test-harness/lib/checker-qual-3.41.0.jar
new file mode 100644
index 0000000..17a85a1
Binary files /dev/null and b/test-harness/lib/checker-qual-3.41.0.jar differ
diff --git a/test-harness/lib/commons-beanutils-1.9.3.jar b/test-harness/lib/commons-beanutils-1.9.3.jar
new file mode 100644
index 0000000..6728154
Binary files /dev/null and b/test-harness/lib/commons-beanutils-1.9.3.jar differ
diff --git a/test-harness/lib/commons-beanutils-core-1.8.0.jar b/test-harness/lib/commons-beanutils-core-1.8.0.jar
new file mode 100644
index 0000000..87c15f4
Binary files /dev/null and b/test-harness/lib/commons-beanutils-core-1.8.0.jar differ
diff --git a/test-harness/lib/commons-codec-1.11.jar b/test-harness/lib/commons-codec-1.11.jar
new file mode 100644
index 0000000..2245120
Binary files /dev/null and b/test-harness/lib/commons-codec-1.11.jar differ
diff --git a/test-harness/lib/commons-collections-3.2.2.jar b/test-harness/lib/commons-collections-3.2.2.jar
new file mode 100644
index 0000000..fa5df82
Binary files /dev/null and b/test-harness/lib/commons-collections-3.2.2.jar differ
diff --git a/test-harness/lib/commons-configuration-1.6.jar b/test-harness/lib/commons-configuration-1.6.jar
new file mode 100644
index 0000000..2d4689a
Binary files /dev/null and b/test-harness/lib/commons-configuration-1.6.jar differ
diff --git a/test-harness/lib/commons-digester-1.8.jar b/test-harness/lib/commons-digester-1.8.jar
new file mode 100644
index 0000000..1110f0a
Binary files /dev/null and b/test-harness/lib/commons-digester-1.8.jar differ
diff --git a/test-harness/lib/commons-exec-1.3.jar b/test-harness/lib/commons-exec-1.3.jar
new file mode 100644
index 0000000..9a64351
Binary files /dev/null and b/test-harness/lib/commons-exec-1.3.jar differ
diff --git a/test-harness/lib/commons-io-2.6.jar b/test-harness/lib/commons-io-2.6.jar
new file mode 100644
index 0000000..00556b1
Binary files /dev/null and b/test-harness/lib/commons-io-2.6.jar differ
diff --git a/test-harness/lib/commons-lang-2.4.jar b/test-harness/lib/commons-lang-2.4.jar
new file mode 100644
index 0000000..532939e
Binary files /dev/null and b/test-harness/lib/commons-lang-2.4.jar differ
diff --git a/test-harness/lib/commons-lang3-3.7.jar b/test-harness/lib/commons-lang3-3.7.jar
new file mode 100644
index 0000000..f37ded6
Binary files /dev/null and b/test-harness/lib/commons-lang3-3.7.jar differ
diff --git a/test-harness/lib/commons-logging-1.2.jar b/test-harness/lib/commons-logging-1.2.jar
new file mode 100644
index 0000000..93a3b9f
Binary files /dev/null and b/test-harness/lib/commons-logging-1.2.jar differ
diff --git a/test-harness/lib/commons-text-1.4.jar b/test-harness/lib/commons-text-1.4.jar
new file mode 100644
index 0000000..3e81a79
Binary files /dev/null and b/test-harness/lib/commons-text-1.4.jar differ
diff --git a/test-harness/lib/cryptacular-1.2.5.jar b/test-harness/lib/cryptacular-1.2.5.jar
new file mode 100644
index 0000000..8cc43f3
Binary files /dev/null and b/test-harness/lib/cryptacular-1.2.5.jar differ
diff --git a/test-harness/lib/cxf-core-4.0.3.jar b/test-harness/lib/cxf-core-4.0.3.jar
new file mode 100644
index 0000000..da16bfd
Binary files /dev/null and b/test-harness/lib/cxf-core-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-bindings-soap-4.0.3.jar b/test-harness/lib/cxf-rt-bindings-soap-4.0.3.jar
new file mode 100644
index 0000000..8d7558f
Binary files /dev/null and b/test-harness/lib/cxf-rt-bindings-soap-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-bindings-xml-4.0.3.jar b/test-harness/lib/cxf-rt-bindings-xml-4.0.3.jar
new file mode 100644
index 0000000..a33f33f
Binary files /dev/null and b/test-harness/lib/cxf-rt-bindings-xml-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-databinding-jaxb-4.0.3.jar b/test-harness/lib/cxf-rt-databinding-jaxb-4.0.3.jar
new file mode 100644
index 0000000..be639d0
Binary files /dev/null and b/test-harness/lib/cxf-rt-databinding-jaxb-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-frontend-jaxws-4.0.3.jar b/test-harness/lib/cxf-rt-frontend-jaxws-4.0.3.jar
new file mode 100644
index 0000000..934b3e7
Binary files /dev/null and b/test-harness/lib/cxf-rt-frontend-jaxws-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-frontend-simple-4.0.3.jar b/test-harness/lib/cxf-rt-frontend-simple-4.0.3.jar
new file mode 100644
index 0000000..ebbd05d
Binary files /dev/null and b/test-harness/lib/cxf-rt-frontend-simple-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-security-4.0.3.jar b/test-harness/lib/cxf-rt-security-4.0.3.jar
new file mode 100644
index 0000000..b19c6ba
Binary files /dev/null and b/test-harness/lib/cxf-rt-security-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-security-saml-4.0.3.jar b/test-harness/lib/cxf-rt-security-saml-4.0.3.jar
new file mode 100644
index 0000000..429085b
Binary files /dev/null and b/test-harness/lib/cxf-rt-security-saml-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-transports-http-4.0.3.jar b/test-harness/lib/cxf-rt-transports-http-4.0.3.jar
new file mode 100644
index 0000000..b13dbf1
Binary files /dev/null and b/test-harness/lib/cxf-rt-transports-http-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-ws-addr-4.0.3.jar b/test-harness/lib/cxf-rt-ws-addr-4.0.3.jar
new file mode 100644
index 0000000..d102c84
Binary files /dev/null and b/test-harness/lib/cxf-rt-ws-addr-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-ws-policy-4.0.3.jar b/test-harness/lib/cxf-rt-ws-policy-4.0.3.jar
new file mode 100644
index 0000000..b0bb85b
Binary files /dev/null and b/test-harness/lib/cxf-rt-ws-policy-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-ws-security-4.0.3.jar b/test-harness/lib/cxf-rt-ws-security-4.0.3.jar
new file mode 100644
index 0000000..33ac751
Binary files /dev/null and b/test-harness/lib/cxf-rt-ws-security-4.0.3.jar differ
diff --git a/test-harness/lib/cxf-rt-wsdl-4.0.3.jar b/test-harness/lib/cxf-rt-wsdl-4.0.3.jar
new file mode 100644
index 0000000..943b8a1
Binary files /dev/null and b/test-harness/lib/cxf-rt-wsdl-4.0.3.jar differ
diff --git a/test-harness/lib/dm3270-lib-0.9.1.jar b/test-harness/lib/dm3270-lib-0.9.1.jar
new file mode 100644
index 0000000..2e9afae
Binary files /dev/null and b/test-harness/lib/dm3270-lib-0.9.1.jar differ
diff --git a/test-harness/lib/ehcache-3.10.8-jakarta.jar b/test-harness/lib/ehcache-3.10.8-jakarta.jar
new file mode 100644
index 0000000..264b547
Binary files /dev/null and b/test-harness/lib/ehcache-3.10.8-jakarta.jar differ
diff --git a/test-harness/lib/error_prone_annotations-2.27.0.jar b/test-harness/lib/error_prone_annotations-2.27.0.jar
new file mode 100644
index 0000000..4ea471f
Binary files /dev/null and b/test-harness/lib/error_prone_annotations-2.27.0.jar differ
diff --git a/test-harness/lib/failsafe-3.3.2.jar b/test-harness/lib/failsafe-3.3.2.jar
new file mode 100644
index 0000000..76ae519
Binary files /dev/null and b/test-harness/lib/failsafe-3.3.2.jar differ
diff --git a/test-harness/lib/failureaccess-1.0.1.jar b/test-harness/lib/failureaccess-1.0.1.jar
new file mode 100644
index 0000000..9b56dc7
Binary files /dev/null and b/test-harness/lib/failureaccess-1.0.1.jar differ
diff --git a/test-harness/lib/gson-2.11.0.jar b/test-harness/lib/gson-2.11.0.jar
new file mode 100644
index 0000000..18e59c8
Binary files /dev/null and b/test-harness/lib/gson-2.11.0.jar differ
diff --git a/test-harness/lib/guava-32.1.2-jre.jar b/test-harness/lib/guava-32.1.2-jre.jar
new file mode 100644
index 0000000..e71fd46
Binary files /dev/null and b/test-harness/lib/guava-32.1.2-jre.jar differ
diff --git a/test-harness/lib/hamcrest-core-1.3.jar b/test-harness/lib/hamcrest-core-1.3.jar
new file mode 100644
index 0000000..9d5fe16
Binary files /dev/null and b/test-harness/lib/hamcrest-core-1.3.jar differ
diff --git a/test-harness/lib/hk2-api-3.0.3.jar b/test-harness/lib/hk2-api-3.0.3.jar
new file mode 100644
index 0000000..a8f8e88
Binary files /dev/null and b/test-harness/lib/hk2-api-3.0.3.jar differ
diff --git a/test-harness/lib/hk2-locator-3.0.3.jar b/test-harness/lib/hk2-locator-3.0.3.jar
new file mode 100644
index 0000000..6fd9b3b
Binary files /dev/null and b/test-harness/lib/hk2-locator-3.0.3.jar differ
diff --git a/test-harness/lib/hk2-utils-3.0.3.jar b/test-harness/lib/hk2-utils-3.0.3.jar
new file mode 100644
index 0000000..e586d4c
Binary files /dev/null and b/test-harness/lib/hk2-utils-3.0.3.jar differ
diff --git a/test-harness/lib/httpclient-4.5.14.jar b/test-harness/lib/httpclient-4.5.14.jar
new file mode 100644
index 0000000..2bb7c07
Binary files /dev/null and b/test-harness/lib/httpclient-4.5.14.jar differ
diff --git a/test-harness/lib/httpcore-4.4.16.jar b/test-harness/lib/httpcore-4.4.16.jar
new file mode 100644
index 0000000..f0bdebe
Binary files /dev/null and b/test-harness/lib/httpcore-4.4.16.jar differ
diff --git a/test-harness/lib/ilods_shared_resources-0.0.9.jar b/test-harness/lib/ilods_shared_resources-0.0.9.jar
new file mode 100644
index 0000000..0d101b1
Binary files /dev/null and b/test-harness/lib/ilods_shared_resources-0.0.9.jar differ
diff --git a/test-harness/lib/istack-commons-runtime-4.0.0.jar b/test-harness/lib/istack-commons-runtime-4.0.0.jar
new file mode 100644
index 0000000..66f9d42
Binary files /dev/null and b/test-harness/lib/istack-commons-runtime-4.0.0.jar differ
diff --git a/test-harness/lib/j2objc-annotations-2.8.jar b/test-harness/lib/j2objc-annotations-2.8.jar
new file mode 100644
index 0000000..3595c4f
Binary files /dev/null and b/test-harness/lib/j2objc-annotations-2.8.jar differ
diff --git a/test-harness/lib/jackson-annotations-2.16.1.jar b/test-harness/lib/jackson-annotations-2.16.1.jar
new file mode 100644
index 0000000..b9c48e6
Binary files /dev/null and b/test-harness/lib/jackson-annotations-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-core-2.16.1.jar b/test-harness/lib/jackson-core-2.16.1.jar
new file mode 100644
index 0000000..a8ff5e3
Binary files /dev/null and b/test-harness/lib/jackson-core-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-databind-2.16.1.jar b/test-harness/lib/jackson-databind-2.16.1.jar
new file mode 100644
index 0000000..5171b7a
Binary files /dev/null and b/test-harness/lib/jackson-databind-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-dataformat-csv-2.16.1.jar b/test-harness/lib/jackson-dataformat-csv-2.16.1.jar
new file mode 100644
index 0000000..d59e286
Binary files /dev/null and b/test-harness/lib/jackson-dataformat-csv-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-dataformat-xml-2.16.1.jar b/test-harness/lib/jackson-dataformat-xml-2.16.1.jar
new file mode 100644
index 0000000..1cd03f2
Binary files /dev/null and b/test-harness/lib/jackson-dataformat-xml-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-datatype-jdk8-2.16.1.jar b/test-harness/lib/jackson-datatype-jdk8-2.16.1.jar
new file mode 100644
index 0000000..e399401
Binary files /dev/null and b/test-harness/lib/jackson-datatype-jdk8-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-datatype-jsr310-2.16.1.jar b/test-harness/lib/jackson-datatype-jsr310-2.16.1.jar
new file mode 100644
index 0000000..d34b0ef
Binary files /dev/null and b/test-harness/lib/jackson-datatype-jsr310-2.16.1.jar differ
diff --git a/test-harness/lib/jackson-module-jakarta-xmlbind-annotations-2.15.3.jar b/test-harness/lib/jackson-module-jakarta-xmlbind-annotations-2.15.3.jar
new file mode 100644
index 0000000..312cfe2
Binary files /dev/null and b/test-harness/lib/jackson-module-jakarta-xmlbind-annotations-2.15.3.jar differ
diff --git a/test-harness/lib/jackson-module-parameter-names-2.16.1.jar b/test-harness/lib/jackson-module-parameter-names-2.16.1.jar
new file mode 100644
index 0000000..83a08af
Binary files /dev/null and b/test-harness/lib/jackson-module-parameter-names-2.16.1.jar differ
diff --git a/test-harness/lib/jakarta.activation-2.0.1.jar b/test-harness/lib/jakarta.activation-2.0.1.jar
new file mode 100644
index 0000000..521c7c4
Binary files /dev/null and b/test-harness/lib/jakarta.activation-2.0.1.jar differ
diff --git a/test-harness/lib/jakarta.activation-api-2.1.2.jar b/test-harness/lib/jakarta.activation-api-2.1.2.jar
new file mode 100644
index 0000000..ea57b3e
Binary files /dev/null and b/test-harness/lib/jakarta.activation-api-2.1.2.jar differ
diff --git a/test-harness/lib/jakarta.annotation-api-2.0.0.jar b/test-harness/lib/jakarta.annotation-api-2.0.0.jar
new file mode 100644
index 0000000..a7f3008
Binary files /dev/null and b/test-harness/lib/jakarta.annotation-api-2.0.0.jar differ
diff --git a/test-harness/lib/jakarta.inject-api-2.0.1.jar b/test-harness/lib/jakarta.inject-api-2.0.1.jar
new file mode 100644
index 0000000..a92e099
Binary files /dev/null and b/test-harness/lib/jakarta.inject-api-2.0.1.jar differ
diff --git a/test-harness/lib/jakarta.jws-api-3.0.0.jar b/test-harness/lib/jakarta.jws-api-3.0.0.jar
new file mode 100644
index 0000000..5d6151b
Binary files /dev/null and b/test-harness/lib/jakarta.jws-api-3.0.0.jar differ
diff --git a/test-harness/lib/jakarta.mail-api-2.1.0.jar b/test-harness/lib/jakarta.mail-api-2.1.0.jar
new file mode 100644
index 0000000..ae91f12
Binary files /dev/null and b/test-harness/lib/jakarta.mail-api-2.1.0.jar differ
diff --git a/test-harness/lib/jakarta.ws.rs-api-3.0.0.jar b/test-harness/lib/jakarta.ws.rs-api-3.0.0.jar
new file mode 100644
index 0000000..31f2596
Binary files /dev/null and b/test-harness/lib/jakarta.ws.rs-api-3.0.0.jar differ
diff --git a/test-harness/lib/jakarta.xml.bind-api-3.0.1.jar b/test-harness/lib/jakarta.xml.bind-api-3.0.1.jar
new file mode 100644
index 0000000..f890cba
Binary files /dev/null and b/test-harness/lib/jakarta.xml.bind-api-3.0.1.jar differ
diff --git a/test-harness/lib/jakarta.xml.soap-api-3.0.1.jar b/test-harness/lib/jakarta.xml.soap-api-3.0.1.jar
new file mode 100644
index 0000000..7a9e6da
Binary files /dev/null and b/test-harness/lib/jakarta.xml.soap-api-3.0.1.jar differ
diff --git a/test-harness/lib/jakarta.xml.ws-api-3.0.1.jar b/test-harness/lib/jakarta.xml.ws-api-3.0.1.jar
new file mode 100644
index 0000000..f27aa1b
Binary files /dev/null and b/test-harness/lib/jakarta.xml.ws-api-3.0.1.jar differ
diff --git a/test-harness/lib/jasypt-1.9.3.jar b/test-harness/lib/jasypt-1.9.3.jar
new file mode 100644
index 0000000..f4c4606
Binary files /dev/null and b/test-harness/lib/jasypt-1.9.3.jar differ
diff --git a/test-harness/lib/java-client-8.6.0.jar b/test-harness/lib/java-client-8.6.0.jar
new file mode 100644
index 0000000..b88a760
Binary files /dev/null and b/test-harness/lib/java-client-8.6.0.jar differ
diff --git a/test-harness/lib/java-support-8.4.0.jar b/test-harness/lib/java-support-8.4.0.jar
new file mode 100644
index 0000000..53c7638
Binary files /dev/null and b/test-harness/lib/java-support-8.4.0.jar differ
diff --git a/test-harness/lib/javassist-3.29.2-GA.jar b/test-harness/lib/javassist-3.29.2-GA.jar
new file mode 100644
index 0000000..68fc301
Binary files /dev/null and b/test-harness/lib/javassist-3.29.2-GA.jar differ
diff --git a/test-harness/lib/jaxb-core-3.0.0-M5.jar b/test-harness/lib/jaxb-core-3.0.0-M5.jar
new file mode 100644
index 0000000..eb823a8
Binary files /dev/null and b/test-harness/lib/jaxb-core-3.0.0-M5.jar differ
diff --git a/test-harness/lib/jaxb-runtime-3.0.0-M5.jar b/test-harness/lib/jaxb-runtime-3.0.0-M5.jar
new file mode 100644
index 0000000..d45e2ff
Binary files /dev/null and b/test-harness/lib/jaxb-runtime-3.0.0-M5.jar differ
diff --git a/test-harness/lib/jersey-client-3.0.12.jar b/test-harness/lib/jersey-client-3.0.12.jar
new file mode 100644
index 0000000..934a76e
Binary files /dev/null and b/test-harness/lib/jersey-client-3.0.12.jar differ
diff --git a/test-harness/lib/jersey-common-3.0.12.jar b/test-harness/lib/jersey-common-3.0.12.jar
new file mode 100644
index 0000000..e4198b0
Binary files /dev/null and b/test-harness/lib/jersey-common-3.0.12.jar differ
diff --git a/test-harness/lib/jersey-entity-filtering-3.0.12.jar b/test-harness/lib/jersey-entity-filtering-3.0.12.jar
new file mode 100644
index 0000000..fa4a122
Binary files /dev/null and b/test-harness/lib/jersey-entity-filtering-3.0.12.jar differ
diff --git a/test-harness/lib/jersey-hk2-3.0.12.jar b/test-harness/lib/jersey-hk2-3.0.12.jar
new file mode 100644
index 0000000..5960a0d
Binary files /dev/null and b/test-harness/lib/jersey-hk2-3.0.12.jar differ
diff --git a/test-harness/lib/jersey-media-json-jackson-3.0.12.jar b/test-harness/lib/jersey-media-json-jackson-3.0.12.jar
new file mode 100644
index 0000000..0fd98cb
Binary files /dev/null and b/test-harness/lib/jersey-media-json-jackson-3.0.12.jar differ
diff --git a/test-harness/lib/jersey-media-multipart-3.0.12.jar b/test-harness/lib/jersey-media-multipart-3.0.12.jar
new file mode 100644
index 0000000..b4b4dea
Binary files /dev/null and b/test-harness/lib/jersey-media-multipart-3.0.12.jar differ
diff --git a/test-harness/lib/jooq-3.1.0.jar b/test-harness/lib/jooq-3.1.0.jar
new file mode 100644
index 0000000..14fac2d
Binary files /dev/null and b/test-harness/lib/jooq-3.1.0.jar differ
diff --git a/test-harness/lib/json-path-2.9.0.jar b/test-harness/lib/json-path-2.9.0.jar
new file mode 100644
index 0000000..f731408
Binary files /dev/null and b/test-harness/lib/json-path-2.9.0.jar differ
diff --git a/test-harness/lib/json-smart-2.5.0.jar b/test-harness/lib/json-smart-2.5.0.jar
new file mode 100644
index 0000000..2685e03
Binary files /dev/null and b/test-harness/lib/json-smart-2.5.0.jar differ
diff --git a/test-harness/lib/json-unit-assertj-3.2.7.jar b/test-harness/lib/json-unit-assertj-3.2.7.jar
new file mode 100644
index 0000000..4a11797
Binary files /dev/null and b/test-harness/lib/json-unit-assertj-3.2.7.jar differ
diff --git a/test-harness/lib/json-unit-core-3.2.7.jar b/test-harness/lib/json-unit-core-3.2.7.jar
new file mode 100644
index 0000000..631f8b8
Binary files /dev/null and b/test-harness/lib/json-unit-core-3.2.7.jar differ
diff --git a/test-harness/lib/json-unit-json-path-3.2.7.jar b/test-harness/lib/json-unit-json-path-3.2.7.jar
new file mode 100644
index 0000000..21dc4df
Binary files /dev/null and b/test-harness/lib/json-unit-json-path-3.2.7.jar differ
diff --git a/test-harness/lib/jsr305-3.0.2.jar b/test-harness/lib/jsr305-3.0.2.jar
new file mode 100644
index 0000000..59222d9
Binary files /dev/null and b/test-harness/lib/jsr305-3.0.2.jar differ
diff --git a/test-harness/lib/junit-4.12.jar b/test-harness/lib/junit-4.12.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/test-harness/lib/junit-4.12.jar differ
diff --git a/test-harness/lib/junit-jupiter-api-5.5.1.jar b/test-harness/lib/junit-jupiter-api-5.5.1.jar
new file mode 100644
index 0000000..8873cb4
Binary files /dev/null and b/test-harness/lib/junit-jupiter-api-5.5.1.jar differ
diff --git a/test-harness/lib/junit-jupiter-engine-5.5.1.jar b/test-harness/lib/junit-jupiter-engine-5.5.1.jar
new file mode 100644
index 0000000..f6f4218
Binary files /dev/null and b/test-harness/lib/junit-jupiter-engine-5.5.1.jar differ
diff --git a/test-harness/lib/junit-jupiter-params-5.5.1.jar b/test-harness/lib/junit-jupiter-params-5.5.1.jar
new file mode 100644
index 0000000..ed32475
Binary files /dev/null and b/test-harness/lib/junit-jupiter-params-5.5.1.jar differ
diff --git a/test-harness/lib/junit-platform-commons-1.5.1.jar b/test-harness/lib/junit-platform-commons-1.5.1.jar
new file mode 100644
index 0000000..f941792
Binary files /dev/null and b/test-harness/lib/junit-platform-commons-1.5.1.jar differ
diff --git a/test-harness/lib/junit-platform-engine-1.5.1.jar b/test-harness/lib/junit-platform-engine-1.5.1.jar
new file mode 100644
index 0000000..34d024f
Binary files /dev/null and b/test-harness/lib/junit-platform-engine-1.5.1.jar differ
diff --git a/test-harness/lib/junit-platform-launcher-1.5.1.jar b/test-harness/lib/junit-platform-launcher-1.5.1.jar
new file mode 100644
index 0000000..829fa2c
Binary files /dev/null and b/test-harness/lib/junit-platform-launcher-1.5.1.jar differ
diff --git a/test-harness/lib/junit-platform-runner-1.5.1.jar b/test-harness/lib/junit-platform-runner-1.5.1.jar
new file mode 100644
index 0000000..9e29b1a
Binary files /dev/null and b/test-harness/lib/junit-platform-runner-1.5.1.jar differ
diff --git a/test-harness/lib/junit-platform-suite-api-1.5.1.jar b/test-harness/lib/junit-platform-suite-api-1.5.1.jar
new file mode 100644
index 0000000..7056e86
Binary files /dev/null and b/test-harness/lib/junit-platform-suite-api-1.5.1.jar differ
diff --git a/test-harness/lib/junit-vintage-engine-5.5.1.jar b/test-harness/lib/junit-vintage-engine-5.5.1.jar
new file mode 100644
index 0000000..3022219
Binary files /dev/null and b/test-harness/lib/junit-vintage-engine-5.5.1.jar differ
diff --git a/test-harness/lib/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar b/test-harness/lib/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar
new file mode 100644
index 0000000..45832c0
Binary files /dev/null and b/test-harness/lib/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar differ
diff --git a/test-harness/lib/log4j-api-2.17.1.jar b/test-harness/lib/log4j-api-2.17.1.jar
new file mode 100644
index 0000000..605c45d
Binary files /dev/null and b/test-harness/lib/log4j-api-2.17.1.jar differ
diff --git a/test-harness/lib/log4j-core-2.17.1.jar b/test-harness/lib/log4j-core-2.17.1.jar
new file mode 100644
index 0000000..bbead12
Binary files /dev/null and b/test-harness/lib/log4j-core-2.17.1.jar differ
diff --git a/test-harness/lib/log4j-iostreams-2.17.1.jar b/test-harness/lib/log4j-iostreams-2.17.1.jar
new file mode 100644
index 0000000..10b6db7
Binary files /dev/null and b/test-harness/lib/log4j-iostreams-2.17.1.jar differ
diff --git a/test-harness/lib/log4j-jul-2.17.1.jar b/test-harness/lib/log4j-jul-2.17.1.jar
new file mode 100644
index 0000000..bab94c2
Binary files /dev/null and b/test-harness/lib/log4j-jul-2.17.1.jar differ
diff --git a/test-harness/lib/metrics-core-4.2.15.jar b/test-harness/lib/metrics-core-4.2.15.jar
new file mode 100644
index 0000000..cbeed55
Binary files /dev/null and b/test-harness/lib/metrics-core-4.2.15.jar differ
diff --git a/test-harness/lib/mimepull-1.9.11.jar b/test-harness/lib/mimepull-1.9.11.jar
new file mode 100644
index 0000000..99a4aba
Binary files /dev/null and b/test-harness/lib/mimepull-1.9.11.jar differ
diff --git a/test-harness/lib/mssql-jdbc-8.2.0.jre8.jar b/test-harness/lib/mssql-jdbc-8.2.0.jre8.jar
new file mode 100644
index 0000000..9b9f025
Binary files /dev/null and b/test-harness/lib/mssql-jdbc-8.2.0.jre8.jar differ
diff --git a/test-harness/lib/neethi-3.2.0.jar b/test-harness/lib/neethi-3.2.0.jar
new file mode 100644
index 0000000..840bf4f
Binary files /dev/null and b/test-harness/lib/neethi-3.2.0.jar differ
diff --git a/test-harness/lib/netty-buffer-4.1.96.Final.jar b/test-harness/lib/netty-buffer-4.1.96.Final.jar
new file mode 100644
index 0000000..354486b
Binary files /dev/null and b/test-harness/lib/netty-buffer-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-codec-4.1.96.Final.jar b/test-harness/lib/netty-codec-4.1.96.Final.jar
new file mode 100644
index 0000000..7101971
Binary files /dev/null and b/test-harness/lib/netty-codec-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-codec-http-4.1.96.Final.jar b/test-harness/lib/netty-codec-http-4.1.96.Final.jar
new file mode 100644
index 0000000..e598d2d
Binary files /dev/null and b/test-harness/lib/netty-codec-http-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-codec-socks-4.1.60.Final.jar b/test-harness/lib/netty-codec-socks-4.1.60.Final.jar
new file mode 100644
index 0000000..6b94d39
Binary files /dev/null and b/test-harness/lib/netty-codec-socks-4.1.60.Final.jar differ
diff --git a/test-harness/lib/netty-common-4.1.96.Final.jar b/test-harness/lib/netty-common-4.1.96.Final.jar
new file mode 100644
index 0000000..c9eff76
Binary files /dev/null and b/test-harness/lib/netty-common-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-handler-4.1.96.Final.jar b/test-harness/lib/netty-handler-4.1.96.Final.jar
new file mode 100644
index 0000000..ec36faa
Binary files /dev/null and b/test-harness/lib/netty-handler-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-handler-proxy-4.1.60.Final.jar b/test-harness/lib/netty-handler-proxy-4.1.60.Final.jar
new file mode 100644
index 0000000..b4be3c7
Binary files /dev/null and b/test-harness/lib/netty-handler-proxy-4.1.60.Final.jar differ
diff --git a/test-harness/lib/netty-reactive-streams-2.0.4.jar b/test-harness/lib/netty-reactive-streams-2.0.4.jar
new file mode 100644
index 0000000..38b5efa
Binary files /dev/null and b/test-harness/lib/netty-reactive-streams-2.0.4.jar differ
diff --git a/test-harness/lib/netty-resolver-4.1.96.Final.jar b/test-harness/lib/netty-resolver-4.1.96.Final.jar
new file mode 100644
index 0000000..29e08ed
Binary files /dev/null and b/test-harness/lib/netty-resolver-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-4.1.96.Final.jar b/test-harness/lib/netty-transport-4.1.96.Final.jar
new file mode 100644
index 0000000..ddf8deb
Binary files /dev/null and b/test-harness/lib/netty-transport-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-classes-epoll-4.1.96.Final.jar b/test-harness/lib/netty-transport-classes-epoll-4.1.96.Final.jar
new file mode 100644
index 0000000..dad7911
Binary files /dev/null and b/test-harness/lib/netty-transport-classes-epoll-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-classes-kqueue-4.1.96.Final.jar b/test-harness/lib/netty-transport-classes-kqueue-4.1.96.Final.jar
new file mode 100644
index 0000000..a5f6a8e
Binary files /dev/null and b/test-harness/lib/netty-transport-classes-kqueue-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-native-epoll-4.1.60.Final-linux-x86_64.jar b/test-harness/lib/netty-transport-native-epoll-4.1.60.Final-linux-x86_64.jar
new file mode 100644
index 0000000..c3f2fed
Binary files /dev/null and b/test-harness/lib/netty-transport-native-epoll-4.1.60.Final-linux-x86_64.jar differ
diff --git a/test-harness/lib/netty-transport-native-epoll-4.1.96.Final.jar b/test-harness/lib/netty-transport-native-epoll-4.1.96.Final.jar
new file mode 100644
index 0000000..3bace84
Binary files /dev/null and b/test-harness/lib/netty-transport-native-epoll-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-native-kqueue-4.1.60.Final-osx-x86_64.jar b/test-harness/lib/netty-transport-native-kqueue-4.1.60.Final-osx-x86_64.jar
new file mode 100644
index 0000000..7cf26a0
Binary files /dev/null and b/test-harness/lib/netty-transport-native-kqueue-4.1.60.Final-osx-x86_64.jar differ
diff --git a/test-harness/lib/netty-transport-native-kqueue-4.1.96.Final.jar b/test-harness/lib/netty-transport-native-kqueue-4.1.96.Final.jar
new file mode 100644
index 0000000..b6eb351
Binary files /dev/null and b/test-harness/lib/netty-transport-native-kqueue-4.1.96.Final.jar differ
diff --git a/test-harness/lib/netty-transport-native-unix-common-4.1.96.Final.jar b/test-harness/lib/netty-transport-native-unix-common-4.1.96.Final.jar
new file mode 100644
index 0000000..f8dfc95
Binary files /dev/null and b/test-harness/lib/netty-transport-native-unix-common-4.1.96.Final.jar differ
diff --git a/test-harness/lib/ojdbc8-12.2.0.1.jar b/test-harness/lib/ojdbc8-12.2.0.1.jar
new file mode 100644
index 0000000..bf41243
Binary files /dev/null and b/test-harness/lib/ojdbc8-12.2.0.1.jar differ
diff --git a/test-harness/lib/opensaml-core-4.3.0.jar b/test-harness/lib/opensaml-core-4.3.0.jar
new file mode 100644
index 0000000..4efa3aa
Binary files /dev/null and b/test-harness/lib/opensaml-core-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-profile-api-4.3.0.jar b/test-harness/lib/opensaml-profile-api-4.3.0.jar
new file mode 100644
index 0000000..9080204
Binary files /dev/null and b/test-harness/lib/opensaml-profile-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-saml-api-4.3.0.jar b/test-harness/lib/opensaml-saml-api-4.3.0.jar
new file mode 100644
index 0000000..8c2d899
Binary files /dev/null and b/test-harness/lib/opensaml-saml-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-saml-impl-4.3.0.jar b/test-harness/lib/opensaml-saml-impl-4.3.0.jar
new file mode 100644
index 0000000..003b27f
Binary files /dev/null and b/test-harness/lib/opensaml-saml-impl-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-security-api-4.3.0.jar b/test-harness/lib/opensaml-security-api-4.3.0.jar
new file mode 100644
index 0000000..6c935e2
Binary files /dev/null and b/test-harness/lib/opensaml-security-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-security-impl-4.3.0.jar b/test-harness/lib/opensaml-security-impl-4.3.0.jar
new file mode 100644
index 0000000..6038fc1
Binary files /dev/null and b/test-harness/lib/opensaml-security-impl-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-soap-api-4.3.0.jar b/test-harness/lib/opensaml-soap-api-4.3.0.jar
new file mode 100644
index 0000000..23f9032
Binary files /dev/null and b/test-harness/lib/opensaml-soap-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xacml-api-4.3.0.jar b/test-harness/lib/opensaml-xacml-api-4.3.0.jar
new file mode 100644
index 0000000..430de78
Binary files /dev/null and b/test-harness/lib/opensaml-xacml-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xacml-impl-4.3.0.jar b/test-harness/lib/opensaml-xacml-impl-4.3.0.jar
new file mode 100644
index 0000000..f70dc31
Binary files /dev/null and b/test-harness/lib/opensaml-xacml-impl-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xacml-saml-api-4.3.0.jar b/test-harness/lib/opensaml-xacml-saml-api-4.3.0.jar
new file mode 100644
index 0000000..bf51757
Binary files /dev/null and b/test-harness/lib/opensaml-xacml-saml-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xacml-saml-impl-4.3.0.jar b/test-harness/lib/opensaml-xacml-saml-impl-4.3.0.jar
new file mode 100644
index 0000000..1e87b9d
Binary files /dev/null and b/test-harness/lib/opensaml-xacml-saml-impl-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xmlsec-api-4.3.0.jar b/test-harness/lib/opensaml-xmlsec-api-4.3.0.jar
new file mode 100644
index 0000000..05d0daf
Binary files /dev/null and b/test-harness/lib/opensaml-xmlsec-api-4.3.0.jar differ
diff --git a/test-harness/lib/opensaml-xmlsec-impl-4.3.0.jar b/test-harness/lib/opensaml-xmlsec-impl-4.3.0.jar
new file mode 100644
index 0000000..10d49b9
Binary files /dev/null and b/test-harness/lib/opensaml-xmlsec-impl-4.3.0.jar differ
diff --git a/test-harness/lib/opentelemetry-api-1.28.0.jar b/test-harness/lib/opentelemetry-api-1.28.0.jar
new file mode 100644
index 0000000..9a80508
Binary files /dev/null and b/test-harness/lib/opentelemetry-api-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-api-events-1.28.0-alpha.jar b/test-harness/lib/opentelemetry-api-events-1.28.0-alpha.jar
new file mode 100644
index 0000000..993a1de
Binary files /dev/null and b/test-harness/lib/opentelemetry-api-events-1.28.0-alpha.jar differ
diff --git a/test-harness/lib/opentelemetry-context-1.28.0.jar b/test-harness/lib/opentelemetry-context-1.28.0.jar
new file mode 100644
index 0000000..2d1a0a4
Binary files /dev/null and b/test-harness/lib/opentelemetry-context-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-exporter-logging-1.28.0.jar b/test-harness/lib/opentelemetry-exporter-logging-1.28.0.jar
new file mode 100644
index 0000000..935d3c2
Binary files /dev/null and b/test-harness/lib/opentelemetry-exporter-logging-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-extension-incubator-1.28.0-alpha.jar b/test-harness/lib/opentelemetry-extension-incubator-1.28.0-alpha.jar
new file mode 100644
index 0000000..61e6ac0
Binary files /dev/null and b/test-harness/lib/opentelemetry-extension-incubator-1.28.0-alpha.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-1.28.0.jar
new file mode 100644
index 0000000..a65263e
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-common-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-common-1.28.0.jar
new file mode 100644
index 0000000..7b021ff
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-common-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-1.28.0.jar
new file mode 100644
index 0000000..976a0ef
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-spi-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-spi-1.28.0.jar
new file mode 100644
index 0000000..31089ee
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-extension-autoconfigure-spi-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-logs-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-logs-1.28.0.jar
new file mode 100644
index 0000000..bb13094
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-logs-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-metrics-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-metrics-1.28.0.jar
new file mode 100644
index 0000000..d42bfdb
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-metrics-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-sdk-trace-1.28.0.jar b/test-harness/lib/opentelemetry-sdk-trace-1.28.0.jar
new file mode 100644
index 0000000..b7963c8
Binary files /dev/null and b/test-harness/lib/opentelemetry-sdk-trace-1.28.0.jar differ
diff --git a/test-harness/lib/opentelemetry-semconv-1.28.0-alpha.jar b/test-harness/lib/opentelemetry-semconv-1.28.0-alpha.jar
new file mode 100644
index 0000000..b293fe4
Binary files /dev/null and b/test-harness/lib/opentelemetry-semconv-1.28.0-alpha.jar differ
diff --git a/test-harness/lib/opentest4j-1.2.0.jar b/test-harness/lib/opentest4j-1.2.0.jar
new file mode 100644
index 0000000..d500636
Binary files /dev/null and b/test-harness/lib/opentest4j-1.2.0.jar differ
diff --git a/test-harness/lib/osgi-resource-locator-1.0.3.jar b/test-harness/lib/osgi-resource-locator-1.0.3.jar
new file mode 100644
index 0000000..0f3c386
Binary files /dev/null and b/test-harness/lib/osgi-resource-locator-1.0.3.jar differ
diff --git a/test-harness/lib/postgresql-42.7.1.jar b/test-harness/lib/postgresql-42.7.1.jar
new file mode 100644
index 0000000..18331b7
Binary files /dev/null and b/test-harness/lib/postgresql-42.7.1.jar differ
diff --git a/test-harness/lib/reactive-streams-1.0.3.jar b/test-harness/lib/reactive-streams-1.0.3.jar
new file mode 100644
index 0000000..b9b487c
Binary files /dev/null and b/test-harness/lib/reactive-streams-1.0.3.jar differ
diff --git a/test-harness/lib/robotil-1.10.jar b/test-harness/lib/robotil-1.10.jar
new file mode 100644
index 0000000..40afd6a
Binary files /dev/null and b/test-harness/lib/robotil-1.10.jar differ
diff --git a/test-harness/lib/saaj-impl-2.0.1.jar b/test-harness/lib/saaj-impl-2.0.1.jar
new file mode 100644
index 0000000..568ae52
Binary files /dev/null and b/test-harness/lib/saaj-impl-2.0.1.jar differ
diff --git a/test-harness/lib/selenium-api-4.13.0.jar b/test-harness/lib/selenium-api-4.13.0.jar
new file mode 100644
index 0000000..d7a9145
Binary files /dev/null and b/test-harness/lib/selenium-api-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-chrome-driver-4.13.0.jar b/test-harness/lib/selenium-chrome-driver-4.13.0.jar
new file mode 100644
index 0000000..21db8a6
Binary files /dev/null and b/test-harness/lib/selenium-chrome-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-chromium-driver-4.13.0.jar b/test-harness/lib/selenium-chromium-driver-4.13.0.jar
new file mode 100644
index 0000000..2be7b0c
Binary files /dev/null and b/test-harness/lib/selenium-chromium-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-devtools-v115-4.13.0.jar b/test-harness/lib/selenium-devtools-v115-4.13.0.jar
new file mode 100644
index 0000000..f891df1
Binary files /dev/null and b/test-harness/lib/selenium-devtools-v115-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-devtools-v116-4.13.0.jar b/test-harness/lib/selenium-devtools-v116-4.13.0.jar
new file mode 100644
index 0000000..89f5c41
Binary files /dev/null and b/test-harness/lib/selenium-devtools-v116-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-devtools-v117-4.13.0.jar b/test-harness/lib/selenium-devtools-v117-4.13.0.jar
new file mode 100644
index 0000000..b7653c9
Binary files /dev/null and b/test-harness/lib/selenium-devtools-v117-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-devtools-v85-4.13.0.jar b/test-harness/lib/selenium-devtools-v85-4.13.0.jar
new file mode 100644
index 0000000..6960a4e
Binary files /dev/null and b/test-harness/lib/selenium-devtools-v85-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-edge-driver-4.13.0.jar b/test-harness/lib/selenium-edge-driver-4.13.0.jar
new file mode 100644
index 0000000..10612f9
Binary files /dev/null and b/test-harness/lib/selenium-edge-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-firefox-driver-4.13.0.jar b/test-harness/lib/selenium-firefox-driver-4.13.0.jar
new file mode 100644
index 0000000..3351da9
Binary files /dev/null and b/test-harness/lib/selenium-firefox-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-http-4.13.0.jar b/test-harness/lib/selenium-http-4.13.0.jar
new file mode 100644
index 0000000..7198845
Binary files /dev/null and b/test-harness/lib/selenium-http-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-ie-driver-4.13.0.jar b/test-harness/lib/selenium-ie-driver-4.13.0.jar
new file mode 100644
index 0000000..5453e27
Binary files /dev/null and b/test-harness/lib/selenium-ie-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-java-4.13.0.jar b/test-harness/lib/selenium-java-4.13.0.jar
new file mode 100644
index 0000000..a63e635
Binary files /dev/null and b/test-harness/lib/selenium-java-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-json-4.13.0.jar b/test-harness/lib/selenium-json-4.13.0.jar
new file mode 100644
index 0000000..98db5e7
Binary files /dev/null and b/test-harness/lib/selenium-json-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-manager-4.13.0.jar b/test-harness/lib/selenium-manager-4.13.0.jar
new file mode 100644
index 0000000..e276481
Binary files /dev/null and b/test-harness/lib/selenium-manager-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-os-4.13.0.jar b/test-harness/lib/selenium-os-4.13.0.jar
new file mode 100644
index 0000000..b8c94dc
Binary files /dev/null and b/test-harness/lib/selenium-os-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-remote-driver-4.13.0.jar b/test-harness/lib/selenium-remote-driver-4.13.0.jar
new file mode 100644
index 0000000..5a754b5
Binary files /dev/null and b/test-harness/lib/selenium-remote-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-safari-driver-4.13.0.jar b/test-harness/lib/selenium-safari-driver-4.13.0.jar
new file mode 100644
index 0000000..a4b7d14
Binary files /dev/null and b/test-harness/lib/selenium-safari-driver-4.13.0.jar differ
diff --git a/test-harness/lib/selenium-support-4.13.0.jar b/test-harness/lib/selenium-support-4.13.0.jar
new file mode 100644
index 0000000..0ce87c8
Binary files /dev/null and b/test-harness/lib/selenium-support-4.13.0.jar differ
diff --git a/test-harness/lib/slf4j-api-2.0.9.jar b/test-harness/lib/slf4j-api-2.0.9.jar
new file mode 100644
index 0000000..3796afe
Binary files /dev/null and b/test-harness/lib/slf4j-api-2.0.9.jar differ
diff --git a/test-harness/lib/stax-ex-2.0.1.jar b/test-harness/lib/stax-ex-2.0.1.jar
new file mode 100644
index 0000000..3df5bc3
Binary files /dev/null and b/test-harness/lib/stax-ex-2.0.1.jar differ
diff --git a/test-harness/lib/stax2-api-4.2.1.jar b/test-harness/lib/stax2-api-4.2.1.jar
new file mode 100644
index 0000000..28c6a08
Binary files /dev/null and b/test-harness/lib/stax2-api-4.2.1.jar differ
diff --git a/test-harness/lib/txw2-3.0.0-M5.jar b/test-harness/lib/txw2-3.0.0-M5.jar
new file mode 100644
index 0000000..86b329c
Binary files /dev/null and b/test-harness/lib/txw2-3.0.0-M5.jar differ
diff --git a/test-harness/lib/vault-java-driver-5.1.0.jar b/test-harness/lib/vault-java-driver-5.1.0.jar
new file mode 100644
index 0000000..4d82f21
Binary files /dev/null and b/test-harness/lib/vault-java-driver-5.1.0.jar differ
diff --git a/test-harness/lib/woodstox-core-6.5.1.jar b/test-harness/lib/woodstox-core-6.5.1.jar
new file mode 100644
index 0000000..b22b384
Binary files /dev/null and b/test-harness/lib/woodstox-core-6.5.1.jar differ
diff --git a/test-harness/lib/wsdl4j-1.6.3.jar b/test-harness/lib/wsdl4j-1.6.3.jar
new file mode 100644
index 0000000..b9c10b9
Binary files /dev/null and b/test-harness/lib/wsdl4j-1.6.3.jar differ
diff --git a/test-harness/lib/wss4j-bindings-3.0.1.jar b/test-harness/lib/wss4j-bindings-3.0.1.jar
new file mode 100644
index 0000000..75a1c00
Binary files /dev/null and b/test-harness/lib/wss4j-bindings-3.0.1.jar differ
diff --git a/test-harness/lib/wss4j-policy-3.0.1.jar b/test-harness/lib/wss4j-policy-3.0.1.jar
new file mode 100644
index 0000000..7a14e48
Binary files /dev/null and b/test-harness/lib/wss4j-policy-3.0.1.jar differ
diff --git a/test-harness/lib/wss4j-ws-security-common-3.0.1.jar b/test-harness/lib/wss4j-ws-security-common-3.0.1.jar
new file mode 100644
index 0000000..278f11f
Binary files /dev/null and b/test-harness/lib/wss4j-ws-security-common-3.0.1.jar differ
diff --git a/test-harness/lib/wss4j-ws-security-dom-3.0.1.jar b/test-harness/lib/wss4j-ws-security-dom-3.0.1.jar
new file mode 100644
index 0000000..7506570
Binary files /dev/null and b/test-harness/lib/wss4j-ws-security-dom-3.0.1.jar differ
diff --git a/test-harness/lib/wss4j-ws-security-policy-stax-3.0.1.jar b/test-harness/lib/wss4j-ws-security-policy-stax-3.0.1.jar
new file mode 100644
index 0000000..14e3edf
Binary files /dev/null and b/test-harness/lib/wss4j-ws-security-policy-stax-3.0.1.jar differ
diff --git a/test-harness/lib/wss4j-ws-security-stax-3.0.1.jar b/test-harness/lib/wss4j-ws-security-stax-3.0.1.jar
new file mode 100644
index 0000000..6ada399
Binary files /dev/null and b/test-harness/lib/wss4j-ws-security-stax-3.0.1.jar differ
diff --git a/test-harness/lib/xml-resolver-1.2.jar b/test-harness/lib/xml-resolver-1.2.jar
new file mode 100644
index 0000000..e535bdc
Binary files /dev/null and b/test-harness/lib/xml-resolver-1.2.jar differ
diff --git a/test-harness/lib/xmlschema-core-2.3.1.jar b/test-harness/lib/xmlschema-core-2.3.1.jar
new file mode 100644
index 0000000..2970160
Binary files /dev/null and b/test-harness/lib/xmlschema-core-2.3.1.jar differ
diff --git a/test-harness/lib/xmlsec-3.0.2.jar b/test-harness/lib/xmlsec-3.0.2.jar
new file mode 100644
index 0000000..aca0482
Binary files /dev/null and b/test-harness/lib/xmlsec-3.0.2.jar differ
diff --git a/test-harness/pom.xml b/test-harness/pom.xml
new file mode 100644
index 0000000..1aa0937
--- /dev/null
+++ b/test-harness/pom.xml
@@ -0,0 +1,532 @@
+
+
+ 4.0.0
+
+ cz.moneta.test
+ harness
+ 7.55-SNAPSHOT
+
+
+ UTF-8
+ 5.5.1
+ 1.5.1
+ 4.13.0
+ 3.0.12
+ 1.9.11
+ 2.16.1
+ 12.2.0.1
+ 8.2.0.jre8
+ 42.7.1
+ 3.7
+ 1.4
+ 2.6
+ 3.1.0
+ 5.1.0
+ 2.17.1
+ 8.6.0
+ 1.9.3
+ 1.6
+ 4.0.3
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.jupiter.version}
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.jupiter.version}
+
+
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${junit.jupiter.version}
+
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+
+
+
+ org.junit.platform
+ junit-platform-suite-api
+ ${junit.platform.version}
+
+
+
+ org.seleniumhq.selenium
+ selenium-java
+ ${selenium.version}
+
+
+
+ com.google.code.gson
+ gson
+ 2.11.0
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.14
+
+
+
+ com.oracle.jdbc
+ ojdbc8
+ ${oracle.driver.version}
+
+
+
+ com.microsoft.sqlserver
+ mssql-jdbc
+ ${microsoft.sql.driver.version}
+
+
+
+ org.postgresql
+ postgresql
+ ${postgres.driver.version}
+
+
+
+ org.glassfish.jersey.core
+ jersey-client
+ ${jersey.version}
+
+
+
+ org.glassfish.jersey.inject
+ jersey-hk2
+ ${jersey.version}
+
+
+
+ org.glassfish.jersey.core
+ jersey-common
+ ${jersey.version}
+
+
+
+ org.jvnet.mimepull
+ mimepull
+ ${mimepull.version}
+
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+ ${jersey.version}
+
+
+
+ org.glassfish.jersey.media
+ jersey-media-multipart
+ ${jersey.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-csv
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+ ${jackson-modules.version}
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ ${jackson-modules.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+ org.apache.commons
+ commons-text
+ ${commons-text.version}
+
+
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
+
+
+ commons-beanutils
+ commons-beanutils
+ ${commons-beanutils.version}
+
+
+
+ commons-configuration
+ commons-configuration
+ ${commons-configuration.version}
+
+
+
+ org.jooq
+ jooq
+ ${jooq.version}
+
+
+
+ com.bettercloud
+ vault-java-driver
+ ${vault-java-driver.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-jul
+ ${log4j.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-iostreams
+ ${log4j.version}
+
+
+
+ io.appium
+ java-client
+ ${appium-java-client.version}
+
+
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxws
+ ${cxf.version}
+
+
+
+ org.apache.cxf
+ cxf-rt-transports-http
+ ${cxf.version}
+
+
+
+ org.apache.cxf
+ cxf-rt-ws-security
+ ${cxf.version}
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
+
+
+
+
+ us.abstracta
+ dm3270-lib
+ 0.9.1
+
+
+
+
+ cz.moneta.ilods
+ ilods_shared_resources
+ 0.0.9
+
+
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+ 3.0.1
+
+
+ jakarta.xml.ws
+ jakarta.xml.ws-api
+ 3.0.1
+
+
+ jakarta.xml.soap
+ jakarta.xml.soap-api
+ 3.0.1
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+ 2.0.0
+
+
+ jakarta.jws
+ jakarta.jws-api
+ 3.0.0
+
+
+
+ com.codoid
+ robotil
+ 1.10
+
+
+
+
+ org.antlr
+ ST4
+ 4.3.4
+
+
+
+
+ net.javacrumbs.json-unit
+ json-unit-assertj
+ 3.2.7
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.9.0
+
+
+ install
+
+ copy-dependencies
+
+
+ ${project.build.directory}/lib
+ runtime
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+ 17
+ 17
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M4
+
+
+ HARNESS_CONFIG
+
+
+
+
+
+
+
+
+ Moneta Artifactory
+
+ true
+
+
+
+
+ false
+ true
+ central
+ libs-release
+ https://artifactory-aws.ux.mbid.cz/artifactory/libs-release
+
+
+ true
+ false
+ snapshots
+ libs-snapshot
+ https://artifactory-aws.ux.mbid.cz/artifactory/libs-snapshot
+
+
+
+
+ false
+ true
+ central
+ plugins-release
+ https://artifactory-aws.ux.mbid.cz/artifactory/plugins-release
+
+
+ true
+ false
+ snapshots
+ plugins-snapshot
+ https://artifactory-aws.ux.mbid.cz/artifactory/plugins-snapshot
+
+
+
+
+
+ cxf-cebia
+
+ false
+
+
+
+
+ org.apache.cxf
+ cxf-codegen-plugin
+ ${cxf.version}
+
+
+ generate-sources
+ generate-sources
+
+ ${basedir}/src/main/java
+
+
+ ${basedir}/src/main/resources/ws/IvaServiceActual.wsdl
+
+ -xjc-Xfluent-api
+
+
+ ${basedir}/src/main/resources/ws/jaxb-bindings.xml
+
+
+
+
+
+
+ wsdl2java
+
+
+
+
+
+ net.java.dev.jaxb2-commons
+ jaxb-fluent-api
+ 2.1.8
+
+
+
+
+
+
+
+ withDeps
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.6.0
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
+
+
+ Martin Petrus
+ martin.petrus@moneta.cz
+
+
+ Petr Fifka
+ petr.fifka1@moneta.cz
+
+
+ Georgij Boljuba
+ georgij@boljuba.com
+
+
+
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/HarnessJunit5Extension.java b/test-harness/src/main/java/cz/moneta/test/harness/HarnessJunit5Extension.java
new file mode 100644
index 0000000..0385b50
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/HarnessJunit5Extension.java
@@ -0,0 +1,418 @@
+package cz.moneta.test.harness;
+
+import cz.moneta.test.harness.annotations.*;
+import cz.moneta.test.harness.config.ConfigProvider;
+import cz.moneta.test.harness.constants.HarnessConfigConstants;
+import cz.moneta.test.harness.context.BaseStoreAccessor;
+import cz.moneta.test.harness.context.StoreAccessor;
+import cz.moneta.test.harness.data.Browser;
+import cz.moneta.test.harness.endpoints.Endpoint;
+import cz.moneta.test.harness.endpoints.MobileEndpoint;
+import cz.moneta.test.harness.endpoints.WebEndpoint;
+import cz.moneta.test.harness.endpoints.greenscreen.GreenScreenEndpoint;
+import cz.moneta.test.harness.endpoints.jira.JiraTestResultPublisher;
+import cz.moneta.test.harness.exception.BeforeAllHarnessException;
+import cz.moneta.test.harness.exception.HarnessConfigurationException;
+import cz.moneta.test.harness.exception.HarnessException;
+import cz.moneta.test.harness.support.auth.AuthSupport;
+import cz.moneta.test.harness.support.auth.Credentials;
+import cz.moneta.test.harness.support.auth.Key;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.extension.*;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+import org.junit.platform.engine.UniqueId;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class HarnessJunit5Extension implements BeforeAllCallback, BeforeEachCallback, ParameterResolver,
+ TestExecutionExceptionHandler, AfterEachCallback, ExecutionCondition, LifecycleMethodExecutionExceptionHandler,
+ BeforeTestExecutionCallback, AfterAllCallback {
+
+ private static final Logger logger = LogManager.getLogger(HarnessJunit5Extension.class);
+
+ public static final String ACTIVE_ENDPOINTS = "ACTIVE_ENDPOINTS";
+ private static final Namespace ENDPOINT_NAMESPACE = Namespace.create("ENDPOINT");
+ protected static final Namespace CONFIG_NAMESPACE = Namespace.create("CONFIG");
+ protected static final Namespace GENERATORS_NAMESPACE = Namespace.create("GENERATORS");
+
+ private static final String RESOLUTION_PASS = "PASS";
+ private static final String RESOLUTION_FAIL = "FAIL";
+
+ private static final String BROWSER_CONFIG_KEY = "browser";
+
+ private boolean hasBeforeAllFail = false;
+ private String beforeAllThrowableMessage;
+
+ @Override
+ public void beforeAll(ExtensionContext extensionContext) {
+ extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet>());
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext extensionContext) {
+ extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet>());
+ extensionContext.getStore(ExtensionContext.Namespace.create("UPLOADS")).remove("jira.uploads");
+ }
+
+ @Override
+ public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
+ return Stream.>>of(
+ () -> getTestContext(parameterContext),
+ () -> getAuthKey(parameterContext))
+ .map(Supplier::get)
+ .anyMatch(Optional::isPresent);
+ }
+
+ @Override
+ public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
+ storeCurrentTestName(extensionContext);
+
+ return Stream.>>of(
+ () -> getTestContext(parameterContext).map(tc -> {
+ try {
+ return tc.getType().getConstructor(Store.class, Store.class, Store.class, Store.class, Store.class)
+ .newInstance(
+ extensionContext.getRoot().getStore(Namespace.GLOBAL),
+ extensionContext.getStore(Namespace.GLOBAL),
+ extensionContext.getStore(ENDPOINT_NAMESPACE),
+ extensionContext.getStore(CONFIG_NAMESPACE),
+ extensionContext.getStore(GENERATORS_NAMESPACE));
+ } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+ throw new ParameterResolutionException("Failed to initialize parameter " +
+ parameterContext.getParameter().getType().getSimpleName(), e);
+ }
+ }),
+ () -> getAuthKey(parameterContext)
+ .map(ak -> ak.getAnnotation(Key.class).value())
+ .map(ak -> AuthSupport.getCredentials(ak, new BaseStoreAccessor(
+ extensionContext.getRoot().getStore(Namespace.GLOBAL),
+ extensionContext.getStore(Namespace.GLOBAL),
+ extensionContext.getStore(ENDPOINT_NAMESPACE),
+ extensionContext.getStore(CONFIG_NAMESPACE),
+ extensionContext.getStore(GENERATORS_NAMESPACE)) {
+ })))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .orElseThrow(() -> new ParameterResolutionException("Parameter of type " +
+ parameterContext.getParameter().getType().getSimpleName() +
+ " is not supported"));
+ }
+
+ @Override
+ public void handleTestExecutionException(ExtensionContext extensionContext, Throwable throwable) throws Throwable {
+ processException(extensionContext);
+
+ throw Optional.of(throwable)
+ .filter(HarnessException.class::isInstance)
+ .map(AssertionError::new)
+ .map(Throwable.class::cast)
+ .orElse(throwable);
+ }
+
+ @Override
+ public void handleBeforeAllMethodExecutionException(ExtensionContext extensionContext, Throwable throwable) {
+ //This is workaround for possible bug in JUnit5 and Surefire - in case of beforeAll exception are test skipped instead of failed
+ //https://github.com/junit-team/junit5/issues/2178
+ hasBeforeAllFail = true;
+ beforeAllThrowableMessage = throwable.toString();
+
+ processException(extensionContext);
+ JiraTestResultPublisher.logJiraTestResult(extensionContext, throwable);
+ logger.error("Method @BeforeAll in test class {} failed with exception: {}.", () -> extensionContext.getRequiredTestClass().getName(), () -> beforeAllThrowableMessage);
+ }
+
+ @Override
+ public void beforeTestExecution(ExtensionContext extensionContext) {
+ if (hasBeforeAllFail) {
+ throw new BeforeAllHarnessException("Exception in before all occurred. Original exception message: " + beforeAllThrowableMessage);
+ }
+ }
+
+ private void processException(ExtensionContext extensionContext) {
+ Set files = processWebEndpoints(extensionContext);
+ processMobileEndpoints(extensionContext);
+ processGreenscreenEndpoints(extensionContext);
+
+ Store uploads = extensionContext.getStore(Namespace.create("UPLOADS"));
+ uploads.put("jira.uploads", files);
+ }
+
+ private void processGreenscreenEndpoints(ExtensionContext extensionContext) {
+ getActiveEndpoints(extensionContext).stream()
+ .filter(p -> GreenScreenEndpoint.class.isAssignableFrom(p.getLeft()))
+ .map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, GreenScreenEndpoint.class))
+ .forEach(e -> logger.info("Failed on screen:\n" + e.getText(1, 1, 0)));
+ }
+
+ private void processMobileEndpoints(ExtensionContext extensionContext) {
+ getActiveEndpoints(extensionContext).stream()
+ .filter(p -> MobileEndpoint.class.isAssignableFrom(p.getLeft()))
+ .map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, MobileEndpoint.class))
+ .forEach(mobileEndpoint -> {
+ mobileEndpoint.takeSnapshot(extensionContext.getDisplayName());
+ mobileEndpoint.saveSources(extensionContext.getDisplayName());
+ });
+ }
+
+ private Set processWebEndpoints(ExtensionContext extensionContext) {
+ return getActiveEndpoints(extensionContext).stream()
+ .filter(p -> WebEndpoint.class.isAssignableFrom(p.getLeft()))
+ .map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, WebEndpoint.class))
+ .map(webEndpoint -> {
+ Set files = new HashSet<>();
+ String filePrefix = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
+ files.add(webEndpoint.takeSnapshot(filePrefix));
+ files.addAll(webEndpoint.captureLogs(filePrefix));
+ webEndpoint.captureDom(filePrefix);
+ return files;
+ })
+ .filter(set -> !set.isEmpty())
+ .collect(HashSet::new, Set::addAll, Set::addAll);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Set, Object[]>> getActiveEndpoints(ExtensionContext extensionContext) {
+ return extensionContext.getStore(ENDPOINT_NAMESPACE).get(ACTIVE_ENDPOINTS, Set.class);
+ }
+
+ private void closeActiveEndpoints(ExtensionContext extensionContext) {
+ getActiveEndpoints(extensionContext).stream()
+ .filter(Objects::nonNull)
+ .map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).remove(c, Endpoint.class))
+ .peek(e -> {
+ if (e instanceof MobileEndpoint) {
+ ((MobileEndpoint) e).captureVideo(extensionContext.getDisplayName());
+ }
+ })
+ .forEach(Endpoint::close);
+ }
+
+ @Override
+ public void afterEach(ExtensionContext extensionContext) {
+ closeActiveEndpoints(extensionContext);
+ if (!hasBeforeAllFail) {
+ JiraTestResultPublisher.logJiraTestResult(extensionContext);
+ }
+ logTestResult(extensionContext);
+ }
+
+ @Override
+ public void afterAll(ExtensionContext extensionContext) {
+ //Closes only endpoints initialized in BeforeAll and not used in test methods
+ closeActiveEndpoints(extensionContext);
+ }
+
+ private Optional getAuthKey(ParameterContext parameterContext) {
+ return Optional.of(parameterContext.getParameter())
+ .filter(p -> p.getAnnotation(Key.class) != null)
+ .filter(p -> Credentials.class.isAssignableFrom(p.getType()));
+ }
+
+ private Optional getTestContext(ParameterContext parameterContext) {
+ return Optional.of(parameterContext.getParameter())
+ .filter(p -> p.getType().getAnnotation(TestContext.class) != null)
+ .filter(p -> StoreAccessor.class.isAssignableFrom(p.getType()));
+ }
+
+ @Override
+ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
+ if (isClassExtensionContext(context)) {
+ return evaluateClassExecutionContext(context);
+ }
+
+ Environment environment = getCurrentEnvironment(context);
+ return context.getTestMethod()
+ .filter(m -> environment != null)
+ .flatMap(m -> Stream.>>of(
+ () -> checkTestCaseEnvironments(environment, m, context.getDisplayName()),
+ () -> checkDefectEnvironments(environment, m, context.getDisplayName(), context),
+ () -> checkGlobalParameterSet(context, m, context.getDisplayName()),
+ () -> checkJiraReportingRules(context, m, context.getDisplayName()))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst())
+ .orElse(enableEvaluationAndLogExecutionStart(context));
+ }
+
+ private boolean isClassExtensionContext(ExtensionContext extensionContext) {
+ String simpleTestName = extensionContext.getTestClass()
+ .get()
+ .getSimpleName();
+
+ return !extensionContext.getTestMethod().isPresent() && extensionContext.getDisplayName().equals(simpleTestName);
+ }
+
+ private ConditionEvaluationResult evaluateClassExecutionContext(ExtensionContext extensionContext) {
+ ConfigProvider.readConfig().forEach((k, v) -> extensionContext.getStore(CONFIG_NAMESPACE).put(k, v));
+ Browser definedBrowser = getBrowserFromConfig(extensionContext);
+ String className = getClassCanonicalName(extensionContext);
+ Class> testClass = extensionContext.getTestClass().get();
+
+ return Stream.>>of(
+ () -> Optional.ofNullable(testClass.getAnnotation(TestScenario.class))
+ .map(TestScenario::browsers),
+ () -> Optional.ofNullable(testClass.getAnnotation(NonConcurrentTestScenario.class))
+ .map(NonConcurrentTestScenario::browsers),
+ () -> Optional.ofNullable(testClass.getAnnotation(TestScenarioWithOrder.class))
+ .map(TestScenarioWithOrder::browsers))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .filter(browsers -> Arrays.stream(browsers)
+ .noneMatch(definedBrowser::equals))
+ .map(browsers -> ConditionEvaluationResult.disabled("Tests in class " +
+ className + " disabled. It is marked to only run on " +
+ Arrays.stream(browsers)
+ .map(Enum::name)
+ .collect(Collectors.joining(", "))))
+ .orElse(enableClassForTests(className));
+ }
+
+ private ConditionEvaluationResult enableClassForTests(String className) {
+ logger.info("Test execution in test class {} started.", () -> className);
+ return ConditionEvaluationResult.enabled("Tests in " + className + " enabled.");
+ }
+
+ private Optional checkGlobalParameterSet(ExtensionContext extensionContext, Method testMethod, String testName) {
+ Store rootStore = extensionContext.getRoot().getStore(Namespace.GLOBAL);
+ return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(EnableIfSet.class))
+ .map(annotation -> {
+ if (rootStore == null) {
+ return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Need to check key '%s' " +
+ "to be set, but root context is null. Skipping...", testName, annotation.globalKey()));
+ }
+ Object value = rootStore.get(annotation.globalKey());
+ if (value == null) {
+ return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Key '%s' " +
+ "is not set in global context. Skipping...", testName, annotation.globalKey()));
+ }
+ return null;
+ }).filter(Objects::nonNull)
+ .findFirst()
+ .orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
+ }
+
+ private Optional checkJiraReportingRules(ExtensionContext extensionContext, Method testMethod, String testName) {
+ if (Boolean.parseBoolean(extensionContext.getStore(CONFIG_NAMESPACE).get("reports.tmfj.publish", String.class))) {
+ return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(JiraTestCase.class))
+ .map(annotation -> {
+ if (JiraTestResultPublisher.doesTestCaseExist(annotation.id(), extensionContext)) {
+ return null;
+ } else {
+ return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Cannot find Test Case with id '%s' " +
+ "in Jira, but reporting to Jira is requested. Skipping...", testName, annotation.id()));
+ }
+ }).filter(Objects::nonNull)
+ .findFirst()
+ .orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
+ }
+ return Optional.empty();
+ }
+
+ private Optional checkTestCaseEnvironments(Environment environment, Method testMethod,
+ String testName) {
+ return Stream.>>of(
+ () -> Optional.ofNullable(testMethod.getAnnotation(TestCase.class))
+ .map(TestCase::environments),
+ () -> Optional.ofNullable(testMethod.getAnnotation(ParameterizedTestCase.class))
+ .map(ParameterizedTestCase::environments))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .filter(envs -> Arrays.stream(envs).noneMatch(environment::equals))
+ .map(envs -> ConditionEvaluationResult.disabled("Test " +
+ testName + " disabled. It is marked to only run on " +
+ Arrays.stream(envs).map(Enum::name).collect(Collectors.joining(", "))));
+ }
+
+ private Optional checkDefectEnvironments(Environment environment, Method testMethod,
+ String testName, ExtensionContext context) {
+ return Optional.of(context.getStore(CONFIG_NAMESPACE)
+ .getOrComputeIfAbsent("harness.defects.ignore", k -> "false"))
+ .filter(ignore -> !"true".equals(ignore))
+ .flatMap(i -> Arrays.stream(testMethod.getAnnotationsByType(Defect.class))
+ .filter(d -> Arrays.stream(d.environments()).anyMatch(environment::equals))
+ .findFirst())
+ .map(d -> ConditionEvaluationResult.disabled("Test " +
+ testName + " in environment " + environment + " disabled due to defect '" + d.value() + "'"));
+ }
+
+ private Environment getCurrentEnvironment(ExtensionContext context) {
+ return Optional.ofNullable(context.getStore(CONFIG_NAMESPACE)
+ .get(HarnessConfigConstants.ENVIRONMENT_TYPE))
+ .filter(String.class::isInstance)
+ .map(String.class::cast)
+ .map(Environment::fromString)
+ .map(e -> {
+ logger.info("Detected environment: {}", () -> e.name());
+ return e;
+ })
+ .orElseThrow(() ->
+ new HarnessConfigurationException("You need to configure mandatory parameter " + HarnessConfigConstants.ENVIRONMENT_TYPE));
+ }
+
+ private Browser getBrowserFromConfig(ExtensionContext extensionContext) {
+ String browserConfigName = System.getProperty(BROWSER_CONFIG_KEY);
+
+ if (browserConfigName == null) {
+ browserConfigName = Optional.ofNullable(extensionContext.getStore(CONFIG_NAMESPACE).get(BROWSER_CONFIG_KEY, String.class))
+ .orElseThrow(() -> new HarnessConfigurationException(("You need to configure browser to run tests!")));
+ }
+
+ Browser browser = Browser.getBrowserByConfigName(browserConfigName);
+ logger.info("Detected browser: {}", browser::name);
+ return browser;
+ }
+
+ private ConditionEvaluationResult enableEvaluationAndLogExecutionStart(ExtensionContext extensionContext) {
+ String testName = extensionContext.getDisplayName();
+ logger.info("Test case {} execution in test class {} started.", () -> testName, () -> getClassCanonicalName(extensionContext));
+ return ConditionEvaluationResult.enabled("Test " + testName + " enabled");
+ }
+
+ private void logTestResult(ExtensionContext extensionContext) {
+ Method testMethod = extensionContext.getRequiredTestMethod();
+ Optional executionException = extensionContext.getExecutionException();
+ String resolution = executionException.map(e -> RESOLUTION_FAIL).orElse(RESOLUTION_PASS);
+
+ logger.info("Test case {} in test class {} executed with resolution - {}.", () -> extensionContext.getDisplayName(), () -> testMethod.getDeclaringClass(), () -> resolution);
+
+ if (resolution.equals(RESOLUTION_FAIL)) {
+ logger.error("ERROR: ", () -> executionException.get());
+ }
+ }
+
+ private String getClassCanonicalName(ExtensionContext extensionContext) {
+ return extensionContext.getTestClass()
+ .get()
+ .getCanonicalName();
+ }
+
+ private void storeCurrentTestName(ExtensionContext extensionContext) {
+ String uniqueId = UniqueId.parse(extensionContext.getUniqueId())
+ .getSegments()
+ .stream()
+ .skip(1)
+ .map(UniqueId.Segment::getValue)
+ .collect(Collectors.joining("."));
+ extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_UNIQUE_ID, uniqueId);
+
+ String shortId = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
+ extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_SHORT_ID, shortId);
+ }
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defect.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defect.java
new file mode 100644
index 0000000..4c0c45f
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defect.java
@@ -0,0 +1,33 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static cz.moneta.test.harness.annotations.Environment.DEV;
+import static cz.moneta.test.harness.annotations.Environment.EDU;
+import static cz.moneta.test.harness.annotations.Environment.FVE;
+import static cz.moneta.test.harness.annotations.Environment.PPE;
+import static cz.moneta.test.harness.annotations.Environment.TST1;
+
+/**
+ * Disables test in specified environments with a reference to the related issue
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Repeatable(Defects.class)
+public @interface Defect {
+ /**
+ * Reason for test being disabled (ideally) including a reference to a JIRA or TEAMTRACK issue
+ */
+ String value();
+
+ /**
+ * Applicable environments where the test should be effectively disabled (default is all environments)
+ */
+ Environment[] environments() default {DEV, TST1, PPE, EDU, FVE};
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defects.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defects.java
new file mode 100644
index 0000000..1d56208
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Defects.java
@@ -0,0 +1,17 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * container annotation for {@link cz.moneta.test.harness.annotations.Defect}
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Defects {
+ Defect[] value();
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSet.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSet.java
new file mode 100644
index 0000000..804fe0c
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSet.java
@@ -0,0 +1,13 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(EnableIfSets.class)
+@Documented
+public @interface EnableIfSet {
+
+ String globalKey();
+
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSets.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSets.java
new file mode 100644
index 0000000..8258831
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/EnableIfSets.java
@@ -0,0 +1,15 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * Container annotation
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface EnableIfSets {
+
+ EnableIfSet[] value();
+
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/Environment.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Environment.java
new file mode 100644
index 0000000..3d0828c
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/Environment.java
@@ -0,0 +1,30 @@
+package cz.moneta.test.harness.annotations;
+
+import java.util.EnumSet;
+
+public enum Environment {
+ DEV("DigDev"),
+ TST1("TST1"),
+ TST3("TST3"),
+ PPE("PPE"),
+ EDU("EDU"),
+ FVE("FVE"),
+ LIVE("LIVE");
+
+ Environment(String jiraAlias) {
+ this.jiraAlias = jiraAlias;
+ }
+
+ private String jiraAlias;
+
+ public static Environment fromString(String env) {
+ return EnumSet.allOf(Environment.class).stream()
+ .filter(e -> e.name().equalsIgnoreCase(env))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public String getJiraAlias() {
+ return jiraAlias;
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCase.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCase.java
new file mode 100644
index 0000000..b8accdc
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCase.java
@@ -0,0 +1,22 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * {@code @JiraTestCase} is used to signal that the annotated method implements
+ * particular test case in Test Management for JIRA.
+ *
+ * The info in this annotation is used by harness to create Test Run objects in
+ * Test Management for JIRA when configured and enabled, otherwise it has no effect.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(JiraTestCases.class)
+@Documented
+public @interface JiraTestCase {
+
+ String id();
+
+ String project() default "";
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCases.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCases.java
new file mode 100644
index 0000000..d9d2871
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/JiraTestCases.java
@@ -0,0 +1,14 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * Container annotation
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface JiraTestCases {
+
+ JiraTestCase[] value();
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/NonConcurrentTestScenario.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/NonConcurrentTestScenario.java
new file mode 100644
index 0000000..214c384
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/NonConcurrentTestScenario.java
@@ -0,0 +1,31 @@
+package cz.moneta.test.harness.annotations;
+
+import cz.moneta.test.harness.HarnessJunit5Extension;
+import cz.moneta.test.harness.data.Browser;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used for managing parallel test execution.
+ * Test class with this annotation will not be executed in parallel to other classes with this annotation.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RunWith(JUnitPlatform.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@ExtendWith(HarnessJunit5Extension.class)
+public @interface NonConcurrentTestScenario {
+
+ String name() default "";
+
+ Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/ParameterizedTestCase.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/ParameterizedTestCase.java
new file mode 100644
index 0000000..1f4490a
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/ParameterizedTestCase.java
@@ -0,0 +1,17 @@
+package cz.moneta.test.harness.annotations;
+
+import org.junit.jupiter.params.ParameterizedTest;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@ParameterizedTest
+public @interface ParameterizedTestCase {
+
+ String name () default "";
+
+ Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestCase.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestCase.java
new file mode 100644
index 0000000..cc4ee4c
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestCase.java
@@ -0,0 +1,16 @@
+package cz.moneta.test.harness.annotations;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Test
+public @interface TestCase {
+
+ String name() default "";
+
+ Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestContext.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestContext.java
new file mode 100644
index 0000000..41a92ad
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestContext.java
@@ -0,0 +1,9 @@
+package cz.moneta.test.harness.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface TestContext {
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenario.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenario.java
new file mode 100644
index 0000000..dd8e48b
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenario.java
@@ -0,0 +1,26 @@
+package cz.moneta.test.harness.annotations;
+
+import cz.moneta.test.harness.HarnessJunit5Extension;
+import cz.moneta.test.harness.data.Browser;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RunWith(JUnitPlatform.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@Execution(ExecutionMode.CONCURRENT)
+@ExtendWith(HarnessJunit5Extension.class)
+public @interface TestScenario {
+
+ String name() default "";
+
+ Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenarioWithOrder.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenarioWithOrder.java
new file mode 100644
index 0000000..505af90
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestScenarioWithOrder.java
@@ -0,0 +1,29 @@
+package cz.moneta.test.harness.annotations;
+
+import cz.moneta.test.harness.HarnessJunit5Extension;
+import cz.moneta.test.harness.data.Browser;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RunWith(JUnitPlatform.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@Execution(ExecutionMode.SAME_THREAD)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@ExtendWith(HarnessJunit5Extension.class)
+public @interface TestScenarioWithOrder {
+
+ String name() default "";
+
+ Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestSuite.java b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestSuite.java
new file mode 100644
index 0000000..113bc1f
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/annotations/TestSuite.java
@@ -0,0 +1,19 @@
+package cz.moneta.test.harness.annotations;
+
+import cz.moneta.test.harness.HarnessJunit5Extension;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RunWith(JUnitPlatform.class)
+@ExtendWith(HarnessJunit5Extension.class)
+public @interface TestSuite {
+
+ String name() default "";
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/config/ConfigProvider.java b/test-harness/src/main/java/cz/moneta/test/harness/config/ConfigProvider.java
new file mode 100644
index 0000000..66e2f23
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/config/ConfigProvider.java
@@ -0,0 +1,117 @@
+package cz.moneta.test.harness.config;
+
+import cz.moneta.test.harness.constants.HarnessConfigConstants;
+import cz.moneta.test.harness.exception.HarnessConfigurationException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public final class ConfigProvider {
+
+ private static final Logger logger = LogManager.getLogger(ConfigProvider.class);
+
+ private ConfigProvider() {}
+
+ public static Map readConfig() {
+ String fileName = Stream.>of(
+ () -> System.getProperty("config"),
+ () -> System.getenv("HARNESS_CONFIG"))
+ .map(Supplier::get)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElseThrow(() -> new HarnessConfigurationException("You need to provide a configuration file"));
+
+ Map resultConfig = loadConfig(fileName, new ArrayList<>());
+ logger.debug("Effective configuration:");
+ resultConfig.entrySet()
+ .stream()
+ .filter(configEntry -> !configEntry.getKey().equals(HarnessConfigConstants.VAULT_PASSWORD_CONFIG)) //DO NOT LOG VAULT PWD VALUE!
+ .collect(Collectors.toMap(Map.Entry::getKey, configEntry -> configEntry.getValue().trim()))
+ .forEach((key, value) -> logger.debug("{}={}", key, value));
+ return resultConfig;
+ }
+
+ private static Map loadConfig(String fileName, List configStack) {
+ Map properties = readProperties(fileName);
+
+ return Optional.ofNullable(properties.get("environment.from"))
+ .map(fn -> {
+ if (configStack.contains(fn)) {
+ throw new IllegalStateException("Config chain contains circular dependencies");
+ }
+ return fn;
+ })
+ .map(fn -> {
+ configStack.add(fn);
+ return loadConfig(fn, configStack);
+ })
+ .map(p -> {
+ p.putAll(properties);
+ return p;
+ })
+ .orElse(properties);
+ }
+
+ private static Map readProperties(String fileName) {
+ Reader reader;
+ try {
+ logger.debug(() -> String.format("Trying to load config file: %s", fileName));
+ reader = new FileReader(fileName);
+ } catch (FileNotFoundException e) {
+ logger.debug(() ->
+ String.format("Could not load file: %s, trying to load it as classpath resource", fileName));
+ reader = getReaderForClasspathResource(fileName);
+ }
+
+ Properties properties = new Properties();
+ try {
+ properties.load(reader);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot read config file", e);
+ }
+
+ return properties.stringPropertyNames().stream()
+ .collect(Collectors.toMap(k -> k, k -> resolveSystemProperty(properties.getProperty(k))));
+ }
+
+ private static String resolveSystemProperty(String value) {
+ return Stream.>>of(
+ () -> Optional.ofNullable(value).filter(v -> !value.startsWith("$")),
+ () -> Optional.ofNullable(value)
+ .map(v -> System.getProperty(v.substring(1)))
+ .map(v -> {
+ logger.debug("Successfully resolved system property: {}", () -> value);
+ return v;
+ }))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("Cannot find system property for: " + value));
+ }
+
+ private static Reader getReaderForClasspathResource(String resourceName) {
+ String resourcePath = "envs/" + resourceName; //TODO hidden magic constant - fix this concept
+ InputStream resourceAsStream = ConfigProvider.class.getClassLoader().getResourceAsStream(resourcePath);
+ if (resourceAsStream != null) {
+ return new InputStreamReader(resourceAsStream);
+ } else {
+ throw new IllegalStateException(String.format("Cannot find config file: %s", resourceName));
+ }
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/Connector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/Connector.java
new file mode 100644
index 0000000..970c711
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/Connector.java
@@ -0,0 +1,7 @@
+package cz.moneta.test.harness.connectors;
+
+public interface Connector {
+
+ default void close() {
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/DemoConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/DemoConnector.java
new file mode 100644
index 0000000..b8ff7d8
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/DemoConnector.java
@@ -0,0 +1,39 @@
+package cz.moneta.test.harness.connectors;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class DemoConnector implements Connector {
+
+ private Path directory;
+
+ public void connectToTempDirectoryService(String name) {
+ try {
+ directory = Files.createTempDirectory(name);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot connect to temp filesystem service", e);
+ }
+ }
+
+ public void createFile(String name) {
+ try {
+ Files.createFile(directory.resolve(name));
+ } catch (IOException e) {
+ throw new RuntimeException(String.format("Cannot create file: %s", name), e);
+ }
+ }
+
+ public boolean fileExists(String name) {
+ return Files.exists(directory.resolve(name));
+ }
+
+ public void deleteFile(String name) {
+ try {
+ Files.delete(directory.resolve(name));
+ } catch (IOException e) {
+ throw new RuntimeException(String.format("Cannot delete file: %s", name), e);
+ }
+ }
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/GreenScreenConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/GreenScreenConnector.java
new file mode 100644
index 0000000..6974288
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/GreenScreenConnector.java
@@ -0,0 +1,156 @@
+package cz.moneta.test.harness.connectors;
+
+import com.bytezone.dm3270.ConnectionListener;
+import com.bytezone.dm3270.TerminalClient;
+import com.bytezone.dm3270.commands.AIDCommand;
+import com.bytezone.dm3270.display.ScreenDimensions;
+import cz.moneta.test.harness.support.greenscreen.Key;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.glassfish.jersey.SslConfigurator;
+
+import javax.net.ssl.SSLSocketFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class GreenScreenConnector implements Connector {
+
+ public static final String CLIENT = "08548";
+ private static final Logger LOG = LogManager.getLogger("GreenScreenConnector");
+ private final Semaphore lock = new Semaphore(0);
+ private final TerminalClient terminal;
+ private final String url;
+ private final String user;
+ private final String password;
+
+ public GreenScreenConnector(String url, String user, String password) {
+ this.url = url;
+ this.user = user;
+ this.password = password;
+
+ this.terminal = new TerminalClient(0, new ScreenDimensions(24, 80));
+ terminal.setConnectionTimeoutMillis(20_000);
+ terminal.setConnectionListener(buildConnectionListener());
+ terminal.addScreenChangeListener(sw -> {
+ LOG.debug(() -> "screen changed: \n" + toCp870(terminal.getScreenText()));
+ lock.release();
+ });
+ terminal.setSocketFactory(buildSocketFactory());
+ }
+
+ private static ConnectionListener buildConnectionListener() {
+ return new ConnectionListener() {
+ @Override
+ public void onConnection() {
+ LOG.info("CONNECTED!!!");
+ }
+
+ @Override
+ public void onException(Exception e) {
+ LOG.error("Green screen connection failed", e);
+ }
+
+ @Override
+ public void onConnectionClosed() {
+ LOG.info("CONNECTION CLOSED!!!");
+ }
+ };
+ }
+
+ private static String toCp870(String cp1047) {
+ try {
+ return new String(cp1047.getBytes("CP1047"), "CP870");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("Error converting CP1047 to CP870");
+ }
+ }
+
+ private static String toCp1047(String cp870) {
+ try {
+ return new String(cp870.getBytes("CP870"), "CP1047");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("Error converting CP870 to CP1047");
+ }
+ }
+
+ public void connectAndLogin() {
+ terminal.connect(url, 992);
+ waitForScreenChange();
+ terminal.setFieldTextByCoord(24, 1, "atrf");
+ terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
+ waitForScreenChange();
+ terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
+ waitForScreenChange();
+ terminal.setFieldTextByCoord(6, 25, CLIENT);
+ terminal.setFieldTextByCoord(8, 24, user);
+ terminal.setFieldTextByCoord(10, 24, password);
+ terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
+ waitForScreenChange();
+ waitForScreenChange();
+ terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
+ waitForScreenChange();
+ }
+
+ private void waitForScreenChange() {
+ boolean success = false;
+ try {
+ success = lock.tryAcquire(5L, TimeUnit.SECONDS);
+ TimeUnit.MILLISECONDS.sleep(200);
+ } catch (InterruptedException e) {
+ LOG.error(e);
+ }
+ if (!success) {
+ throw new IllegalStateException("Failed to load screen within 5 second timeout");
+ }
+ }
+
+ private SSLSocketFactory buildSocketFactory() {
+ try (InputStream truststoreIs = this.getClass().getClassLoader()
+ .getResourceAsStream("keystores/greenScreenTrustStore")) {
+
+ KeyStore trustedStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustedStore.load(truststoreIs, "changeit".toCharArray());
+
+ return SslConfigurator.newInstance()
+ .trustStore(trustedStore)
+ .keyStorePassword("changeit".toCharArray())
+ .createSSLContext()
+ .getSocketFactory();
+
+ } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Failed to initialize SSL context", e);
+ }
+ }
+
+ public String getText(int row, int column, int length) {
+ String screenText = toCp870(terminal.getScreenText());
+ if (length == 0) {
+ return screenText;
+ } else {
+ int beginIndex = (row - 1) * 81 + column - 1;
+ return screenText.substring(beginIndex, beginIndex + length);
+ }
+ }
+
+ public void pressKey(Key key) {
+ switch (key) {
+ case ENTER:
+ terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
+ waitForScreenChange();
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown key " + key.name());
+ }
+ }
+
+ public void type(int row, int column, String text) {
+ terminal.setFieldTextByCoord(row, column, toCp1047(text));
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/IlodsServerConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/IlodsServerConnector.java
new file mode 100644
index 0000000..bc3b78a
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/IlodsServerConnector.java
@@ -0,0 +1,129 @@
+package cz.moneta.test.harness.connectors;
+
+import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerEnvironmentList;
+import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerSourceSystem;
+import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToClient;
+import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToServer;
+import itSodTesting.sharedResources.utils.getUser.LoggedUser;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+
+public class IlodsServerConnector implements Connector {
+
+ private static final Logger logger = LogManager.getLogger(IlodsServerConnector.class);
+
+ private static final String token = "&PE$T.Q;1SXdhF@#$vdnv3,M8r5+dr=e";
+
+ private static final Pair liveServer = Pair.of("primary", "MBCZVW1DL0LRC02.mbid.cz");
+ private static final Pair backupServer = Pair.of("secondary", "MBCZVW0BL0LRC01.mbid.cz");
+ private static final Pair devServer = Pair.of("develop", "MBCZDWHQXX10366.mbid.cz");
+
+ private Pair currentServer = liveServer;
+ // private Pair currentServer = devServer;
+
+ private IlodsServerEnvironmentList environment;
+
+ public IlodsServerConnector(IlodsServerEnvironmentList environment) {
+ this.environment = environment;
+ }
+
+ public ObjectTransferToClient sendRequest(ObjectTransferToServer data) {
+ ObjectTransferToClient receivedObject = connectAndReceiveData(data);
+
+ // no response = second try
+ if (receivedObject == null) {
+ switchServer();
+ receivedObject = connectAndReceiveData(data);
+ }
+
+ if (receivedObject == null) {
+ throw new RuntimeException("Cannot connect to Ilods servers");
+ }
+
+ return receivedObject;
+ }
+
+ private ObjectTransferToClient connectAndReceiveData(ObjectTransferToServer data) {
+ ObjectTransferToClient receivedObject = null;
+ Socket clientSocket = null;
+
+ try {
+ clientSocket = new Socket();
+ clientSocket.connect(new InetSocketAddress(currentServer.getRight(), 51001), 1000);
+ clientSocket.setSoTimeout(60 * 1000);
+
+ ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
+ ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());
+
+ // add clients informations do data
+ data.setToken(token);
+ data.setSourceSystem(IlodsServerSourceSystem.HARNESS);
+ data.setMachineId(LoggedUser.getComputerName());
+ data.setClientId(LoggedUser.getUserName());
+ data.setEnvironment(environment);
+
+ // pack and sending object data to server
+ outToServer.writeObject(data);
+
+ // unpack and reading object response from server
+ Object transferedObject = inFromServer.readObject();
+ receivedObject = (ObjectTransferToClient) transferedObject;
+
+ // recognize and process errors
+ if (receivedObject.getResultCode() == 12) {
+ throw new RuntimeException("Fatal error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
+ } else if (receivedObject.getResultCode() != 0) {
+ throw new RuntimeException("Server error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
+ }
+
+ } catch (SocketTimeoutException e) {
+ logger.warn("Connection timed out - " + currentServer.getLeft() + " Ilods server didn't respond within the specified interval");
+ } catch (ClassNotFoundException | IOException e) {
+ logger.warn("Ilods server (" + currentServer.getLeft() + ") unavailable", e);
+ } finally {
+ closeConnection(clientSocket);
+ }
+
+ return receivedObject;
+ }
+
+ private void switchServer() {
+ // Set up switch between primary/secondary Ilods Server (with check develop mode)
+ if (currentServer == devServer) {
+ return;
+ }
+
+ if (currentServer == liveServer) {
+ currentServer = backupServer;
+ } else {
+ currentServer = liveServer;
+ }
+ logger.info("Trying to call " + currentServer.getLeft() + " Ilods server");
+ }
+
+ private void closeConnection(Socket socket) {
+ if (socket == null)
+ return;
+ try {
+ socket.shutdownInput();
+ } catch (IOException e) {
+ }
+ try {
+ socket.shutdownOutput();
+ } catch (IOException e) {
+ }
+ try {
+ socket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/VaultConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/VaultConnector.java
new file mode 100644
index 0000000..d8fff14
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/VaultConnector.java
@@ -0,0 +1,123 @@
+package cz.moneta.test.harness.connectors;
+
+import com.bettercloud.vault.SslConfig;
+import com.bettercloud.vault.Vault;
+import com.bettercloud.vault.VaultConfig;
+import com.bettercloud.vault.VaultException;
+import com.bettercloud.vault.api.Logical;
+import com.bettercloud.vault.response.LogicalResponse;
+import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
+import cz.moneta.test.harness.support.auth.Credentials;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.security.KeyStore;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class VaultConnector extends BaseRestConnector {
+
+ private static final Logger LOG = LogManager.getLogger(VaultConnector.class);
+
+ private static String url;
+ private static String username;
+ private static String pwd;
+
+ private static Vault vault;
+ private static VaultConfig config;
+
+ private static LocalDateTime tokenExpiration;
+
+ public VaultConnector(String url, String username, String pwd) {
+ this.url = url;
+ this.username = username;
+ this.pwd = pwd;
+
+ if (tokenExpiration == null) {
+ initVaultConnection();
+ } else if (tokenExpiration.minusMinutes(1).isBefore(LocalDateTime.now())) {
+ loginVault();
+ }
+ }
+
+ private void initVaultConnection() {
+ try {
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keyStore.load(this.getClass().getClassLoader().getResourceAsStream("keystores/mb_root"), "changeit".toCharArray());
+ config = new VaultConfig()
+ .address(url)
+ .sslConfig(new SslConfig()
+ .trustStore(keyStore)
+ .build())
+ .build();
+
+ vault = new Vault(config, 2);
+
+ loginVault();
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to connect to vault url: " + url, e);
+ }
+ }
+
+ private void loginVault() {
+ try {
+ String token = vault.auth().loginByUserPass(username, pwd).getAuthClientToken();
+ config.token(token).build();
+
+ long ttl = vault.auth().lookupSelf().getTTL();
+ tokenExpiration = LocalDateTime.now().plusSeconds(ttl);
+ } catch (VaultException e) {
+ LOG.error("Cannot get Vault token", e);
+ }
+ }
+
+ public Optional getUsernameAndPassword(String path) {
+ return Optional.ofNullable(vault.logical())
+ .map(v -> readValue(path, v))
+ .map(LogicalResponse::getDataObject)
+ .flatMap(d -> Optional.ofNullable(d.getString("username")).map(usr -> Pair.of(usr, d)))
+ .flatMap(p -> Optional.ofNullable(p.getRight().getString("password")).map(pwd -> new Credentials(p.getLeft(), pwd)));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Optional getValue(String path, String key) {
+ return Optional.ofNullable(vault.logical())
+ .map(v -> readValue(path, v))
+ .map(LogicalResponse::getDataObject)
+ .map(d -> (T) d.getString(key));
+ }
+
+ public void setKeyValue(String path, String key, String value) {
+ Optional.ofNullable(readValue(path, vault.logical()))
+ .map(LogicalResponse::getData)
+ .map(map -> new HashMap(map))
+ .map(m -> {
+ m.put(key, value);
+ return m;
+ })
+ .map(m -> writeValue(path, m, vault.logical()))
+ .orElseThrow(() -> new IllegalStateException("Cannot write value to vault"));
+ }
+
+ private LogicalResponse writeValue(String path, Map value, Logical vault) {
+ try {
+ return vault.write(path, value);
+ } catch (VaultException e) {
+ LOG.error("Failed to write value from vault: {}\n{}", path, e);
+ return null;
+ }
+ }
+
+ private LogicalResponse readValue(String path, Logical v) {
+ try {
+ return v.read(path);
+ } catch (VaultException e) {
+ LOG.error("Failed to read value from vault: {}\n{}", path, e);
+ return null;
+ }
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/WsConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/WsConnector.java
new file mode 100644
index 0000000..f2a41ba
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/WsConnector.java
@@ -0,0 +1,46 @@
+package cz.moneta.test.harness.connectors;
+
+import jakarta.xml.bind.JAXBContext;
+import jakarta.xml.bind.JAXBException;
+import jakarta.xml.ws.BindingProvider;
+import jakarta.xml.ws.Dispatch;
+import jakarta.xml.ws.Service;
+import jakarta.xml.ws.handler.MessageContext;
+import org.apache.cxf.jaxws.CXFService;
+
+import javax.xml.namespace.QName;
+import java.net.URL;
+
+public class WsConnector {
+
+ private final Service service;
+ private final QName portName;
+ private final String address;
+ private final String serviceNamespace;
+
+ public WsConnector(String address, URL wsdlUrl, QName serviceName) {
+ this.address = address;
+ Service service = CXFService.create(wsdlUrl, serviceName);
+ if (service.getPorts().hasNext()) {
+ this.portName = service.getPorts().next();
+ } else {
+ throw new IllegalStateException(String.format("Service definition %s has no ports defined", wsdlUrl.toString()));
+ }
+ this.serviceNamespace = serviceName.getNamespaceURI();
+ this.service = service;
+ }
+
+ @SuppressWarnings("unchecked")
+ public RESP invoke(Object request, Class responseClass) {
+ try {
+ QName operationName = new QName(serviceNamespace, request.getClass().getSimpleName());
+ JAXBContext jaxbContext = JAXBContext.newInstance(responseClass.getPackage().getName());
+ Dispatch
+ *
+ * @param path - path to select element
+ * @param lookup - lookup strategy for select element
+ * @param textOptionsParts - list of option text parts to look for
+ */
+ public void selectByContainingTexts(String path, Lookup lookup, List textOptionsParts) {
+ List optionXpaths = textOptionsParts.stream()
+ .map(text -> String.format("contains(text(),'%s')", text))
+ .collect(Collectors.toList());
+ String joinedContains = String.join(" or ", optionXpaths);
+ String optionXpath = String.format("//option[%s]", joinedContains);
+
+ Waits waits = connector.waits();
+ waits.waitForLazyElement(path, lookup)
+ .findElements(waits.byLocator(Lookup.XPATH, optionXpath))
+ .get(0)
+ .click();
+ }
+
+ public String getFirstSelectedOptionText(String path, Lookup lookup) {
+ Select select = new Select(connector.waits().waitForLazyElement(path, lookup));
+ return select.getFirstSelectedOption().getText();
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumChromeConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumChromeConnector.java
new file mode 100644
index 0000000..d55e706
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumChromeConnector.java
@@ -0,0 +1,92 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.constants.HarnessConfigConstants;
+import cz.moneta.test.harness.context.StoreAccessor;
+import cz.moneta.test.harness.exception.HarnessException;
+import org.apache.commons.lang3.tuple.Pair;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Capabilities;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.remote.DesiredCapabilities;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
+import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
+
+public class SeleniumChromeConnector extends SeleniumWebConnector {
+
+ public SeleniumChromeConnector(Function defaultLookup, StoreAccessor store, String... extraOptions) {
+ super(defaultLookup, store, extraOptions);
+ }
+
+ @Override
+ protected Pair getLocalWebDriver() {
+ setupDriver();
+
+ return Optional.ofNullable(store.getConfig("selenium.chromebinary.path"))
+ .map(bin -> {
+ ChromeOptions chromeOptions = getChromeOptions();
+ chromeOptions.setBinary(bin);
+ return chromeOptions;
+ })
+ .map(o -> Pair.of(new ChromeDriver(o), false))
+ .orElse(null);
+ }
+
+ private void setupDriver() {
+ Optional.ofNullable(store.getConfig("selenium.chrome.webdriver.path"))
+ .map(path -> {
+ System.setProperty("webdriver.chrome.driver", path);
+ return path;
+ })
+ .orElseThrow(() -> new AssertionError("Please provide path to the Chrome driver using selenium.chrome.webdriver.path configuration key or use a selenium grid"));
+ }
+
+ private ChromeOptions getChromeOptions() {
+ ChromeOptions chromeOptions = new ChromeOptions();
+
+ Optional.ofNullable(store.getConfig("selenium.webdriver.chrome.options"))
+ .map(o -> o.split("\\s+"))
+ .ifPresent(chromeOptions::addArguments);
+
+ chromeOptions.addArguments(extraOptions);
+
+ this.driverUtils().addDefaultDesiredCapabilities(chromeOptions);
+
+ this.driverUtils().addAdditionalDesiredCapabilities(chromeOptions, "selenium.chrome.capabilities");
+
+ // Set up Selenium Grid session name
+ chromeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
+
+ // Choose which Selenium Grid node will be used
+ Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
+ .ifPresent(platform -> chromeOptions.setPlatformName(platform));
+
+ // Overrides default download directory in Chrome profile preferences, http://chromedriver.chromium.org/capabilities
+ // By default headless mode does not download any files unless download directory is set.
+ Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
+ .ifPresent(experimentalOptions -> {
+ Map prefs = new HashMap();
+ prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
+ chromeOptions.setExperimentalOption("prefs", prefs);
+ });
+
+ return chromeOptions;
+ }
+
+ @Override
+ protected Capabilities getDesiredRemoteCapabilities() {
+ return new DesiredCapabilities(getChromeOptions());
+ }
+
+ @Override
+ public void downloadFileViaIePrompt(int waitInSeconds) {
+ throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumEdgeConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumEdgeConnector.java
new file mode 100644
index 0000000..5b530a5
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumEdgeConnector.java
@@ -0,0 +1,91 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.constants.HarnessConfigConstants;
+import cz.moneta.test.harness.context.StoreAccessor;
+import cz.moneta.test.harness.exception.HarnessException;
+import org.apache.commons.lang3.tuple.Pair;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Capabilities;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.edge.EdgeDriver;
+import org.openqa.selenium.edge.EdgeOptions;
+import org.openqa.selenium.remote.DesiredCapabilities;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
+import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
+
+public class SeleniumEdgeConnector extends SeleniumWebConnector {
+
+ public SeleniumEdgeConnector(Function defaultLookup, StoreAccessor store) {
+ super(defaultLookup, store);
+ }
+
+ @Override
+ protected Pair getLocalWebDriver() {
+ setupDriver();
+
+ EdgeDriver driver = new EdgeDriver(getEdgeOptions());
+ driver.manage()
+ .window()
+ .maximize();
+ return Pair.of(driver, false);
+ }
+
+ private void setupDriver() {
+ Optional.ofNullable(store.getConfig("selenium.edge.webdriver.path"))
+ .map(path -> {
+ System.setProperty("webdriver.edge.driver", path);
+ return path;
+ })
+ .orElseThrow(() -> new AssertionError("Please provide path to the Edge driver using selenium.edge.webdriver.path configuration key or use a selenium grid"));
+ }
+
+ private EdgeOptions getEdgeOptions() {
+ EdgeOptions edgeOptions = new EdgeOptions();
+
+ this.driverUtils().addDefaultDesiredCapabilities(edgeOptions);
+
+ Optional.ofNullable(store.getConfig("selenium.webdriver.edge.options"))
+ .map(o -> o.split("\\s+"))
+ .ifPresent(edgeOptions::addArguments);
+ edgeOptions.addArguments(extraOptions);
+
+ this.driverUtils().addAdditionalDesiredCapabilities(edgeOptions, "selenium.edge.capabilities");
+
+ // Set up Selenium Grid session name
+ edgeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
+
+ // Choose which Selenium Grid node will be used
+ Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
+ .ifPresent(platform -> edgeOptions.setPlatformName(platform));
+
+
+ Map prefs = new HashMap();
+ // Allow clipboard usage
+ prefs.put("profile.default_content_setting_values.clipboard", 1);
+
+ // Overrides default download directory in Edge profile preferences
+ Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
+ .ifPresent(experimentalOptions -> {
+ prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
+ });
+ edgeOptions.setExperimentalOption("prefs", prefs);
+
+ return edgeOptions;
+ }
+
+ @Override
+ protected Capabilities getDesiredRemoteCapabilities() {
+ return new DesiredCapabilities(getEdgeOptions());
+ }
+
+ @Override
+ public void downloadFileViaIePrompt(int waitInSeconds) {
+ throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumWebConnector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumWebConnector.java
new file mode 100644
index 0000000..0226505
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SeleniumWebConnector.java
@@ -0,0 +1,185 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.connectors.Connector;
+import cz.moneta.test.harness.context.StoreAccessor;
+import cz.moneta.test.harness.exception.HarnessException;
+import cz.moneta.test.harness.support.web.Lookup;
+import org.apache.commons.lang3.tuple.Pair;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Capabilities;
+import org.openqa.selenium.WebDriver;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public abstract class SeleniumWebConnector implements Connector {
+
+ private final WebDriver webDriver;
+ private final boolean isRemoteDriver;
+ protected final Function defaultLookup;
+ protected final StoreAccessor store;
+ protected final String[] extraOptions;
+
+ private String nodeHost;
+
+ public SeleniumWebConnector(Function defaultLookup, StoreAccessor store, String[] extraOptions) {
+ this.extraOptions = extraOptions;
+ this.store = store;
+ this.defaultLookup = defaultLookup;
+ Pair driverInfo = getPreferredWebDriver();
+ isRemoteDriver = driverInfo.getRight();
+ WebDriver driver = driverInfo.getLeft();
+ if (driver == null) {
+ throw new HarnessException("We were not able to initialize webDriver. Please check your configs, logs and talk to your admin");
+ }
+
+ //TODO fix this - it slows down test execution but some tests are build with this implicit wait
+ driver.manage().timeouts().implicitlyWait(2L, TimeUnit.SECONDS);
+ driver.manage().timeouts().pageLoadTimeout(90L, TimeUnit.SECONDS);
+ this.webDriver = driver;
+ }
+
+ public SeleniumWebConnector(Function defaultLookup, StoreAccessor store) {
+ this(defaultLookup, store, new String[]{});
+ }
+
+ public Clicks clicks() {
+ return new Clicks(this);
+ }
+
+ public Waits waits() {
+ return new Waits(this);
+ }
+
+ public Alerts alerts() {
+ return new Alerts(this);
+ }
+
+ public Types types() {
+ return new Types(this);
+ }
+
+ public Selects selects() {
+ return new Selects(this);
+ }
+
+ public SendKeys sendKeys() {
+ return new SendKeys(this);
+ }
+
+ public Frames frames() {
+ return new Frames(this);
+ }
+
+ public Windows windows() {
+ return new Windows(this);
+ }
+
+ public Navigations navigations() {
+ return new Navigations(this);
+ }
+
+ public ElementsChecks elementsChecks() {
+ return new ElementsChecks(this);
+ }
+
+ public Cookies cookies() {
+ return new Cookies(this);
+ }
+
+ public Scripts scripts() {
+ return new Scripts(this);
+ }
+
+ public ElementsActions elementsActions() {
+ return new ElementsActions(this);
+ }
+
+ public Scrolling scrolling() {return new Scrolling(this); }
+
+ public DriverUtils driverUtils() {
+ return new DriverUtils(this);
+ }
+
+ public StoreAccessor getStore() {
+ return store;
+ }
+
+ public boolean isRemoteDriver() {
+ return isRemoteDriver;
+ }
+
+ public abstract void downloadFileViaIePrompt(int waitInSeconds);
+
+ /**
+ * returns a pair of web driver and whether it is a remote instance (true = remote, false = local driver)
+ */
+ private Pair getPreferredWebDriver() {
+ return Stream.>>of(this::getDedicatedWebDriver, this::getSharedWebDriver, this::getLocalWebDriver)
+ .map(Supplier::get)
+ .filter(Objects::nonNull)
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("No suitable web driver could be initialized."));
+ }
+
+ private Pair getDedicatedWebDriver() {
+ return Optional.ofNullable(store.getConfig("selenium.grid.dedicated.url"))
+ .map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
+ .orElse(null);
+ }
+
+ private Pair getSharedWebDriver() {
+ return Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
+ .map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
+ .orElse(null);
+ }
+
+ public String getAttribute(String path, Lookup lookup, String attributeName) {
+ return waits().waitForLazyElement(path, lookup)
+ .getAttribute(attributeName );
+ }
+
+ protected abstract Pair getLocalWebDriver();
+
+ protected abstract Capabilities getDesiredRemoteCapabilities();
+
+ public WebDriver getDriver() {
+ return webDriver;
+ }
+
+ @Override
+ public void close() {
+ webDriver.quit();
+ }
+
+ public String getCurrentUrl() {
+ return webDriver.getCurrentUrl();
+ }
+
+ protected Function resolveLookup(Lookup lookup) {
+ switch (lookup) {
+ case XPATH:
+ return By::xpath;
+ case ID:
+ return By::id;
+ case CLASSNAME:
+ return By::className;
+ case NAME:
+ return By::name;
+ default:
+ return defaultLookup;
+ }
+ }
+
+ public String getNodeHost() {
+ return nodeHost;
+ }
+
+ public void setNodeHost(String nodeHost) {
+ this.nodeHost = nodeHost;
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SendKeys.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SendKeys.java
new file mode 100644
index 0000000..3c42c7d
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/SendKeys.java
@@ -0,0 +1,35 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.support.web.Key;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.interactions.Actions;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+public class SendKeys {
+
+ private final SeleniumWebConnector connector;
+
+ public SendKeys(SeleniumWebConnector connector) {
+ this.connector = connector;
+ }
+
+ public void sendKeysOneAtATime(Key[] keys) {
+ Actions actions = new Actions(connector.getDriver());
+ Arrays.stream(keys)
+ .forEach(k -> {
+ actions.sendKeys(k).build().perform();
+ try {
+ TimeUnit.MILLISECONDS.sleep(100);
+ } catch (InterruptedException e) {
+ //
+ }
+ });
+ }
+
+ public void sendKeysAsChord(Key[] keys) {
+ Actions actions = new Actions(connector.getDriver());
+ actions.sendKeys(Keys.chord(keys)).build().perform();
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Types.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Types.java
new file mode 100644
index 0000000..1d55257
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Types.java
@@ -0,0 +1,61 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.support.web.Lookup;
+import cz.moneta.test.harness.support.web.TextContainer;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import java.util.Optional;
+
+public class Types {
+
+ private final SeleniumWebConnector connector;
+
+ public Types(SeleniumWebConnector connector) {
+ this.connector = connector;
+ }
+
+ public void type(TextContainer input, String text, boolean clear, Lookup lookup) {
+ if (clear) {
+ clearInput(input, lookup);
+ }
+ connector.waits()
+ .waitForLazyElement(input.getPath(), lookup)
+ .sendKeys(text);
+ }
+
+ public void jsType(TextContainer input, String text, boolean clear, Lookup lookup) {
+ if (clear) {
+ clearInput(input, lookup);
+ }
+ Optional.of(connector.getDriver())
+ .filter(JavascriptExecutor.class::isInstance)
+ .map(JavascriptExecutor.class::cast)
+ .ifPresent(e -> {
+ WebElement element = connector.waits().waitForLazyElement(input.getPath(), lookup);
+ if ("textarea".equals(element.getTagName())) {
+ e.executeScript("arguments[0].value=arguments[1];", element, text);
+ } else {
+ e.executeScript("arguments[0].setAttribute('value', arguments[1]);", element, text);
+ }
+ });
+ }
+
+ private void clearInput(TextContainer input, Lookup lookup) {
+ connector.waits()
+ .waitForLazyElement(input.getPath(), lookup)
+ .sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);
+ }
+
+ public void typeWithControlDown(String input, String keys, Lookup lookup) {
+ new Actions(connector.getDriver())
+ .click(connector.waits().waitForLazyElement(input, lookup))
+ .keyDown(Keys.CONTROL)
+ .sendKeys(keys)
+ .keyUp(Keys.CONTROL)
+ .perform();
+ }
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Waits.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Waits.java
new file mode 100644
index 0000000..0e8be48
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Waits.java
@@ -0,0 +1,184 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.exception.HarnessException;
+import cz.moneta.test.harness.support.web.Lookup;
+import cz.moneta.test.harness.support.web.Until;
+import cz.moneta.test.harness.utils.expectedconditions.BooleanExpectedConditions;
+import org.apache.commons.lang3.ArrayUtils;
+import org.openqa.selenium.*;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.FluentWait;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+public class Waits {
+
+ private final SeleniumWebConnector connector;
+ private static final int LAZY_ELEMENT_RENDER_TIMEOUT = 5;
+
+ public Waits(SeleniumWebConnector connector) {
+ this.connector = connector;
+ }
+
+ public void waitForElements(int timeoutSeconds, Lookup lookup, Until until, String... elementsToCheck) {
+ WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
+ wait.ignoring(WebDriverException.class);
+
+ Arrays.stream(elementsToCheck)
+ .forEach(path -> wait.until(resolveUntil(until).apply(byLocator(lookup, path))));
+ }
+
+ public void waitForAtLeastOneElement(int timeoutSeconds, Lookup lookup, String... elementsToCheck) {
+ WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
+
+ ExpectedCondition[] expectedConditions = Arrays.stream(elementsToCheck)
+ .map(path ->
+ ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path)))
+ .toArray(ExpectedCondition[]::new);
+ wait.until(ExpectedConditions.or(expectedConditions));
+ }
+
+ WebElement waitForLazyElement(String path, Lookup lookup) {
+ doWaitForLazyElement(path, lookup);
+ return connector.getDriver()
+ .findElement(byLocator(lookup, path));
+ }
+
+ List waitForLazyElements(String path, Lookup lookup) {
+ doWaitForLazyElement(path, lookup);
+ return connector.getDriver()
+ .findElements(byLocator(lookup, path));
+ }
+
+ private void doWaitForLazyElement(String path, Lookup lookup) {
+ new FluentWait<>(connector.getDriver())
+ .withTimeout(Duration.ofSeconds(LAZY_ELEMENT_RENDER_TIMEOUT))
+ .pollingEvery(Duration.ofMillis(500))
+ .ignoring(NoSuchElementException.class)
+ .ignoring(StaleElementReferenceException.class)
+ .withMessage("Element/s not found within " + LAZY_ELEMENT_RENDER_TIMEOUT
+ + " seconds timeout. If this error occurs, you might want to consider using explicit @Wait annotation. " + path)
+ .until(driver -> !driver.findElements(byLocator(lookup, path)).isEmpty());
+ }
+
+ public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath, Lookup waitElementLookup,
+ Lookup refreshElementLookup, int timeoutSeconds, int pollingEverySeconds) {
+ Runnable refreshFunction = () -> connector.clicks().click(() -> refreshElementPath, refreshElementLookup);
+ waitForElementAndRefresh(waitElementPath, waitElementLookup, refreshFunction, timeoutSeconds,
+ pollingEverySeconds, true);
+// waitForLazyElement(refreshElementPath, refreshElementLookup).click();
+ }
+
+ public void waitForElementAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction,
+ int timeoutSeconds, int pollingEverySeconds, boolean shouldBeWaitElementVisible) {
+ new FluentWait<>(connector.getDriver())
+ .withTimeout(Duration.ofSeconds(timeoutSeconds))
+ .pollingEvery(Duration.ofSeconds(pollingEverySeconds))
+ .ignoring(NoSuchElementException.class)
+ .ignoring(StaleElementReferenceException.class)
+ .ignoring(TimeoutException.class)
+ .withMessage("Element/s not present within " + timeoutSeconds + " seconds timeout. Wait and refreshed " +
+ "every: " + pollingEverySeconds + " seconds.\n For element: " + waitElementPath)
+ .until(driver -> {
+ if (shouldBeWaitElementVisible == true) {
+ return checkIfElementPresentAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
+ } else {
+ return checkIfElementDisappearAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
+ }
+ });
+ }
+
+ private WebElement checkIfElementPresentAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
+ Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
+ .filter(Collection::isEmpty)
+ .ifPresent(col -> refreshFunction.run());
+ return driver.findElement(byLocator(waitElementLookup, waitElementPath));
+ }
+
+ private boolean checkIfElementDisappearAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
+ Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
+ .ifPresent(col -> refreshFunction.run());
+ return driver.findElements(byLocator(waitElementLookup, waitElementPath)).size() == 0;
+ }
+
+ public void waitOrFailOnErrorElement(int timeoutSeconds, String pathForWaiting, String pathToFail, Lookup lookup) {
+ WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
+ wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
+ .pollingEvery(Duration.ofSeconds(3))
+ .withMessage("Element " + pathForWaiting + " or " + pathToFail + " isn't present after " + timeoutSeconds + " seconds")
+ .until(ExpectedConditions.or(
+ visibilityOfElementLocated(lookup, pathForWaiting),
+ visibilityOfElementLocated(lookup, pathToFail)));
+ if (connector.elementsChecks().isElementPresent(pathToFail, lookup)) {
+ throw new HarnessException(pathToFail + " is present instead of " + pathForWaiting);
+ }
+ }
+
+ /**
+ * Method for wait if all elements passed as parameter in defined state (Visible, Gone, Present in DOM) and there is not
+ * visible fail element. Fail element is for example error screen.
+ *
+ * Wait is implemented as fluent wait with 500ms polling and all parameters are transformed into expected conditions.
+ * These conditions are used in fluent wait.
+ *
+ *
+ * In case of fail element visible is thrown FailElementDisplayedException.
+ *
+ *
+ * @param timeoutSeconds - wait timeout
+ * @param lookup - define locator strategy
+ * @param until - state to check - Visible, Gone, Present in DOM
+ * @param xpathToFail - xpath for fail element/error screen
+ * @param elementsToCheck - elements to be wait for
+ */
+ public void waitOrFailOnErrorXpath(int timeoutSeconds, Lookup lookup, Until until, String xpathToFail, String... elementsToCheck) {
+ ExpectedCondition[] conditionsToCheck = Arrays.stream(elementsToCheck)
+ .map(elementToCheck -> resolveUntil(until).apply(byLocator(lookup, elementToCheck)))
+ .toArray(ExpectedCondition[]::new);
+
+ ExpectedCondition[] failElementNotDisplayed = new ExpectedCondition[]{BooleanExpectedConditions.failElementNotDisplayed(byLocator(Lookup.XPATH, xpathToFail))};
+
+ ExpectedCondition[] conditionsToEvaluate = ArrayUtils.addAll(failElementNotDisplayed, conditionsToCheck);
+
+ WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
+ wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
+ .pollingEvery(Duration.ofMillis(500L))
+ .withMessage(getErrorElementsMessage(timeoutSeconds, elementsToCheck))
+ .until(ExpectedConditions.and(conditionsToEvaluate));
+ }
+
+ private String getErrorElementsMessage(int timeoutSeconds, String... elements) {
+ String elementsInString = String.join(",", elements);
+ return "Some of listed elements isn't present after " + timeoutSeconds + " seconds. Elements: " + elementsInString;
+ }
+
+ public By byLocator(Lookup lookup, String path) {
+ return connector.resolveLookup(lookup).apply(path);
+ }
+
+ private ExpectedCondition visibilityOfElementLocated(Lookup lookup, String path) {
+ return ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path));
+ }
+
+ private Function> resolveUntil(Until until) {
+ switch (until) {
+ case VISIBLE:
+ return ExpectedConditions::visibilityOfElementLocated;
+ case PRESENT_IN_DOM:
+ return ExpectedConditions::presenceOfElementLocated;
+ case GONE:
+ return ExpectedConditions::invisibilityOfElementLocated;
+ case CLICKABLE:
+ return ExpectedConditions::elementToBeClickable;
+ default:
+ return ExpectedConditions::presenceOfElementLocated;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Windows.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Windows.java
new file mode 100644
index 0000000..7b317fd
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/web/Windows.java
@@ -0,0 +1,65 @@
+package cz.moneta.test.harness.connectors.web;
+
+import cz.moneta.test.harness.exception.HarnessException;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.NoSuchWindowException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.time.Duration;
+import java.util.Optional;
+
+public class Windows {
+
+ private final SeleniumWebConnector connector;
+
+ public Windows(SeleniumWebConnector connector) {
+ this.connector = connector;
+ }
+
+ public void switchToOtherWindow(int timeout, int expectedNumberOfWindows) {
+ WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeout));
+ wait.until(ExpectedConditions.numberOfWindowsToBe(expectedNumberOfWindows));
+ connector.getDriver()
+ .getWindowHandles()
+ .stream()
+ .filter(w -> getCurrentWindow().map(cw -> !cw.equals(w)).orElse(true))
+ .findFirst()
+ .map(otherWindow -> connector.getDriver().switchTo().window(otherWindow))
+ .orElseThrow(() -> new HarnessException(
+ "Not possible to switch to other window. Only current window is present."));
+ }
+
+ public void switchToWindowByTitle(String title) {
+ for (String winHandle : connector.getDriver().getWindowHandles()) {
+ if (connector.getDriver().switchTo().window(winHandle).getTitle().equals(title)) {
+ return;
+ }
+ }
+ throw new HarnessException("A browser window named \"" + title + "\" was not found");
+ }
+
+ public void switchWindowResolution(int width, int height) {
+ WebDriver.Window window = connector.getDriver().manage().window();
+ Dimension targetSize = new Dimension(width, height);
+ window.setSize(targetSize);
+ if (!targetSize.equals(window.getSize())) {
+ throw new IllegalStateException(String.format("Failed to switch window size to %d %d", width, height));
+ }
+ }
+
+ private Optional getCurrentWindow() {
+ try {
+ return Optional.of(connector
+ .getDriver()
+ .getWindowHandle());
+ } catch (NoSuchWindowException e) {
+ return Optional.empty();
+ }
+ }
+
+ public void closeCurrentWindow() {
+ connector.getDriver().close();
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/RemoteWso2Connector.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/RemoteWso2Connector.java
new file mode 100644
index 0000000..35615fd
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/RemoteWso2Connector.java
@@ -0,0 +1,78 @@
+package cz.moneta.test.harness.connectors.wso2;
+
+import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
+import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
+import cz.moneta.test.harness.connectors.rest.ResponseHandler;
+import cz.moneta.test.harness.connectors.rest.RestConnector;
+import cz.moneta.test.harness.context.ConfigAccessor;
+import org.apache.commons.lang3.tuple.Pair;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.Invocation;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+@Deprecated
+public class RemoteWso2Connector extends BaseRestConnector implements RestConnector {
+
+ private final WebTarget target;
+
+ public RemoteWso2Connector(ConfigAccessor store) {
+ try {
+ Client client = createHttpClient("RemoteWso2Connector");
+ this.target = Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
+ .map(client::target)
+ .map(t -> t.path("grid/admin"))
+ .map(t -> t.path(Wso2ConnectorServlet.class.getSimpleName()))
+ .orElseThrow(() -> new IllegalStateException("Failed to initialize remote Wso2 connector."));
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to initialize remote Wso2 connector.", e);
+ }
+ }
+
+ @Override
+ public Pair get(String path, Map properties, GenericType responseType,
+ Map headers) {
+ RemoteRestCallRequest get = new RemoteRestCallRequest(path, properties, headers);
+ return sendAndReceive("call-get", get, responseType);
+ }
+
+ @Override
+ public Pair post(String path, Entity> request, GenericType responseType, Map headers) {
+ RemoteRestCallRequest post = new RemoteRestCallRequest(path, request, headers);
+ return sendAndReceive("call-post", post, responseType);
+ }
+
+ @Override
+ public Pair delete(String path, Map properties, GenericType responseType,
+ Map headers) {
+ RemoteRestCallRequest delete = new RemoteRestCallRequest(path, properties, headers);
+ return sendAndReceive("call-delete", delete, responseType);
+ }
+
+ @Override
+ public Pair patch(String path, Entity> request, GenericType responseType, Map headers) {
+ RemoteRestCallRequest patch = new RemoteRestCallRequest(path, request, headers);
+ return sendAndReceive("call-patch", patch, responseType);
+ }
+
+ @Override
+ public RestConnector registerResponseHandler(
+ ResponseHandler>, Response> responseHandler) {
+ throw new UnsupportedOperationException("RemoteWso2Connector#registerResponseHandler not implemented");
+ }
+
+ @SuppressWarnings("unchecked")
+ private Pair sendAndReceive(String method, RemoteRestCallRequest request, GenericType responseType) {
+ Response response = target.path(method)
+ .request()
+ .post(Entity.json(request));
+ return Pair.of(response.getStatus(), response.readEntity(responseType));
+ }
+
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/TokenRenewalResponseHandler.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/TokenRenewalResponseHandler.java
new file mode 100644
index 0000000..75aa26b
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/TokenRenewalResponseHandler.java
@@ -0,0 +1,33 @@
+package cz.moneta.test.harness.connectors.wso2;
+
+import cz.moneta.test.harness.connectors.rest.ResponseHandler;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.http.HttpStatus;
+
+import jakarta.ws.rs.client.Invocation;
+import jakarta.ws.rs.core.Response;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class TokenRenewalResponseHandler implements ResponseHandler>, Response> {
+
+ private final Supplier tokenSupplier;
+
+ public TokenRenewalResponseHandler(Supplier tokenSupplier) {
+ this.tokenSupplier = tokenSupplier;
+ }
+
+ /**
+ * Checks whether the HTTP response status is 401 UNAUTHORIZED in which case it resends the original request with
+ * a renewed Authorization Bearer token
+ */
+ @Override
+ public Response handle(Pair> invocation, Response response) {
+ if (response.getStatus() == HttpStatus.SC_UNAUTHORIZED) {
+ Invocation.Builder builder = invocation.getLeft().header("Authorization", "Bearer " + tokenSupplier.get());
+ return invocation.getRight().apply(builder);
+ } else {
+ return response;
+ }
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/Wso2ConnectorServlet.java b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/Wso2ConnectorServlet.java
new file mode 100644
index 0000000..cf56a10
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/connectors/wso2/Wso2ConnectorServlet.java
@@ -0,0 +1,95 @@
+package cz.moneta.test.harness.connectors.wso2;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
+import cz.moneta.test.harness.connectors.common.ServletConfigAccessor;
+import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
+import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
+import cz.moneta.test.harness.context.ConfigAccessor;
+import org.apache.commons.lang3.tuple.Pair;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.GenericType;
+import java.io.IOException;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+public class Wso2ConnectorServlet extends HttpServlet {
+
+ private static final ObjectMapper JACKSON = new ObjectMapper()
+ .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
+ .registerModules(new ParameterNamesModule(), new Jdk8Module(), new JavaTimeModule());
+
+ private final ConfigAccessor store;
+ private final SimpleRestConnector connector;
+
+ public Wso2ConnectorServlet() {
+ this.store = new ServletConfigAccessor();
+ this.connector = new SimpleRestConnector(
+ store.getConfig("endpoints.wso2.url"), "Wso2ConnectorServlet");
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
+ Optional.ofNullable(req.getPathInfo())
+ .flatMap(pi -> Stream.>>>>of(
+ () -> Optional.of(pi).filter(i -> i.contains("/call-get")).map(i -> () -> callGet(req)),
+ () -> Optional.of(pi).filter(i -> i.contains("/call-post")).map(i -> () -> callPost(req)))
+ .map(Supplier::get)
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst())
+ .map(Supplier::get)
+ .ifPresent(r -> mapOntoResponse(r, resp));
+ }
+
+ private void mapOntoResponse(Pair result, HttpServletResponse resp) {
+ try {
+ resp.setContentType("application/json");
+ resp.setStatus(result.getLeft());
+ resp.getWriter().write(result.getRight());
+ } catch (IOException e) {
+ e.printStackTrace(); //TODO logging
+ }
+ }
+
+ private Pair callGet(HttpServletRequest req) {
+ try {
+ RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
+ return connector.get(
+ request.getPath(),
+ request.getGetProperties(),
+ new GenericType<>(String.class),
+ request.getHeaders());
+ } catch (Exception e) {
+ e.printStackTrace(); //TODO logging
+ return null;
+ }
+ }
+
+ private Pair callPost(HttpServletRequest req) {
+ try {
+ RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
+ return connector.post(
+ request.getPath(),
+ Entity.json(request.getRequest()),
+ new GenericType<>(String.class),
+ request.getHeaders());
+ } catch (Exception e) {
+ e.printStackTrace(); //TODO logging
+ return null;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ connector.close();
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/constants/HarnessConfigConstants.java b/test-harness/src/main/java/cz/moneta/test/harness/constants/HarnessConfigConstants.java
new file mode 100644
index 0000000..5ebf940
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/constants/HarnessConfigConstants.java
@@ -0,0 +1,21 @@
+package cz.moneta.test.harness.constants;
+
+public final class HarnessConfigConstants {
+
+ public static final String TEST_UNIQUE_ID = "TEST_UNIQUE_ID";
+ public static final String TEST_SHORT_ID = "TEST_SHORT_ID";
+ public static final String VAULT_USERNAME_CONFIG = "vault.username";
+ public static final String VAULT_PASSWORD_CONFIG = "vault.password";
+ public static final String VAULT_URL_CONFIG = "vault.url";
+ public static final String VAULT_WSO2_KEYS_PATH = "vault.client.secrets.path";
+ public static final String VAULT_CAGW_KEYS_PATH = "vault.cagw.client.secrets.path";
+ public static final String ENVIRONMENT_TYPE = "environment.type";
+ public static final String JENKINS_BUILD_URL = "jenkins.build.url";
+ public static final String SELENIUM_DOWNLOAD_DIRECTORY = "selenium.download.directory";
+ public static final String SELENIUM_GRID_PLATFORM = "selenium.grid.platform";
+
+ public static final long DEFAULT_REST_READ_TIMEOUT = 10L;
+
+ private HarnessConfigConstants() {
+ }
+}
diff --git a/test-harness/src/main/java/cz/moneta/test/harness/context/BaseStoreAccessor.java b/test-harness/src/main/java/cz/moneta/test/harness/context/BaseStoreAccessor.java
new file mode 100644
index 0000000..589d83f
--- /dev/null
+++ b/test-harness/src/main/java/cz/moneta/test/harness/context/BaseStoreAccessor.java
@@ -0,0 +1,204 @@
+package cz.moneta.test.harness.context;
+
+import cz.moneta.test.harness.annotations.TestContext;
+import cz.moneta.test.harness.endpoints.Endpoint;
+import cz.moneta.test.harness.exception.HarnessConfigurationException;
+import cz.moneta.test.harness.exception.HarnessException;
+import cz.moneta.test.harness.support.data.Generator;
+import cz.moneta.test.harness.support.data.GeneratorType;
+import cz.moneta.test.harness.support.util.Level;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static cz.moneta.test.harness.HarnessJunit5Extension.ACTIVE_ENDPOINTS;
+
+@SuppressWarnings("unchecked")
+@TestContext
+public abstract class BaseStoreAccessor implements StoreAccessor {
+
+ private static final Logger LOG = LogManager.getLogger("Harness");
+
+ private final Store rootStore;
+ private final Store globalStore;
+ private final Store endpointStore;
+ private final Store configStore;
+ private final Store generatorsStore;
+
+ public BaseStoreAccessor(Store rootStore, Store globalStore, Store endpointStore, Store configStore, Store generatorsStore) {
+ this.rootStore = rootStore;
+ this.globalStore = globalStore;
+ this.endpointStore = endpointStore;
+ this.configStore = configStore;
+ this.generatorsStore = generatorsStore;
+ }
+
+ @Override
+ public E getEndpoint(Class endpointClass, Object... args) {
+ Set, List