Skip to content

Commit 264b76c

Browse files
ftheirsHermanObst
andauthored
Initialization phases (#9)
* WIP: having sequential index for each VU * common entry point for all tests * common account derivation step * clean siwe + getProfile test * point to index.ts entry point * mute/restor metrics functions * update derive accounts * update authentication * run with logs & index allocator script * minor improvements in config * example getProfile * clean up * fmt & clean up * update download test * clean up * update download test * improve download test * simplify running test command * clean up * update readme * fmt * clean up unauth calls * remove duplicated function * Update scripts/run-with-logs.ts Co-authored-by: Herman Obst Demaestri <hodemaestri@gmail.com> * Remove unnecessary endpoint query * rename run script * update with new script name * better private key error handling * add typecheck * fmt --------- Co-authored-by: Herman Obst Demaestri <hodemaestri@gmail.com>
1 parent 0a794df commit 264b76c

30 files changed

+981
-692
lines changed

.github/workflows/lint-and-format.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ jobs:
3131
- name: Install dependencies
3232
run: pnpm install --frozen-lockfile
3333

34+
- name: Typecheck
35+
run: pnpm typecheck
36+
3437
- name: Lint
3538
run: pnpm lint
3639

README.md

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This repo is an Artillery-based load testing suite to measure limits and identif
44

55
## Requirements
66

7-
- **Node.js >= 22** (SDK `@storagehub-sdk/core@0.3.4` declares this)
7+
- **Node.js >= 22**
88
- `pnpm`
99

1010
## Install
@@ -18,12 +18,13 @@ pnpm install
1818
Required:
1919
- `NETWORK` (`testnet`, `stagenet` or `local`)
2020

21-
Optional:
22-
- none (health checks are unauthenticated)
21+
Per-test required:
22+
- `TEST_MNEMONIC` (required by tests that derive accounts and do SIWE)
23+
- `FILE_KEY` (required by the download test)
2324

2425
## Network configuration
2526

26-
Network URLs/IDs are intentionally **hardcoded** in `src/networks.ts` and are copied from `datahaven-monitor`:
27+
Network URLs/IDs are intentionally **hardcoded** in `src/networks.ts`:
2728
- **Testnet**: MSP `https://deo-dh-backend.testnet.datahaven-infra.network`
2829
- **Stagenet**: MSP `https://deo-dh-backend.stagenet.datahaven-infra.network`
2930
- **Local**: MSP `http://127.0.0.1:8080`, RPC `http://127.0.0.1:9888`
@@ -44,28 +45,39 @@ Local notes:
4445
- `pnpm fmt:fix` — apply formatting
4546
- `pnpm lint` — check lint rules
4647
- `pnpm lint:fix` — apply safe lint fixes
47-
- `pnpm test` — build -> artillery
48-
- `pnpm test:msp-unauth` — standalone unauth MSP load test (no SIWE, no keys)
49-
- `pnpm test:download` — file download load test (requires SIWE auth + FILE_KEY)
48+
- `pnpm test:run scenarios/<file>.yml` — run any scenario (build + logs wrapper)
5049

51-
Examples (local):
50+
List available scenarios:
5251

5352
```bash
54-
NETWORK=local pnpm test:msp-unauth
53+
ls scenarios
5554
```
5655

56+
Run one:
57+
5758
```bash
58-
NETWORK=local STORAGEHUB_PRIVATE_KEY=0x... pnpm test
59+
NETWORK=stagenet pnpm test:run scenarios/<file>.yml
5960
```
6061

61-
Examples (local):
62+
Examples (replace the scenario file with anything from `ls scenarios`):
63+
64+
```bash
65+
NETWORK=local pnpm test:run scenarios/artillery.msp-unauth.yml
66+
```
6267

6368
```bash
64-
NETWORK=local pnpm test:msp-unauth
69+
LOG_LEVEL=info LOG_CONSOLE=true \
70+
NETWORK=stagenet \
71+
TEST_MNEMONIC="test test test test test test test test test test test junk" \
72+
pnpm test:run scenarios/examples.getProfile.yml
6573
```
6674

6775
```bash
68-
LOG_LEVEL=info LOG_CONSOLE=true TEST_MNEMONIC="test test test test test test test test test test test junk" NETWORK=local pnpm test
76+
LOG_LEVEL=info LOG_CONSOLE=true \
77+
NETWORK=stagenet \
78+
TEST_MNEMONIC="test test test test test test test test test test test junk" \
79+
FILE_KEY="<your-file-key>" \
80+
pnpm test:run scenarios/download.yml
6981
```
7082

7183
## Logging
@@ -81,11 +93,11 @@ Env vars:
8193
Examples:
8294

8395
```bash
84-
LOG_LEVEL=debug NETWORK=testnet pnpm test
96+
LOG_LEVEL=debug NETWORK=testnet pnpm test:run scenarios/artillery.msp-unauth.yml
8597
```
8698

8799
```bash
88-
LOG_LEVEL=info LOG_FILE=./artillery.log NETWORK=testnet pnpm test
100+
LOG_LEVEL=info LOG_FILE=./artillery.log NETWORK=testnet pnpm test:run scenarios/artillery.msp-unauth.yml
89101
```
90102

91103
## Standalone MSP unauth load test
@@ -99,11 +111,10 @@ It uses `NETWORK=testnet|stagenet` and the MSP base URL from `src/networks.ts`.
99111
Run:
100112

101113
```bash
102-
NETWORK=stagenet pnpm test:msp-unauth
114+
NETWORK=stagenet pnpm test:run scenarios/artillery.msp-unauth.yml
103115
```
104116

105117
Knobs (optional):
106-
- `ARTILLERY_WORKERS=4` (true parallel local processes; spawns N concurrent Artillery runs)
107118
- `VU_SLEEP_MIN_MS=50` / `VU_SLEEP_MAX_MS=250` (jitter per request loop)
108119
- `MSP_TIMEOUT_MS=60000` (override HTTP timeout)
109120

@@ -114,55 +125,74 @@ Metrics emitted (counters + histograms):
114125

115126
## Download load test
116127

117-
This test authenticates via SIWE and downloads a file from the MSP, measuring throughput and latency.
128+
This test performs init steps (derive + SIWE) and downloads a file from the MSP, measuring throughput and latency.
118129

119130
Required env vars:
120-
- `NETWORK` (`testnet` or `stagenet`)
131+
- `NETWORK` (`testnet`, `stagenet` or `local`)
132+
- `TEST_MNEMONIC`
121133
- `FILE_KEY` (the file key/hash to download)
122134

123135
Run:
124136

125137
```bash
126-
NETWORK=stagenet FILE_KEY=<your-file-key> pnpm test:download
138+
NETWORK=stagenet FILE_KEY=<your-file-key> pnpm test:run scenarios/download.yml
127139
```
128140

129141
Knobs (optional):
130-
- `ARTILLERY_WORKERS=4` (parallel local processes)
131142
- `LOG_LEVEL=info` (see Logging section)
132143

133144
Metrics emitted:
134-
- `download.siwe.ok`, `download.siwe.ms` (SIWE auth)
135145
- `download.file.ok`, `download.file.ms` (file download)
136146
- `download.bytes` (total bytes downloaded per request)
137-
- `download.siwe.err`, `download.file.err` (error counters)
147+
- `download.file.err` (error counter)
148+
- `auth.siwe.err` (only if SIWE fails; init steps are muted so only errors surface)
149+
150+
## How initialization + mute metrics works
138151

139-
## Per-VU private keys (Artillery payload)
152+
Most scenarios follow the same pattern:
153+
- **Init** (muted): `deriveAccount``SIWE`
154+
- **Actions** (not muted): call one or more action processors (e.g. `actionGetProfile`, `downloadFile`)
140155

141-
This test expects a per-VU `privateKey` variable from `config.payload` in `scenarios/artillery.yml`.
156+
The muting is controlled by two processor steps:
157+
- `muteMetrics`: while muted, the metrics helper will **only emit `*.err` counters**; it drops ok counters + histograms.
158+
- `unmuteMetrics`: restores normal metric emission for the action phase.
142159

143-
1) Create `data/private_keys.csv` (ignored by git), based on the example:
144-
- `data/private_keys.example.csv`
160+
This keeps summaries focused on action timings while still surfacing setup/auth failures.
145161

