Skip to content

Commit

Permalink
implementation of load testing
Browse files Browse the repository at this point in the history
  • Loading branch information
hoeppner-dataport committed Jul 9, 2024
1 parent eb7b13d commit 02f5c47
Show file tree
Hide file tree
Showing 5 changed files with 384 additions and 0 deletions.
66 changes: 66 additions & 0 deletions apps/server/src/modules/board/loadtest/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Loadtesting the boards

The socket.io documentation suggests to use the tool artillery in order to load test a socket-io tool like our board-collaboration service.

For defining scenarios you need to use/create Yaml-files that define which operations with which parameters need to be executed in which order.

Some sceneraios were already prepared and are stored in the subfolder scenarios.

## install artillery

To run artillery from your local environment you need to install it first including an adapter that supports socketio-v3-websocket communication:

```sh
npm install -g artillery artillery-engine-socketio-v3
```

## manual execution

To execute a scenario you can run artillery from the shell / commandline...:

Using the `--variables` parameter it is possible to define several variables and there values that can be used in the scenerio-yaml-file:

- **target**: defines the base url for all requests (REST and WebSocket)
e.g. https://main.dbc.dbildungscloud.dev
- **token**: a valid JWT for the targeted system
- **board_id**: id of an existing board the tests should be executed on

```powershell
npx artillery run --variables "{'target': 'https://main.dbc.dbildungscloud.dev', 'token': 'eJ....', 'board_id': '668d0e03bf3689d12e1e86fb' }" './scenarios/3users.yml' --output artilleryreport.json
```

## visualizing the recorded results

It is possible to generate a HTML-report based on the recorded data.

```powershell
npx artillery report --output=$board_title.html artilleryreport.json
```

## automatic execution

You can run one of the existing scenarios by executing:

```bash
bash runScenario.sh
```

This will:

1. let you choose from scenario-files
2. create a fresh JWT-webtoken
3. create a fresh board (in one of the courses) the user has access to
4. name the board by a combination of datetime and the scenario name.
5. start the execution of the scenario against this newly created board
6. generate a html report in the end

## Open Todos:

- [x] split code into functions
- [x] suppress curl output
- [ ] add proper error handling of failed requests
- [x] cleanup scenarios
- x ] write documentation for linux => runScenario.sh
- [x] write documentation for windows => direct command
- [ ] properly handle credentials - env-vars?
- [ ] replace selects with parameters from script call
116 changes: 116 additions & 0 deletions apps/server/src/modules/board/loadtest/runScenario.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/bin/bash
function select_target() {
declare -a targets=("https://main.nbc.dbildungscloud.dev" "https://bc-6854-basic-load-tests.nbc.dbildungscloud.dev")
echo "Please select the target for the test:" >&2
select target in "${targets[@]}"; do
if [[ -n $target ]]; then
break
else
echo "Invalid selection. Please try again." >&2
fi
done
}

function select_scenario() {
# list files in the scenarios directory
scenarios_dir="./scenarios"
declare -a scenario_files=($(ls $scenarios_dir))

echo "Please select a scenario file for the test:" >&2
select scenario_file in "${scenario_files[@]}"; do
if [[ -n $scenario_file ]]; then
echo "You have selected: $scenario_file" >&2
break
else
echo "Invalid selection. Please try again." >&2
fi
done

scenario_name="${scenario_file%.*}"
}

