[stress-tests] Documentation + necessary files to perform testing
By executing tests in this normalised fashion it is easier to compare results between different instances or different patch levels. Roughly speaking there are two kinds of tests: - VM tests: which summarise general performance of the instance, without taking DD into account - DD tests: which simulates many logins and interactions with DD, while recording the session as a user would pereceive it from a browser By using these we should be able to consistently compare and improve performance. The original dd-stress-test.tpl.jmx tests file was prepared by Teradisk with hardcoded instance, threadcount and duration values. Testing should now be performed with `vm-test.sh` and `dd-test.sh` respectively, and the template file should stay generic.nc-nginx-test
parent
eb6c14958b
commit
567bfd770d
|
@ -0,0 +1 @@
|
||||||
|
../stress-tests/README.md
|
|
@ -63,6 +63,7 @@ nav:
|
||||||
- integrations.ca.md
|
- integrations.ca.md
|
||||||
- post-install.ca.md
|
- post-install.ca.md
|
||||||
- contributing.ca.md
|
- contributing.ca.md
|
||||||
|
- stress-tests.md
|
||||||
- security.ca.md
|
- security.ca.md
|
||||||
- project-management.md
|
- project-management.md
|
||||||
#- upgrade.md
|
#- upgrade.md
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Potentially private files
|
||||||
|
users.csv
|
||||||
|
docs.csv
|
||||||
|
dd-stress-test.users.csv
|
||||||
|
dd-stress-test.docs.csv
|
||||||
|
# Transient files
|
||||||
|
dd-stress-test.jmx
|
||||||
|
apache-jmeter-*/
|
||||||
|
results/
|
||||||
|
vm-test.log
|
|
@ -0,0 +1,13 @@
|
||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
selenium = ">=4.8"
|
||||||
|
pyyaml = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3"
|
|
@ -0,0 +1,189 @@
|
||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "7668d1f96f732a37135c5c4d46a18b784dd1e38661f5e8968bb31dcc0cc6ef18"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"async-generator": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
|
||||||
|
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==1.10"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836",
|
||||||
|
"sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==22.2.0"
|
||||||
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||||
|
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2022.12.7"
|
||||||
|
},
|
||||||
|
"exceptiongroup": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e",
|
||||||
|
"sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"
|
||||||
|
],
|
||||||
|
"markers": "python_version < '3.11'",
|
||||||
|
"version": "==1.1.0"
|
||||||
|
},
|
||||||
|
"h11": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
|
||||||
|
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==0.14.0"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||||
|
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==3.4"
|
||||||
|
},
|
||||||
|
"outcome": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
|
||||||
|
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.2.0"
|
||||||
|
},
|
||||||
|
"pysocks": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
|
||||||
|
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
|
||||||
|
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
|
||||||
|
],
|
||||||
|
"version": "==1.7.1"
|
||||||
|
},
|
||||||
|
"pyyaml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf",
|
||||||
|
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||||
|
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||||
|
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||||
|
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
|
||||||
|
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
|
||||||
|
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
|
||||||
|
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
|
||||||
|
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
|
||||||
|
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||||
|
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||||
|
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||||
|
"sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782",
|
||||||
|
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||||
|
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||||
|
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||||
|
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||||
|
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||||
|
"sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1",
|
||||||
|
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||||
|
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||||
|
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||||
|
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||||
|
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||||
|
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||||
|
"sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d",
|
||||||
|
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||||
|
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||||
|
"sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7",
|
||||||
|
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||||
|
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||||
|
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||||
|
"sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358",
|
||||||
|
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||||
|
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||||
|
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||||
|
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||||
|
"sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f",
|
||||||
|
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||||
|
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==6.0"
|
||||||
|
},
|
||||||
|
"selenium": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:bd04eb41395605d9b2b65fe587f3fed21431da75512985c52772529e5e210c60",
|
||||||
|
"sha256:c48372905bffcc3b24bd55ab4683a07ee5e1f30fe918c59558ea5ee44cedf6c3"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==4.8.2"
|
||||||
|
},
|
||||||
|
"sniffio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
|
||||||
|
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.3.0"
|
||||||
|
},
|
||||||
|
"sortedcontainers": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88",
|
||||||
|
"sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"
|
||||||
|
],
|
||||||
|
"version": "==2.4.0"
|
||||||
|
},
|
||||||
|
"trio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
|
||||||
|
"sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==0.22.0"
|
||||||
|
},
|
||||||
|
"trio-websocket": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5b558f6e83cc20a37c3b61202476c5295d1addf57bd65543364e0337e37ed2bc",
|
||||||
|
"sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==0.9.2"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"extras": [
|
||||||
|
"socks"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72",
|
||||||
|
"sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||||
|
"version": "==1.26.14"
|
||||||
|
},
|
||||||
|
"wsproto": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065",
|
||||||
|
"sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Stress tests
|
||||||
|
|
||||||
|
By executing tests in this normalised fashion it is easier to compare results
|
||||||
|
between different instances or different patch levels.
|
||||||
|
|
||||||
|
This documents normalised stress-testing and references files under
|
||||||
|
[`stress-tests`][st].
|
||||||
|
|
||||||
|
[st]: https://gitlab.com/DD-workspace/DD/-/tree/main/stress-tests
|
||||||
|
|
||||||
|
|
||||||
|
## VM tests
|
||||||
|
|
||||||
|
- `vm-test.sh`: generate a text file to compare CPU and other factors across
|
||||||
|
VM types, providers or instances which may affect DD performance.
|
||||||
|
|
||||||
|
We can compare the resulting lgos just with, e.g. `vim -d`.
|
||||||
|
|
||||||
|
|
||||||
|
## DD tests
|
||||||
|
|
||||||
|
Currently these tests perform logins and interact with Nextcloud, but it would
|
||||||
|
be interesting to expand them to interact with other services.
|
||||||
|
|
||||||
|
### Directory contents
|
||||||
|
|
||||||
|
This directory contains following files:
|
||||||
|
|
||||||
|
- `dd-stress-test.tpl.jmx`: template to generate [JMeter][jm] tests to execute
|
||||||
|
- `dd-tests.sh`: helper script that generates the actual test plan files and
|
||||||
|
executes them. See `./dd-tests.sh --help`
|
||||||
|
- `dd-test-selenium.sh`: this gives us an idea of how a user would perceive
|
||||||
|
DD to be behaving while under load. Called by `./dd-tests.sh` by default.
|
||||||
|
|
||||||
|
|
||||||
|
### Results
|
||||||
|
|
||||||
|
Results will be saved in a `results` directory, where each subdirectory
|
||||||
|
corresponds to a stress test executed with `dd-test.sh`.
|
||||||
|
|
||||||
|
The naming scheme for those subdirectories is: `DOMAIN_THREADCOUNT_DURATION`
|
||||||
|
Where `THREADCOUNT` and `DURATION` are the corresponding [JMeter][jm]
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
Each results directory contains:
|
||||||
|
|
||||||
|
- `log`: the [JMeter][jm] log
|
||||||
|
- `results`: the [JMeter][jm] results file
|
||||||
|
- `html/index.html`: the interactive graphs for the data as produced by JMeter
|
||||||
|
- `selenium/session.html`: the session report as would be perceived by a user.
|
||||||
|
Note this requires Python3 and selenium and can be disabled by setting the
|
||||||
|
environment variable: `USE_SELENIUM=NO`.
|
||||||
|
|
||||||
|
[jm]: https://jmeter.apache.org/
|
|
@ -0,0 +1,822 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Originally designed by Teradisk.
|
||||||
|
Generalised and adapted by Evilham. -->
|
||||||
|
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
|
||||||
|
<hashTree>
|
||||||
|
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="NextCloud Tests" enabled="true">
|
||||||
|
<stringProp name="TestPlan.comments"></stringProp>
|
||||||
|
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||||
|
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||||
|
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments"/>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||||
|
</TestPlan>
|
||||||
|
<hashTree>
|
||||||
|
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="ThreadCount" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">ThreadCount</stringProp>
|
||||||
|
<stringProp name="Argument.value">10</stringProp> <!-- TC -->
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">Number of Threads</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Duration" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">Duration</stringProp>
|
||||||
|
<stringProp name="Argument.value">60</stringProp> <!-- DURATION -->
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">in seconds</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="RampUpPeriod" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">RampUpPeriod</stringProp>
|
||||||
|
<stringProp name="Argument.value">10</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">in seconds</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="IdPHost" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">IdPHost</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://sso.DD_DOMAIN</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">The main URL of your SSO Instance</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="IdPPort" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">IdPPort</stringProp>
|
||||||
|
<stringProp name="Argument.value">80</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="IdPContext" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">IdPContext</stringProp>
|
||||||
|
<stringProp name="Argument.value">idp</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="startupDelay" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">startupDelay</stringProp>
|
||||||
|
<stringProp name="Argument.value">1</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">in seconds</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="LoginSP" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">LoginSP</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://nextcloud.DD_DOMAIN/apps/user_saml/saml/login</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">Domain of registered NC using SAML</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="ProviderId" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">ProviderId</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://nextcloud.DD_DOMAIN/apps/user_saml/saml/metadata</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<stringProp name="Argument.desc">SAML EntityId for NC</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="ACSUrl" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">ACSUrl</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://nextcloud.DD_DOMAIN/apps/user_saml/saml/acs</stringProp>
|
||||||
|
<stringProp name="Argument.desc">SP ACS Url to post the final response/assertion back</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="NextCloud" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">NextCloud</stringProp>
|
||||||
|
<stringProp name="Argument.value">nextcloud.DD_DOMAIN</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="ClientId" elementType="Argument">
|
||||||
|
<stringProp name="Argument.name">ClientId</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://nextcloud.DD_DOMAIN/apps/user_saml/saml/metadata</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
<stringProp name="TestPlan.comments">Environment Variables that will need to be updated</stringProp>
|
||||||
|
</Arguments>
|
||||||
|
<hashTree/>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers"/>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Get Users/Passwords" enabled="true">
|
||||||
|
<stringProp name="filename">./dd-stress-test.users.csv</stringProp>
|
||||||
|
<stringProp name="fileEncoding"></stringProp>
|
||||||
|
<stringProp name="variableNames">User,Password</stringProp>
|
||||||
|
<stringProp name="delimiter">,</stringProp>
|
||||||
|
<boolProp name="quotedData">false</boolProp>
|
||||||
|
<boolProp name="recycle">true</boolProp>
|
||||||
|
<boolProp name="stopThread">false</boolProp>
|
||||||
|
<stringProp name="shareMode">shareMode.all</stringProp>
|
||||||
|
<stringProp name="TestPlan.comments">No Spaces between User, comma, and Password fields!!</stringProp>
|
||||||
|
<boolProp name="ignoreFirstLine">false</boolProp>
|
||||||
|
</CSVDataSet>
|
||||||
|
<hashTree/>
|
||||||
|
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV list folder/documents" enabled="true">
|
||||||
|
<stringProp name="filename">./dd-stress-test.docs.csv</stringProp>
|
||||||
|
<stringProp name="fileEncoding"></stringProp>
|
||||||
|
<stringProp name="variableNames">folder,document</stringProp>
|
||||||
|
<stringProp name="delimiter">,</stringProp>
|
||||||
|
<boolProp name="quotedData">false</boolProp>
|
||||||
|
<boolProp name="recycle">true</boolProp>
|
||||||
|
<boolProp name="stopThread">false</boolProp>
|
||||||
|
<stringProp name="shareMode">shareMode.all</stringProp>
|
||||||
|
<stringProp name="TestPlan.comments">No Spaces between Folder, comma, and DocumentID fields!!</stringProp>
|
||||||
|
<boolProp name="ignoreFirstLine">false</boolProp>
|
||||||
|
</CSVDataSet>
|
||||||
|
<hashTree/>
|
||||||
|
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
|
||||||
|
<collectionProp name="CookieManager.cookies"/>
|
||||||
|
<boolProp name="CookieManager.clearEachIteration">true</boolProp>
|
||||||
|
<boolProp name="CookieManager.controlledByThreadGroup">false</boolProp>
|
||||||
|
</CookieManager>
|
||||||
|
<hashTree/>
|
||||||
|
<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments"/>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.concurrentPool">4</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout">120000</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout">120000</stringProp>
|
||||||
|
</ConfigTestElement>
|
||||||
|
<hashTree/>
|
||||||
|
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="SAML Login Method" enabled="true">
|
||||||
|
<stringProp name="ThreadGroup.on_sample_error">stopthread</stringProp>
|
||||||
|
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||||
|
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||||
|
<intProp name="LoopController.loops">-1</intProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="ThreadGroup.num_threads">${ThreadCount}</stringProp>
|
||||||
|
<stringProp name="ThreadGroup.ramp_time">${RampUpPeriod}</stringProp>
|
||||||
|
<longProp name="ThreadGroup.start_time">1500501650000</longProp>
|
||||||
|
<longProp name="ThreadGroup.end_time">1500501650000</longProp>
|
||||||
|
<boolProp name="ThreadGroup.scheduler">true</boolProp>
|
||||||
|
<stringProp name="ThreadGroup.duration">${Duration}</stringProp>
|
||||||
|
<stringProp name="ThreadGroup.delay">${StartupDelay}</stringProp>
|
||||||
|
<stringProp name="TestPlan.comments">SAML Support Login Process</stringProp>
|
||||||
|
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||||
|
<boolProp name="ThreadGroup.delayedStart">true</boolProp>
|
||||||
|
</ThreadGroup>
|
||||||
|
<hashTree>
|
||||||
|
<ModuleController guiclass="ModuleControllerGui" testclass="ModuleController" testname="NEXTCLOUD Login" enabled="true">
|
||||||
|
<collectionProp name="ModuleController.node_path">
|
||||||
|
<stringProp name="764597751">Test Plan</stringProp>
|
||||||
|
<stringProp name="1997185859">NextCloud Tests</stringProp>
|
||||||
|
<stringProp name="-911831893">NEXTCLOUD Login</stringProp>
|
||||||
|
</collectionProp>
|
||||||
|
<stringProp name="TestPlan.comments">SAML2 SP calls the login page for CAS IdP</stringProp>
|
||||||
|
</ModuleController>
|
||||||
|
<hashTree/>
|
||||||
|
<ModuleController guiclass="ModuleControllerGui" testclass="ModuleController" testname="POST - Login User" enabled="true">
|
||||||
|
<collectionProp name="ModuleController.node_path">
|
||||||
|
<stringProp name="764597751">Test Plan</stringProp>
|
||||||
|
<stringProp name="1997185859">NextCloud Tests</stringProp>
|
||||||
|
<stringProp name="-497736779">POST - Login User</stringProp>
|
||||||
|
</collectionProp>
|
||||||
|
<stringProp name="TestPlan.comments">Logging into CAS Idp</stringProp>
|
||||||
|
</ModuleController>
|
||||||
|
<hashTree/>
|
||||||
|
<ModuleController guiclass="ModuleControllerGui" testclass="ModuleController" testname="POST - Authorization to NEXTCLOUD" enabled="true">
|
||||||
|
<collectionProp name="ModuleController.node_path">
|
||||||
|
<stringProp name="764597751">Test Plan</stringProp>
|
||||||
|
<stringProp name="1997185859">NextCloud Tests</stringProp>
|
||||||
|
<stringProp name="329930583">POST - Authorization to NEXTCLOUD</stringProp>
|
||||||
|
</collectionProp>
|
||||||
|
<stringProp name="TestPlan.comments">Send response from login to SP for processing</stringProp>
|
||||||
|
</ModuleController>
|
||||||
|
<hashTree/>
|
||||||
|
<ModuleController guiclass="ModuleControllerGui" testclass="ModuleController" testname="GET - Document" enabled="true">
|
||||||
|
<collectionProp name="ModuleController.node_path">
|
||||||
|
<stringProp name="764597751">Test Plan</stringProp>
|
||||||
|
<stringProp name="1997185859">NextCloud Tests</stringProp>
|
||||||
|
<stringProp name="-1050525960">GET - Document</stringProp>
|
||||||
|
</collectionProp>
|
||||||
|
<stringProp name="TestPlan.comments">Send response from login to SP for processing</stringProp>
|
||||||
|
</ModuleController>
|
||||||
|
<hashTree/>
|
||||||
|
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report 20 users" enabled="true">
|
||||||
|
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||||
|
<objProp>
|
||||||
|
<name>saveConfig</name>
|
||||||
|
<value class="SampleSaveConfiguration">
|
||||||
|
<time>true</time>
|
||||||
|
<latency>true</latency>
|
||||||
|
<timestamp>true</timestamp>
|
||||||
|
<success>true</success>
|
||||||
|
<label>true</label>
|
||||||
|
<code>true</code>
|
||||||
|
<message>true</message>
|
||||||
|
<threadName>true</threadName>
|
||||||
|
<dataType>true</dataType>
|
||||||
|
<encoding>false</encoding>
|
||||||
|
<assertions>true</assertions>
|
||||||
|
<subresults>true</subresults>
|
||||||
|
<responseData>false</responseData>
|
||||||
|
<samplerData>false</samplerData>
|
||||||
|
<xml>false</xml>
|
||||||
|
<fieldNames>true</fieldNames>
|
||||||
|
<responseHeaders>false</responseHeaders>
|
||||||
|
<requestHeaders>false</requestHeaders>
|
||||||
|
<responseDataOnError>false</responseDataOnError>
|
||||||
|
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||||
|
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||||
|
<bytes>true</bytes>
|
||||||
|
<sentBytes>true</sentBytes>
|
||||||
|
<url>true</url>
|
||||||
|
<threadCounts>true</threadCounts>
|
||||||
|
<idleTime>true</idleTime>
|
||||||
|
<connectTime>true</connectTime>
|
||||||
|
</value>
|
||||||
|
</objProp>
|
||||||
|
<stringProp name="filename"></stringProp>
|
||||||
|
</ResultCollector>
|
||||||
|
<hashTree/>
|
||||||
|
<ResultCollector guiclass="RespTimeGraphVisualizer" testclass="ResultCollector" testname="Response Time Graph" enabled="true">
|
||||||
|
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||||
|
<objProp>
|
||||||
|
<name>saveConfig</name>
|
||||||
|
<value class="SampleSaveConfiguration">
|
||||||
|
<time>true</time>
|
||||||
|
<latency>true</latency>
|
||||||
|
<timestamp>true</timestamp>
|
||||||
|
<success>true</success>
|
||||||
|
<label>true</label>
|
||||||
|
<code>true</code>
|
||||||
|
<message>true</message>
|
||||||
|
<threadName>true</threadName>
|
||||||
|
<dataType>true</dataType>
|
||||||
|
<encoding>false</encoding>
|
||||||
|
<assertions>true</assertions>
|
||||||
|
<subresults>true</subresults>
|
||||||
|
<responseData>false</responseData>
|
||||||
|
<samplerData>false</samplerData>
|
||||||
|
<xml>false</xml>
|
||||||
|
<fieldNames>true</fieldNames>
|
||||||
|
<responseHeaders>false</responseHeaders>
|
||||||
|
<requestHeaders>false</requestHeaders>
|
||||||
|
<responseDataOnError>false</responseDataOnError>
|
||||||
|
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||||
|
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||||
|
<bytes>true</bytes>
|
||||||
|
<sentBytes>true</sentBytes>
|
||||||
|
<url>true</url>
|
||||||
|
<threadCounts>true</threadCounts>
|
||||||
|
<idleTime>true</idleTime>
|
||||||
|
<connectTime>true</connectTime>
|
||||||
|
</value>
|
||||||
|
</objProp>
|
||||||
|
<stringProp name="filename"></stringProp>
|
||||||
|
</ResultCollector>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="NEXTCLOUD Login" enabled="true"/>
|
||||||
|
<hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET SAML2 Protected Page 2" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments"/>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">${LoginSP}/login</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
<stringProp name="TestPlan.comments">Calling secured SP page, that should then redirect to CAS login page</stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers">
|
||||||
|
<elementProp name="Sec-Fetch-Mode" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Mode</stringProp>
|
||||||
|
<stringProp name="Header.value">navigate</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Site" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Site</stringProp>
|
||||||
|
<stringProp name="Header.value">none</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Language" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Language</stringProp>
|
||||||
|
<stringProp name="Header.value">en-US,en;q=0.5</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-User" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-User</stringProp>
|
||||||
|
<stringProp name="Header.value">?1</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Pragma" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Pragma</stringProp>
|
||||||
|
<stringProp name="Header.value">no-cache</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept</stringProp>
|
||||||
|
<stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Upgrade-Insecure-Requests" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Upgrade-Insecure-Requests</stringProp>
|
||||||
|
<stringProp name="Header.value">1</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Cache-Control" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Cache-Control</stringProp>
|
||||||
|
<stringProp name="Header.value">no-cache</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Encoding" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Encoding</stringProp>
|
||||||
|
<stringProp name="Header.value">gzip, deflate, br</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="User-Agent" elementType="Header">
|
||||||
|
<stringProp name="Header.name">User-Agent</stringProp>
|
||||||
|
<stringProp name="Header.value">Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Dest" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Dest</stringProp>
|
||||||
|
<stringProp name="Header.value">document</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract RelayState Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">RelayState</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex"><input type="hidden" name="RelayState" value="(.+?)"</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract SAMLRequest Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">SAMLResponse</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex"><input type="hidden" name="SAMLResponse" value="(.+?)"</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Execution Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">Execution</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex">\;execution=(.+?)&amp;</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number"></stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract session_code Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">SessionCode</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex">session_code=(.*)&amp;ex</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number"></stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract tab_id Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">tab_id</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex">\;tab_id=(.*)\" me</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number"></stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract SAMLRequest Variable NC-21" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">URL</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">SAMLRequest</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex">SAMLRequest=(.+)</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
</hashTree>
|
||||||
|
<TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="POST - Login User" enabled="true"/>
|
||||||
|
<hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST User Login Credentials" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="username" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">true</boolProp>
|
||||||
|
<stringProp name="Argument.value">${User}</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
<stringProp name="Argument.name">username</stringProp>
|
||||||
|
<stringProp name="Argument.desc">false</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="password" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">true</boolProp>
|
||||||
|
<stringProp name="Argument.value">${Password}</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
<stringProp name="Argument.name">password</stringProp>
|
||||||
|
<stringProp name="Argument.desc">false</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">${IdPHost}/auth/realms/master/login-actions/authenticate?session_code=${SessionCode}&execution=${Execution}&client_id=${__urlencode(${ClientId})}&tab_id=${tab_id}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
<stringProp name="TestPlan.comments">POST Login Credentials for SAMLResponse</stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract RelayState Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">unescaped</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">RelayState</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex"><input type="hidden" name="RelayState" value="(.+?)"</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract SAMLResponse Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">unescaped</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">SAMLResponse</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex"><input type="hidden" name="SAMLResponse" value="(.+?)"</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number">1</stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
</hashTree>
|
||||||
|
<TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="POST - Authorization to NEXTCLOUD" enabled="true"/>
|
||||||
|
<hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST Authorization back to SP" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="RelayState" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">true</boolProp>
|
||||||
|
<stringProp name="Argument.value">${RelayState}</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
<stringProp name="Argument.name">RelayState</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="SAMLResponse" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">true</boolProp>
|
||||||
|
<stringProp name="Argument.value">${SAMLResponse}</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
<stringProp name="Argument.name">SAMLResponse</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">${ACSUrl}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="GET - Document" enabled="true"/>
|
||||||
|
<hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET - home" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="OCS-APIREQUEST" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">true</boolProp>
|
||||||
|
<stringProp name="Argument.name">OCS-APIREQUEST</stringProp>
|
||||||
|
<stringProp name="Argument.value">true</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain">${NextCloud}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Execution Variable" enabled="true">
|
||||||
|
<stringProp name="RegexExtractor.useHeaders">false</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.refname">requestToken</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.regex">data-requesttoken="(.+)\"</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.template">$1$</stringProp>
|
||||||
|
<stringProp name="RegexExtractor.default"></stringProp>
|
||||||
|
<stringProp name="RegexExtractor.match_number"></stringProp>
|
||||||
|
<stringProp name="Sample.scope">all</stringProp>
|
||||||
|
</RegexExtractor>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="List - Folder Documents" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="dir" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||||
|
<stringProp name="Argument.name">dir</stringProp>
|
||||||
|
<stringProp name="Argument.value">Documents</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain">${NextCloud}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">/apps/files</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers">
|
||||||
|
<elementProp name="Accept" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept</stringProp>
|
||||||
|
<stringProp name="Header.value">application/json, text/plain, */*</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="requesttoken" elementType="Header">
|
||||||
|
<stringProp name="Header.name">requesttoken</stringProp>
|
||||||
|
<stringProp name="Header.value">${requestToken}</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="PROFIND - File Redis PDF (viewer)" enabled="true">
|
||||||
|
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||||
|
<stringProp name="Argument.value"><?xml version="1.0"?>
|
||||||
|
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns" xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||||
|
<d:prop>
|
||||||
|
<d:getlastmodified />
|
||||||
|
<d:getetag />
|
||||||
|
<d:getcontenttype />
|
||||||
|
<d:resourcetype />
|
||||||
|
<oc:fileid />
|
||||||
|
<oc:permissions />
|
||||||
|
<oc:size />
|
||||||
|
<d:getcontentlength />
|
||||||
|
<d:quota-available-bytes />
|
||||||
|
<nc:has-preview />
|
||||||
|
<nc:mount-type />
|
||||||
|
<nc:is-encrypted />
|
||||||
|
<ocs:share-permissions />
|
||||||
|
<nc:share-attributes />
|
||||||
|
<oc:tags />
|
||||||
|
<oc:favorite />
|
||||||
|
<oc:owner-id />
|
||||||
|
<oc:owner-display-name />
|
||||||
|
<oc:share-types />
|
||||||
|
<oc:comments-unread />
|
||||||
|
</d:prop>
|
||||||
|
</d:propfind></stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain">${NextCloud}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port">443</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">/remote.php/dav/files/${User}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">PROPFIND</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers">
|
||||||
|
<elementProp name="Sec-Fetch-Mode" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Mode</stringProp>
|
||||||
|
<stringProp name="Header.value">cors</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Site" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Site</stringProp>
|
||||||
|
<stringProp name="Header.value">same-origin</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Language" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Language</stringProp>
|
||||||
|
<stringProp name="Header.value">en-US,en;q=0.5</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Origin" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Origin</stringProp>
|
||||||
|
<stringProp name="Header.value">https://${NextCloud}</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept</stringProp>
|
||||||
|
<stringProp name="Header.value">text/plain</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Depth" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Depth</stringProp>
|
||||||
|
<stringProp name="Header.value">0</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="X-Requested-With" elementType="Header">
|
||||||
|
<stringProp name="Header.name">X-Requested-With</stringProp>
|
||||||
|
<stringProp name="Header.value">XMLHttpRequest</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Content-Type" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Content-Type</stringProp>
|
||||||
|
<stringProp name="Header.value">text/plain;charset=UTF-8</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Encoding" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Encoding</stringProp>
|
||||||
|
<stringProp name="Header.value">gzip, deflate, br</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="User-Agent" elementType="Header">
|
||||||
|
<stringProp name="Header.name">User-Agent</stringProp>
|
||||||
|
<stringProp name="Header.value">Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Dest" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Dest</stringProp>
|
||||||
|
<stringProp name="Header.value">empty</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="" elementType="Header">
|
||||||
|
<stringProp name="Header.name">requesttoken</stringProp>
|
||||||
|
<stringProp name="Header.value">${requestToken}</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET - viewer" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments">
|
||||||
|
<elementProp name="file" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||||
|
<stringProp name="Argument.name">file</stringProp>
|
||||||
|
<stringProp name="Argument.value">https://${NextCloud}/remote.php/dav/files/${User}/${document}</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="canDownload" elementType="HTTPArgument">
|
||||||
|
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||||
|
<stringProp name="Argument.name">canDownload</stringProp>
|
||||||
|
<stringProp name="Argument.value">1</stringProp>
|
||||||
|
<stringProp name="Argument.metadata">=</stringProp>
|
||||||
|
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain">${NextCloud}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port">443</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">/apps/files_pdfviewer/</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers">
|
||||||
|
<elementProp name="Sec-Fetch-Mode" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Mode</stringProp>
|
||||||
|
<stringProp name="Header.value">navigate</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Site" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Site</stringProp>
|
||||||
|
<stringProp name="Header.value">same-origin</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Language" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Language</stringProp>
|
||||||
|
<stringProp name="Header.value">en-US,en;q=0.5</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Upgrade-Insecure-Requests" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Upgrade-Insecure-Requests</stringProp>
|
||||||
|
<stringProp name="Header.value">1</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Encoding" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Encoding</stringProp>
|
||||||
|
<stringProp name="Header.value">gzip, deflate, br</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="User-Agent" elementType="Header">
|
||||||
|
<stringProp name="Header.name">User-Agent</stringProp>
|
||||||
|
<stringProp name="Header.value">Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept</stringProp>
|
||||||
|
<stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Dest" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Dest</stringProp>
|
||||||
|
<stringProp name="Header.value">iframe</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="" elementType="Header">
|
||||||
|
<stringProp name="Header.name">requesttoken</stringProp>
|
||||||
|
<stringProp name="Header.value">${requestToken}</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Document" enabled="true">
|
||||||
|
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
|
||||||
|
<collectionProp name="Arguments.arguments"/>
|
||||||
|
</elementProp>
|
||||||
|
<stringProp name="HTTPSampler.domain">${NextCloud}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.port">443</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.contentEncoding">utf-8</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.path">/remote.php/dav/files/${User}/${document}</stringProp>
|
||||||
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||||
|
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||||
|
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||||
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
|
</HTTPSamplerProxy>
|
||||||
|
<hashTree>
|
||||||
|
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||||
|
<collectionProp name="HeaderManager.headers">
|
||||||
|
<elementProp name="Sec-Fetch-Mode" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Mode</stringProp>
|
||||||
|
<stringProp name="Header.value">cors</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Site" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Site</stringProp>
|
||||||
|
<stringProp name="Header.value">same-origin</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Language" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Language</stringProp>
|
||||||
|
<stringProp name="Header.value">en-US,en;q=0.5</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept-Encoding" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept-Encoding</stringProp>
|
||||||
|
<stringProp name="Header.value">gzip, deflate, br</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="User-Agent" elementType="Header">
|
||||||
|
<stringProp name="Header.name">User-Agent</stringProp>
|
||||||
|
<stringProp name="Header.value">Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Accept" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Accept</stringProp>
|
||||||
|
<stringProp name="Header.value">*/*</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="Sec-Fetch-Dest" elementType="Header">
|
||||||
|
<stringProp name="Header.name">Sec-Fetch-Dest</stringProp>
|
||||||
|
<stringProp name="Header.value">empty</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
<elementProp name="" elementType="Header">
|
||||||
|
<stringProp name="Header.name">requesttoken</stringProp>
|
||||||
|
<stringProp name="Header.value">${requestToken}</stringProp>
|
||||||
|
</elementProp>
|
||||||
|
</collectionProp>
|
||||||
|
</HeaderManager>
|
||||||
|
<hashTree/>
|
||||||
|
</hashTree>
|
||||||
|
</hashTree>
|
||||||
|
</hashTree>
|
||||||
|
</hashTree>
|
||||||
|
</jmeterTestPlan>
|
|
@ -0,0 +1,361 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import io
|
||||||
|
import pathlib
|
||||||
|
import random
|
||||||
|
import traceback
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from selenium import webdriver
|
||||||
|
|
||||||
|
# We need selenium 4
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
||||||
|
|
||||||
|
from typing import Any, Dict, Iterable, List
|
||||||
|
|
||||||
|
|
||||||
|
class DDSession:
|
||||||
|
driver: webdriver.Chrome
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
instance: str,
|
||||||
|
users_file: str,
|
||||||
|
out_dir: pathlib.Path,
|
||||||
|
stepTimeoutSeconds: int = 20,
|
||||||
|
):
|
||||||
|
self.instance = instance
|
||||||
|
self.users_file = [
|
||||||
|
[i.strip() for i in l.split(sep=",", maxsplit=1)]
|
||||||
|
for l in users_file.split("\n")
|
||||||
|
if l
|
||||||
|
]
|
||||||
|
self.out_dir = out_dir
|
||||||
|
self.stepTimeoutSeconds = stepTimeoutSeconds
|
||||||
|
|
||||||
|
if not self.out_dir.is_dir():
|
||||||
|
self.out_dir.mkdir()
|
||||||
|
|
||||||
|
self.start_time = datetime.utcnow()
|
||||||
|
self.last_time = self.start_time
|
||||||
|
self.screenshot_counter = 0
|
||||||
|
self.data: Dict[str, Any] = dict(
|
||||||
|
instance=self.instance, start_time=self.start_time, steps=[]
|
||||||
|
)
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def persist(self) -> None:
|
||||||
|
yaml.dump(self.data, (self.out_dir / "session.yml").open("w"))
|
||||||
|
(self.out_dir / "session.html").open("w").write(self.summarise()["html"])
|
||||||
|
|
||||||
|
def summarise(self) -> Dict[str, Any]:
|
||||||
|
return DDSession.summarise_data(self.data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def summarise_data(data) -> Dict[str, Any]:
|
||||||
|
import copy
|
||||||
|
from operator import getitem
|
||||||
|
from itertools import groupby
|
||||||
|
|
||||||
|
def summarise_item(g: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
|
if "substeps" in g[0]:
|
||||||
|
items = [sum((st["step_t"] for st in s["substeps"])) for s in g]
|
||||||
|
else:
|
||||||
|
items = [st["step_t"] for st in g]
|
||||||
|
return {
|
||||||
|
"max": max(items),
|
||||||
|
"count": len(items),
|
||||||
|
"average": sum(items) / len(items),
|
||||||
|
}
|
||||||
|
|
||||||
|
def summarise(d: Iterable[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
|
dict_data = dict()
|
||||||
|
for k, g in groupby(
|
||||||
|
sorted(d, key=lambda x: getitem(x, "step")),
|
||||||
|
key=lambda x: getitem(x, "step"),
|
||||||
|
):
|
||||||
|
dict_data[k] = summarise_item(list(g))
|
||||||
|
return dict_data
|
||||||
|
|
||||||
|
summary: Dict[str, Any] = dict()
|
||||||
|
summary["overview"] = summarise(data["steps"])
|
||||||
|
|
||||||
|
d = copy.deepcopy(data["steps"])
|
||||||
|
substeps = [
|
||||||
|
dict(st, step=f"{step['step']}: {st['substep']}")
|
||||||
|
for step in data["steps"]
|
||||||
|
for st in step["substeps"]
|
||||||
|
]
|
||||||
|
summary["stepbystep"] = summarise(substeps)
|
||||||
|
|
||||||
|
html = """<html>
|
||||||
|
<head><style>img { margin-bottom: 60px; }</style></head>
|
||||||
|
<body>
|
||||||
|
"""
|
||||||
|
html += f"""
|
||||||
|
<h1>{data['instance']}</h1>
|
||||||
|
<h2>{data['start_time']}</h2>
|
||||||
|
<details><summary>Summary</summary>
|
||||||
|
<pre>{yaml.dump(summary)}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
for s in data["steps"]:
|
||||||
|
html += f"""<h3>{s['step']}: {s['t']} s (+ {sum((st['step_t'] for st in s['substeps']))} s)</h3>"""
|
||||||
|
for st in s["substeps"]:
|
||||||
|
html += f"""<h4>{s['step']} - {st['substep']}: {st['t']} s (+ {st['step_t']} s)</h4>"""
|
||||||
|
html += f"""<img src="{st['png']}"/>"""
|
||||||
|
html += "</body></html>"
|
||||||
|
summary["html"] = html
|
||||||
|
return summary
|
||||||
|
|
||||||
|
@property
|
||||||
|
def executed_seconds(self) -> float:
|
||||||
|
return (datetime.utcnow() - self.start_time).total_seconds()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nextcloud_url(self) -> str:
|
||||||
|
return f"https://nextcloud.{self.instance}"
|
||||||
|
|
||||||
|
def reset(self, new_driver: bool = False) -> None:
|
||||||
|
# If needed:
|
||||||
|
# https://stackoverflow.com/a/72922584
|
||||||
|
if getattr(self, "driver", None) is None or new_driver:
|
||||||
|
if getattr(self, "driver", None) is not None:
|
||||||
|
self.driver.quit()
|
||||||
|
options = ChromeOptions()
|
||||||
|
for arg in [
|
||||||
|
"--headless",
|
||||||
|
"--no-sandbox",
|
||||||
|
"--disable-infobars",
|
||||||
|
"--disable-extensions",
|
||||||
|
"--disable-dev-shm-usage",
|
||||||
|
"window-size=1400,600",
|
||||||
|
]:
|
||||||
|
options.add_argument(arg)
|
||||||
|
self.driver = webdriver.Chrome(options=options)
|
||||||
|
msg = "Restart"
|
||||||
|
else:
|
||||||
|
self.driver.delete_all_cookies()
|
||||||
|
msg = "Cleaned cache"
|
||||||
|
self.screenshot("Browser", msg)
|
||||||
|
|
||||||
|
def perform_login(self) -> None:
|
||||||
|
# Wait until Keycloak is shown
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: d.find_elements(By.ID, "kc-form-login")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.screenshot("Login", "Request")
|
||||||
|
|
||||||
|
# Fill in form data for login
|
||||||
|
(username, password) = random.choice(self.users_file)
|
||||||
|
usernameField = self.driver.find_element(By.ID, "username")
|
||||||
|
usernameField.send_keys(username)
|
||||||
|
passwordField = self.driver.find_element(By.ID, "password")
|
||||||
|
passwordField.send_keys(password)
|
||||||
|
loginButton = self.driver.find_element(By.ID, "kc-login")
|
||||||
|
self.screenshot("Login", "Form filled")
|
||||||
|
|
||||||
|
loginButton.click()
|
||||||
|
|
||||||
|
self.screenshot("Login", "Form sent")
|
||||||
|
# TODO: wait for redirection middle step and session.screenshot it
|
||||||
|
|
||||||
|
def load_nextcloud_files(self) -> None:
|
||||||
|
# Start loading
|
||||||
|
self.driver.get(self.nextcloud_url)
|
||||||
|
|
||||||
|
# Wait until Nextcloud with Megamenu is shown or a login is requested
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: (
|
||||||
|
d.find_elements(By.ID, "kc-form-login")
|
||||||
|
+ d.find_elements(By.ID, "menu-apps-icon")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Detect whether or not a login is needed
|
||||||
|
login_required = self.driver.find_elements(By.ID, "kc-form-login")
|
||||||
|
if login_required:
|
||||||
|
# Perform login
|
||||||
|
self.perform_login()
|
||||||
|
|
||||||
|
self.screenshot("Nextcloud", "Loading")
|
||||||
|
|
||||||
|
# Wait until Nextcloud files app has loaded
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: d.find_elements(
|
||||||
|
By.CSS_SELECTOR, "#app-content-files td.selection"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.screenshot("Nextcloud", "Loaded")
|
||||||
|
|
||||||
|
def open_file_in_onlyoffice(self, file_base_name: str) -> None:
|
||||||
|
# Get and click docx file
|
||||||
|
# This assumes we are on the nextcloud files app with a FILE_BASE file
|
||||||
|
window_count = len(self.driver.window_handles)
|
||||||
|
docxFileLabel = self.driver.find_element(By.PARTIAL_LINK_TEXT, file_base_name)
|
||||||
|
docxFileLabel.click()
|
||||||
|
|
||||||
|
# Switch to OnlyOffice window
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
EC.number_of_windows_to_be(window_count + 1)
|
||||||
|
)
|
||||||
|
child = self.driver.window_handles[window_count]
|
||||||
|
self.driver.switch_to.window(child)
|
||||||
|
|
||||||
|
self.screenshot("OnlyOffice", "Opening")
|
||||||
|
|
||||||
|
# Wait for OnlyOffice to start loading
|
||||||
|
oofCSS = "div.app-onlyoffice #app iframe"
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: d.find_elements(By.CSS_SELECTOR, oofCSS)
|
||||||
|
)
|
||||||
|
self.screenshot("OnlyOffice", "Loading 1")
|
||||||
|
|
||||||
|
# Switch to its iframe
|
||||||
|
oof = WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: EC.frame_to_be_available_and_switch_to_it(
|
||||||
|
d.find_element(By.CSS_SELECTOR, oofCSS)
|
||||||
|
)(d)
|
||||||
|
)
|
||||||
|
self.screenshot("OnlyOffice", "Loading 2")
|
||||||
|
|
||||||
|
oofLoaded = lambda d: EC.element_to_be_clickable(
|
||||||
|
d.find_element(By.ID, "id-toolbar-btn-save")
|
||||||
|
)(d)
|
||||||
|
|
||||||
|
# Get the first loading screen
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: oofLoaded(d)
|
||||||
|
or d.find_elements(By.CSS_SELECTOR, "#loading-mask div.loader-page")
|
||||||
|
)
|
||||||
|
self.screenshot("OnlyOffice", "Loading 3")
|
||||||
|
|
||||||
|
# Wait for OnlyOffice second loading phase
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds).until(
|
||||||
|
lambda d: oofLoaded(d)
|
||||||
|
or d.find_elements(By.CSS_SELECTOR, "div.asc-loadmask-title")
|
||||||
|
)
|
||||||
|
self.screenshot("OnlyOffice", "Loading 4")
|
||||||
|
|
||||||
|
# Wait a for final loading (save button clickable)
|
||||||
|
WebDriverWait(self.driver, timeout=self.stepTimeoutSeconds * 3).until(oofLoaded)
|
||||||
|
self.screenshot("OnlyOffice", "Loaded")
|
||||||
|
|
||||||
|
# Close OnlyOffice window
|
||||||
|
self.driver.close()
|
||||||
|
# And change back to the main window
|
||||||
|
self.driver.switch_to.window(self.driver.window_handles[window_count - 1])
|
||||||
|
self.screenshot("OnlyOffice", "Closed")
|
||||||
|
|
||||||
|
def screenshot(self, step: str, substep_txt: str) -> None:
|
||||||
|
# Get data
|
||||||
|
scr_now = datetime.utcnow()
|
||||||
|
scr_s = (scr_now - self.start_time).total_seconds()
|
||||||
|
step_t = (scr_now - self.last_time).total_seconds()
|
||||||
|
scr_id = self.screenshot_counter
|
||||||
|
|
||||||
|
# Upgrade values
|
||||||
|
self.last_time = scr_now
|
||||||
|
self.screenshot_counter += 1
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
scr_fn = f"{scr_id:04d}.png"
|
||||||
|
scr_path = self.out_dir / scr_fn
|
||||||
|
# Get screenie
|
||||||
|
scr_img = self.driver.get_screenshot_as_png()
|
||||||
|
# Write it
|
||||||
|
open(scr_path, "wb").write(scr_img)
|
||||||
|
# Add to report
|
||||||
|
substep: Dict[str, Any] = dict(
|
||||||
|
id=scr_id, substep=substep_txt, step_t=step_t, t=scr_s, png=scr_fn
|
||||||
|
)
|
||||||
|
if self.data["steps"] and self.data["steps"][-1]["step"] == step:
|
||||||
|
self.data["steps"][-1]["substeps"].append(substep)
|
||||||
|
else:
|
||||||
|
self.data["steps"].append(dict(step=step, t=scr_s, substeps=[substep]))
|
||||||
|
|
||||||
|
|
||||||
|
def randomBool() -> bool:
|
||||||
|
return random.choice([True, False])
|
||||||
|
|
||||||
|
|
||||||
|
def main_test(
|
||||||
|
instance: str,
|
||||||
|
users_file: io.TextIOWrapper,
|
||||||
|
out_dir: pathlib.Path,
|
||||||
|
duration: int,
|
||||||
|
stepTimeoutSeconds: int = 20,
|
||||||
|
) -> DDSession:
|
||||||
|
session = DDSession(
|
||||||
|
instance=instance,
|
||||||
|
users_file=users_file.read(),
|
||||||
|
out_dir=out_dir,
|
||||||
|
stepTimeoutSeconds=20,
|
||||||
|
)
|
||||||
|
|
||||||
|
first_run = True
|
||||||
|
while session.executed_seconds < duration:
|
||||||
|
# 50% chance of requiring login reseting cookies
|
||||||
|
# First run always requires login, skip reset
|
||||||
|
if not first_run and randomBool():
|
||||||
|
# Possibly driver too
|
||||||
|
# 25% chance of cleaning all cache and reopening the browser
|
||||||
|
session.reset(new_driver=randomBool())
|
||||||
|
first_run = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Load nextcloud files
|
||||||
|
session.load_nextcloud_files()
|
||||||
|
# Open file in OnlyOffice
|
||||||
|
session.open_file_in_onlyoffice("template_1")
|
||||||
|
except Exception as ex:
|
||||||
|
session.screenshot("Exception", str(ex))
|
||||||
|
print(traceback.format_exc())
|
||||||
|
session.persist()
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
def main_summary(filename: io.TextIOWrapper) -> None:
|
||||||
|
data = yaml.safe_load(filename)
|
||||||
|
summary = DDSession.summarise_data(data)
|
||||||
|
html_fn = pathlib.Path(filename.name).with_suffix(".html")
|
||||||
|
open(html_fn, "w").write(summary["html"])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="DD Selenium tester", description="Run basic UI tests on DD"
|
||||||
|
)
|
||||||
|
subparsers = parser.add_subparsers(required=True)
|
||||||
|
test_parser = subparsers.add_parser("test")
|
||||||
|
|
||||||
|
test_parser.add_argument("instance")
|
||||||
|
test_parser.add_argument(
|
||||||
|
"-u",
|
||||||
|
"--users-file",
|
||||||
|
default="dd-stress-test.users.csv",
|
||||||
|
type=argparse.FileType("r"),
|
||||||
|
)
|
||||||
|
test_parser.add_argument("-d", "--duration", default=300, type=int)
|
||||||
|
test_parser.add_argument("-o", "--out-dir", default="results", type=pathlib.Path)
|
||||||
|
|
||||||
|
summary_parser = subparsers.add_parser("summary")
|
||||||
|
summary_parser.add_argument(
|
||||||
|
"filename", default="session.yml", type=argparse.FileType("r")
|
||||||
|
)
|
||||||
|
|
||||||
|
ns = parser.parse_args()
|
||||||
|
if "instance" in ns:
|
||||||
|
main_test(**vars(ns))
|
||||||
|
else:
|
||||||
|
main_summary(**vars(ns))
|
|
@ -0,0 +1,180 @@
|
||||||
|
#!/bin/sh -eu
|
||||||
|
|
||||||
|
# Process inputs
|
||||||
|
DD_DOMAIN="${1:-}"
|
||||||
|
tc="${2:-}"
|
||||||
|
duration="${3:-60}"
|
||||||
|
USE_SELENIUM="${USE_SELENIUM:-YES}"
|
||||||
|
SCRIPT_PATH="$(realpath "${0}")"
|
||||||
|
SCRIPT_NAME="$(basename "${0}")"
|
||||||
|
|
||||||
|
JMETER_DEFAULT="./apache-jmeter-5.5/bin/jmeter"
|
||||||
|
|
||||||
|
full_tests() {
|
||||||
|
# Runtime: 7 tests * 5 mins / test = 35 mins
|
||||||
|
# Cool-off periods: 30s * 6 = 3 mins
|
||||||
|
# Total: 38 mins
|
||||||
|
cooloff="30"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 10 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 20 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 30 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 60 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 100 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 300 300
|
||||||
|
sleep "${cooloff}"
|
||||||
|
"${SCRIPT_PATH}" "${DD_DOMAIN}" 600 300
|
||||||
|
}
|
||||||
|
|
||||||
|
help_users_file() {
|
||||||
|
cat <<-EOF
|
||||||
|
The format of the users.csv file must be:
|
||||||
|
USERNAME1,PASSWORD1
|
||||||
|
USERNAME2,PASSWORD2
|
||||||
|
...
|
||||||
|
|
||||||
|
Take care not to have any spaces between fields.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
help_jmeter() {
|
||||||
|
cat <<-EOF
|
||||||
|
Note this scripts depends on JMeter with some plugins enabled.
|
||||||
|
You can set the JMETER environment variable to its binary path.
|
||||||
|
If this variable is unset, ${JMETER_DEFAULT}
|
||||||
|
will be used, from this script's location.
|
||||||
|
See:
|
||||||
|
https://jmeter.apache.org/download_jmeter.cgi
|
||||||
|
https://jmeter-plugins.org/install/Install/
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
cat <<-EOF
|
||||||
|
Examples:
|
||||||
|
./${SCRIPT_NAME} DD_DOMAIN THREAD_COUNT [DURATION]
|
||||||
|
or:
|
||||||
|
./${SCRIPT_NAME} --full-tests DD_DOMAIN
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
help_jmeter
|
||||||
|
|
||||||
|
cat <<-EOF
|
||||||
|
|
||||||
|
When using --full-tests, a pre-selected combination of
|
||||||
|
THREAD_COUNT and DURATION will be used against DD_DOMAIN.
|
||||||
|
|
||||||
|
Where DD_DOMAIN is the base domain, e.g. if your DD instance's
|
||||||
|
Nextcloud can be accessed at nextcloud.example.org, the parameter
|
||||||
|
should be "example.org".
|
||||||
|
|
||||||
|
THREAD_COUNT refers to the amount of users that will be simulated.
|
||||||
|
|
||||||
|
DURATION is the total test time time in seconds. Defaults to 60.
|
||||||
|
|
||||||
|
Note that you MUST have a users.csv file in the current directory.
|
||||||
|
|
||||||
|
By default this script runs tests with selenium and documents the
|
||||||
|
session as would be perceived by a user.
|
||||||
|
You can disable this behaviour by setting the environment variable
|
||||||
|
USE_SELENIUM=NO.
|
||||||
|
EOF
|
||||||
|
help_users_file
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${DD_DOMAIN:-}" = "--full-tests" ]; then
|
||||||
|
shift # Consume operation argument
|
||||||
|
# Re-set global variable
|
||||||
|
DD_DOMAIN="${1:-}"
|
||||||
|
# Execute full suite
|
||||||
|
full_tests
|
||||||
|
# And exit
|
||||||
|
exit 0
|
||||||
|
elif [ "${1:-}" = "--help" ]; then
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
elif [ -z "${DD_DOMAIN:-}" ] || [ -z "${tc:-}" ]; then
|
||||||
|
help >> /dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
USERS_FILE="$(pwd)/users.csv"
|
||||||
|
DOCS_FILE="$(pwd)/docs.csv"
|
||||||
|
|
||||||
|
# Change current path
|
||||||
|
cd "$(dirname "${SCRIPT_PATH}")"
|
||||||
|
out_dir="$(pwd)/results/${DD_DOMAIN}_${tc}_${duration}"
|
||||||
|
|
||||||
|
if [ -f "${USERS_FILE}" ]; then
|
||||||
|
cat "${USERS_FILE}" > dd-stress-test.users.csv
|
||||||
|
else
|
||||||
|
printf "ERROR: missing file\t%s\n\n" "${USERS_FILE}" >> /dev/stderr
|
||||||
|
help_users_file >> /dev/stderr
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${DOCS_FILE}" ]; then
|
||||||
|
cat "${DOCS_FILE}" > dd-stress-test.docs.csv
|
||||||
|
else
|
||||||
|
cat > dd-stress-test.docs.csv <<-EOF
|
||||||
|
/,Readme.md
|
||||||
|
/,template.docx
|
||||||
|
/,template_1.docx
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
JMETER="${JMETER:-${JMETER_DEFAULT}}"
|
||||||
|
# Ensure JMeter is available / bootstrap it
|
||||||
|
if [ ! -f "${JMETER}" ]; then
|
||||||
|
echo "INFO: Could not find JMeter, attempting to download it" >> /dev/stderr
|
||||||
|
curl -L 'https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.5.tgz' | tar -xz
|
||||||
|
JMETER="${JMETER_DEFAULT}"
|
||||||
|
fi
|
||||||
|
if [ ! -f "${JMETER}" ]; then
|
||||||
|
printf "ERROR: missing JMeter\t%s\n\n" "${JMETER}" >> /dev/stderr
|
||||||
|
fi
|
||||||
|
# Ensure JMeter plugins are available / bootstrap them
|
||||||
|
JMETER_PLUGINS="$(dirname "${JMETER}")/../lib/ext/jmeter-plugins-manager-1.8.jar"
|
||||||
|
if [ ! -f "${JMETER_PLUGINS}" ]; then
|
||||||
|
echo "INFO: Could not find JMeter plugins, attempting to download them" >> /dev/stderr
|
||||||
|
curl -L 'https://jmeter-plugins.org/get/' > "${JMETER_PLUGINS}"
|
||||||
|
fi
|
||||||
|
if [ ! -f "${JMETER_PLUGINS}" ]; then
|
||||||
|
printf "ERROR: missing JMeter plugins\t%s\n\n" "${JMETER_PLUGINS}" >> /dev/stderr
|
||||||
|
fi
|
||||||
|
if [ ! -f "${JMETER}" ] || [ ! -f "${JMETER_PLUGINS}" ]; then
|
||||||
|
help_jmeter >> /dev/stderr
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up out dir
|
||||||
|
rm -rf "${out_dir}"
|
||||||
|
mkdir -p "${out_dir}"
|
||||||
|
|
||||||
|
# Adapt template
|
||||||
|
sed -E \
|
||||||
|
-e "s%([^>]*)>(.*)<\!-- TC.*$%\\1>${tc}</stringProp> <\!-- TC -->%" \
|
||||||
|
-e "s%([^>]*)>(.*)<\!-- DURATION.*$%\\1>${duration}</stringProp> <\!-- DURATION -->%" \
|
||||||
|
-e "s/DD_DOMAIN/${DD_DOMAIN}/g" \
|
||||||
|
dd-stress-test.tpl.jmx > dd-stress-test.jmx
|
||||||
|
|
||||||
|
# Call Selenium test process in parallel
|
||||||
|
if [ "${USE_SELENIUM}" = "YES" ]; then
|
||||||
|
printf "\n\nRunning parallel Selenium-based tests:\t%s\tover %s seconds\n\n" "${DD_DOMAIN}" "${duration}"
|
||||||
|
python3 dd-test-selenium.py test --duration "${duration}" --out-dir "${out_dir}/selenium" "${DD_DOMAIN}" 2>&1 > "${out_dir}/selenium.log" &
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute test
|
||||||
|
printf "\n\nAbout to test:\t%s\twith %s 'users' over %s seconds\n\n" \
|
||||||
|
"${DD_DOMAIN}" "${tc}" "${duration}"
|
||||||
|
env HEAP="-Xms2g -Xmx2g -XX:MaxMetaspaceSize=2g" "${JMETER}" -n -t dd-stress-test.jmx -l "${out_dir}/results" -e -o "${out_dir}/html"
|
||||||
|
mv jmeter.log "${out_dir}/log"
|
||||||
|
|
||||||
|
# Notify results
|
||||||
|
printf "\n\nYou can find the results at:\t%s\n\n" "${out_dir}"
|
|
@ -0,0 +1,20 @@
|
||||||
|
async-generator==1.10
|
||||||
|
attrs==22.2.0
|
||||||
|
certifi==2022.12.7
|
||||||
|
exceptiongroup==1.1.0
|
||||||
|
h11==0.14.0
|
||||||
|
idna==3.4
|
||||||
|
outcome==1.2.0
|
||||||
|
pip==22.3.1
|
||||||
|
PySocks==1.7.1
|
||||||
|
PyYAML==6.0
|
||||||
|
selenium==4.8.2
|
||||||
|
setuptools==67.0.0
|
||||||
|
sniffio==1.3.0
|
||||||
|
sortedcontainers==2.4.0
|
||||||
|
sqlite3==0.0.0
|
||||||
|
trio==0.22.0
|
||||||
|
trio-websocket==0.9.2
|
||||||
|
urllib3==1.26.14
|
||||||
|
wheel==0.38.4
|
||||||
|
wsproto==1.2.0
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/sh -eu
|
||||||
|
|
||||||
|
LOG_FILE="${LOG_FILE:-vm-test.log}"
|
||||||
|
|
||||||
|
SYSBENCH="$(command -v sysbench2)"
|
||||||
|
|
||||||
|
# Save stderr as well
|
||||||
|
exec 2>&1
|
||||||
|
|
||||||
|
echo "$(date)" | tee "${LOG_FILE}"
|
||||||
|
|
||||||
|
if [ -z "${SYSBENCH}" ]; then
|
||||||
|
echo "Skipping: sysbench tests (try: apt install sysbench)" | \
|
||||||
|
tee -a "${LOG_FILE}"
|
||||||
|
else
|
||||||
|
printf "\n\nfileio\n\n" | tee -a "${LOG_FILE}"
|
||||||
|
"${SYSBENCH}" fileio prepare --file-test-mode=rndrw --threads=4 --time=60 | tee -a "${LOG_FILE}"
|
||||||
|
"${SYSBENCH}" fileio run --file-test-mode=rndrw --threads=4 --time=60 | tee -a "${LOG_FILE}"
|
||||||
|
printf "\n\ncpu\n\n" | tee -a "${LOG_FILE}"
|
||||||
|
"${SYSBENCH}" cpu run --threads=4 --time=60 | tee -a "${LOG_FILE}"
|
||||||
|
printf "\n\nmemory\n\n" | tee -a "${LOG_FILE}"
|
||||||
|
"${SYSBENCH}" memory run --threads=4 --time=60 | tee -a "${LOG_FILE}"
|
||||||
|
fi
|
||||||
|
# Perform basic OpenSSL tests too
|
||||||
|
printf "\n\nCPU OpenSSL\n\n" | tee -a "${LOG_FILE}"
|
||||||
|
openssl speed --seconds 60 sha256 | tee -a "${LOG_FILE}"
|
Loading…
Reference in New Issue