146-
Notes:
147-
- `pnpm preflight` will use `STORAGEHUB_PRIVATE_KEY` **if set**, otherwise it will use the **first key** in `data/private_keys.csv`.
148-
- If Artillery does not inject `privateKey` into `context.vars` (depends on engine/runtime), the scenario will fall back to reading keys directly from `data/private_keys.csv` (round-robin).
162+
### What `deriveAccount` does
163+
- Picks a unique account index (via the local index allocator started by `scripts/run-scenario.ts`)
164+
- Derives an account from `TEST_MNEMONIC`
165+
- Persists `privateKey` (and derivation metadata) into Artillery vars for later steps
149166

150-
2) Run:
167+
### What `SIWE` does
168+
- Reads the derived `privateKey`
169+
- Calls the SDK SIWE auth (`mspClient.auth.SIWE(...)`)
170+
- Persists the resulting `__siweSession` into Artillery vars
171+
172+
## How to add a new test
173+
174+
1) **Create a scenario file** under `scenarios/` (for example `scenarios/myTest.yml`).
175+
176+
2) **Use the standard template**:
177+
- `config.processor: "../dist/src/processors/index.js"`
178+
- Init steps (muted): `muteMetrics``deriveAccount``SIWE``unmuteMetrics`
179+
- Then call your action processor(s)
180+
181+
3) Run it via the generic runner:
151182