function get_token() {
response=$(curl -s -f -X 'POST' \
"$target/api/v3/authentication/local" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "[email protected]",
"password": "Schulcloud1!"
}')

if [ $? -ne 0 ]; then
echo "ERROR: Failed to get token. Please check your credentials and target URL." >&2
exit 1
fi

token=$(echo $response | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
}

function get_course_id() {
response=$(curl -s -f -X 'GET' \
"$target/api/v3/courses" \
-H "Accept: application/json" \
-H "Authorization: Bearer $token")

if [ $? -ne 0 ]; then
echo "ERROR: Failed to get course list. Please check your credentials and target URL." >&2
exit 1
fi

course_id=$(echo $response | sed -n 's/.*"id":"\([^"]*\)".*/\1/p')
}

function create_board_title() {
current_date=$(date +%Y-%m-%d_%H:%M)
board_title="${current_date}_$1"
}

function create_board() {
response=$(curl -s -f -X 'POST' \
"$target/api/v3/boards" \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $token" \
-d "{
\"title\": \"$board_title\",
\"parentId\": \"$course_id\",
\"parentType\": \"course\",
\"layout\": \"columns\"
}")

if [ $? -ne 0 ]; then
echo "ERROR: Failed to create a board." >&2
exit 1
fi

board_id=$(echo $response | sed -n 's/.*"id":"\([^"]*\)".*/\1/p' )
}

#select_target
target=https://bc-6854-basic-load-tests.nbc.dbildungscloud.dev
echo "target: $target"


echo $1
select_scenario
echo "scenario_name: $scenario_name"

get_token
echo "token: ${token:0:50}..."

get_course_id
echo "course_id: $course_id"

create_board_title $scenario_name
echo "board_title: $board_title"

create_board
echo "board_id $board_id"

echo "board: $target/rooms/$board_id/board"
echo "Running artillery test..."

npx artillery run --variables "{\"target\": \"$target\", \"token\": \"$token\", \"board_id\": \"$board_id\" }" "./scenarios/$scenario_name.yml" --output artilleryreport.json

npx artillery report --output=$board_title.html artilleryreport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
config:
target: '{{ target }}'
engines:
socketio:
transport: ['websocket', 'polling']
path: '/board-collaboration'
socketio-v3:
path: '/board-collaboration'
timeout: 1000000
extraHeaders:
Cookie: 'jwt={{ token }}'

phases:
- duration: 300
arrivalRate: 10
maxVusers: 30

scenarios:
- name: create card
engine: socketio-v3
socketio-v3:
extraHeaders:
Cookie: 'jwt={{ token }}'
flow:
- think: 1

- emit:
channel: 'fetch-board-request'
data:
boardId: '{{ board_id }}'

- think: 1

- emit:
channel: 'create-column-request'
data:
boardId: '{{ board_id }}'
response:
on: 'create-column-success'
capture:
- json: $.newColumn.id
as: columnId

- think: 1

- loop:
- emit:
channel: 'create-card-request'
data:
columnId: '{{ columnId}}'
response:
on: 'create-card-success'
capture:
- json: $.newCard.id
as: cardId

- think: 1

- emit:
channel: 'fetch-card-request'
data:
cardIds:
- '{{ cardId }}'

- think: 2

- emit:
channel: 'update-card-title-request'
data:
cardId: '{{ cardId }}'
newTitle: 'Card {{ cardId}}'

- think: 1

count: 20
57 changes: 57 additions & 0 deletions apps/server/src/modules/board/loadtest/scenarios/3users.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
config:
target: '{{ target }}'
engines:
socketio:
transport: ['websocket', 'polling']
path: '/board-collaboration'
socketio-v3:
path: '/board-collaboration'
timeout: 1000000
extraHeaders:
Cookie: 'jwt={{ token }}'

phases:
- duration: 1
arrivalRate: 3

scenarios:
- name: create card
engine: socketio-v3
socketio-v3:
extraHeaders:
Cookie: 'jwt={{ token }}'
flow:
- log: '{{ target }}'
- think: 1

- emit:
channel: 'create-column-request'
data:
boardId: '{{ board_id }}'
response:
on: 'create-column-success'
capture:
- json: $.newColumn.id
as: columnId

- think: 1

- emit:
channel: 'create-card-request'
data:
columnId: '{{ columnId}}'
response:
on: 'create-card-success'
capture:
- json: $.newCard.id
as: cardId

- think: 1

- emit:
channel: 'update-card-title-request'
data:
cardId: '{{ cardId }}'
newTitle: 'One {{ cardId}}'

- think: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
config:
target: '{{ target }}'
engines:
socketio:
transport: ['websocket', 'polling']
path: '/board-collaboration'
socketio-v3:
path: '/board-collaboration'
timeout: 1000000
extraHeaders:
Cookie: 'jwt={{ token }}'

phases:
- duration: 2
arrivalRate: 50

scenarios:
- name: create card
engine: socketio-v3
socketio:
extraHeaders:
Cookie: 'jwt={{ token }}'
socketio-v3:
extraHeaders:
Cookie: 'jwt={{ token }}'
flow:
- think: 1

- emit:
channel: 'create-column-request'
data:
boardId: '{{ board_id }}'
response:
on: 'create-column-success'
capture:
- json: $.newColumn.id
as: columnId

- think: 2

- loop:
- emit:
channel: 'create-card-request'
data:
columnId: '{{ columnId}}'
response:
on: 'create-card-success'
capture:
- json: $.newCard.id
as: cardId

- think: 1

- emit:
channel: 'fetch-card-request'
data:
cardIds:
- '{{ cardId }}'

- think: 2

- emit:
channel: 'update-card-title-request'
data:
cardId: '{{ cardId }}'
newTitle: 'Card {{ cardId}}'

- think: 1

count: 6

0 comments on commit 02f5c47

Please sign in to comment.