152183
```bash
153-
NETWORK=stagenet LOG_LEVEL=info pnpm test
184+
NETWORK=stagenet pnpm test:run scenarios/myTest.yml
154185
```
155186

156-
## Scenario output
187+
(Optional) If you want a shortcut alias, add `test:myTest`: `"pnpm test:run scenarios/myTest.yml"`.
188+
189+
## Metrics (quick orientation)
157190

158-
Counters:
159-
- `sdk.storagehub.connect.ok`
160-
- `sdk.msp.connect.ok`
161-
- `sdk.disconnect.ok`
162-
- `sdk.connect.error`
191+
Metrics depend on the scenario and processor functions used. Common ones:
192+
- `msp.health.ok`, `msp.health.ms`, `msp.info.ok`, `msp.info.ms`, `msp.req.err`
193+
- `action.getProfile.ok`, `action.getProfile.ms`, `action.getProfile.err`
194+
- `download.file.ok`, `download.file.ms`, `download.bytes`, `download.file.err`
163195

164-
Timings:
165-
- `sdk.storagehub.connect.ms`
166-
- `sdk.msp.connect.ms`
196+
When init steps are wrapped with `muteMetrics`/`unmuteMetrics`, only `*.err` counters from init will appear in the summary (ok + histograms are muted).
167197

168198

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
"fmt:fix": "biome format . --write",
1111
"lint": "biome lint .",
1212
"lint:fix": "biome lint . --write",
13+
"typecheck": "tsc -p tsconfig.json --noEmit",
1314
"build": "tsc -p tsconfig.json",
14-
"test": "pnpm build && pnpm exec artillery run scenarios/artillery.yml",
15-
"test:msp-unauth": "pnpm build && pnpm exec tsx scripts/run-artillery-parallel.ts scenarios/artillery.msp-unauth.yml",
16-
"test:siwe:byIndex": "pnpm build && pnpm exec artillery run scenarios/siwe.byIndex.yml",
17-
"test:siwe:sequential": "pnpm build && pnpm exec artillery run scenarios/siwe.sequential.yml",
18-
"test:siwe:random": "pnpm build && pnpm exec artillery run scenarios/siwe.random.yml",
19-
"test:siwe:auth:sequential": "pnpm build && pnpm exec artillery run scenarios/siwe.auth.sequential.yml",
20-
"test:download": "pnpm build && pnpm exec tsx scripts/run-artillery-parallel.ts scenarios/artillery.download.yml"
15+
"with-logs": "pnpm exec tsx scripts/run-scenario.ts --",
16+
"run:with-logs": "pnpm build && pnpm with-logs",
17+
"test:run": "pnpm run:with-logs artillery run",
18+
19+
"test:unauth": "pnpm test:run scenarios/msp.unauth.yml",
20+
"test:examples.getProfile": "pnpm test:run scenarios/examples.getProfile.yml",
21+
"test:download": "pnpm test:run scenarios/download.yml"
2122
},
2223
"dependencies": {
2324
"@storagehub-sdk/core": "0.4.0",

scenarios/artillery.yml

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
config:
22
target: "http://localhost"
3-
processor: "../dist/src/processors/download.js"
3+
processor: "../dist/src/processors/index.js"
4+
variables:
5+
# Account index selection (mnemonic-based):
6+
ACCOUNT_MODE: sequential
7+
ACCOUNT_INDEX_START: 0
8+
ACCOUNT_INDEX_COUNT: 1000
49

510
phases:
611
- name: warmup
@@ -24,5 +29,11 @@ config:
2429
scenarios:
2530
- name: download_file
2631
flow:
32+
# Init (muted): derive + SIWE without polluting summary metrics
33+
- function: muteMetrics
34+
- function: deriveAccount
35+
- function: SIWE
36+
- function: unmuteMetrics
37+
# Action
2738
- function: downloadFile
2839

scenarios/examples.getProfile.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
config:
2+
# Template scenario: init steps + action step (metrics-focused)
3+
#
4+
# Notes:
5+
# - Provide `NETWORK` and `TEST_MNEMONIC` via env vars.
6+
# - Use `muteMetrics`/`unmuteMetrics` to keep init step metrics out of the summary
7+
# while still surfacing error counters (e.g. `*.err`).
8+
target: "http://localhost"
9+
processor: "../dist/src/processors/index.js"
10+
variables:
11+
# Account index selection:
12+
# - byIndex: all VUs use ACCOUNT_INDEX unless payload var `accountIndex` is provided
13+
# - sequential/random: uses START/COUNT
14+
ACCOUNT_MODE: byIndex
15+
# Fallback if allocator isn't used (e.g. running Artillery directly without pnpm script)
16+
ACCOUNT_INDEX: 0
17+
phases:
18+
- name: init-and-actions
19+
duration: 60
20+
arrivalRate: 5
21+
scenarios:
22+
- name: getProfile
23+
flow:
24+
# Init (muted): derive + SIWE without polluting summary metrics
25+
- function: muteMetrics
26+
- function: deriveAccount
27+
- function: SIWE
28+
- function: unmuteMetrics
29+
# Actions: focus metrics here
30+
- loop:
31+
- function: actionGetProfile
32+
count: 100
33+
34+
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
config:
22
target: "http://localhost"
3-
processor: "../dist/src/processors/msp-unauth.js"
3+
processor: "../dist/src/processors/index.js"
44
variables:
55
# IMPORTANT: keep this in sync with the total duration of all phases (seconds).
66
# (10 + 20 + 40 + 60 + 10) = 140
@@ -32,8 +32,9 @@ config:
3232
rampTo: 1
3333

3434
scenarios:
35-
- name: msp_unauth_load
35+
- name: MSP_Unauthenticated_calls
3636
flow:
37-
- function: mspUnauthLoad
37+
- function: getHealth
38+
- function: getInfo
3839

3940

scenarios/siwe.auth.sequential.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.

scenarios/siwe.byIndex.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

scenarios/siwe.random.yml

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)