From 0bbb1f9faae674d97b33cfd407ac8369897168e8 Mon Sep 17 00:00:00 2001 From: lucarlig Date: Thu, 27 Aug 2026 13:46:59 +0100 Subject: [PATCH] Remove Goose load tests Signed-off-by: lucarlig --- Cargo.lock | 361 ------ Cargo.toml | 1 - README.md | 19 +- _context/wiki/index.md | 2 +- _context/wiki/performance.md | 25 +- _context/wiki/project.md | 1 - crates/contextforge-load-test/Cargo.toml | 23 - crates/contextforge-load-test/src/main.rs | 212 ---- deny.toml | 5 - ...gateway-performance-report-2026-05-01.html | 1046 ----------------- 10 files changed, 13 insertions(+), 1682 deletions(-) delete mode 100644 crates/contextforge-load-test/Cargo.toml delete mode 100644 crates/contextforge-load-test/src/main.rs delete mode 100644 reports/cf-gateway-performance-report-2026-05-01.html diff --git a/Cargo.lock b/Cargo.lock index a1f9d8c9..7ba34497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,12 +164,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" -[[package]] -name = "arrayvec" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" - [[package]] name = "async-compression" version = "0.4.43" @@ -364,15 +358,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - [[package]] name = "brotli" version = "8.0.4" @@ -653,46 +638,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "contextforge-load-test" -version = "0.1.0" -dependencies = [ - "goose", - "http", - "reqwest", - "tokio", - "uuid", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" -dependencies = [ - "cookie", - "document-features", - "idna", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -851,17 +796,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctrlc" -version = "3.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" -dependencies = [ - "dispatch2", - "nix", - "windows-sys 0.61.2", -] - [[package]] name = "darling" version = "0.24.0" @@ -896,12 +830,6 @@ dependencies = [ "syn 3.0.3", ] -[[package]] -name = "data-encoding" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" - [[package]] name = "deranged" version = "0.5.8" @@ -918,18 +846,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", -] - [[package]] name = "displaydoc" version = "0.2.7" @@ -941,21 +857,6 @@ dependencies = [ "syn 3.0.3", ] -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "downcast-rs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" - [[package]] name = "dunce" version = "1.0.5" @@ -1046,9 +947,6 @@ name = "fastrand" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" -dependencies = [ - "getrandom 0.4.3", -] [[package]] name = "find-msvc-tools" @@ -1066,18 +964,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "fastrand", - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1252,56 +1138,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "goose" -version = "0.19.0-dev" -source = "git+https://github.com/tag1consulting/goose#614f75149cab5a963a8bc26d7b62639dedfa7a2b" -dependencies = [ - "async-trait", - "chrono", - "ctrlc", - "downcast-rs", - "flume", - "futures", - "gumdrop", - "http", - "itertools", - "lazy_static", - "log", - "num-format", - "rand 0.9.5", - "regex", - "reqwest", - "serde", - "serde_json", - "simplelog", - "strum", - "strum_macros", - "tokio", - "tokio-tungstenite", - "url", -] - -[[package]] -name = "gumdrop" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc700f989d2f6f0248546222d9b4258f5b02a171a431f8285a81c08142629e3" -dependencies = [ - "gumdrop_derive", -] - -[[package]] -name = "gumdrop_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f9bd3449d77e7831a18abfb7ba2f99ee813dfd15b8c2167c9a54ba20aa99d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "h2" version = "0.4.16" @@ -1746,21 +1582,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.33" @@ -1837,18 +1658,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "nix" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1874,16 +1683,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -1912,15 +1711,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "oauth2" version = "5.0.0" @@ -1940,21 +1730,6 @@ dependencies = [ "url", ] -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - [[package]] name = "once_cell" version = "1.21.4" @@ -2196,22 +1971,6 @@ dependencies = [ "prost", ] -[[package]] -name = "psl-types" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" - -[[package]] -name = "publicsuffix" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" -dependencies = [ - "idna", - "psl-types", -] - [[package]] name = "quinn" version = "0.11.11" @@ -2463,8 +2222,6 @@ checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", "bytes", - "cookie", - "cookie_store", "encoding_rs", "futures-channel", "futures-core", @@ -2487,7 +2244,6 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls", @@ -2731,12 +2487,6 @@ dependencies = [ "syn 3.0.3", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "secret-string" version = "0.0.2" @@ -2863,17 +2613,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha1" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - [[package]] name = "sha1_smol" version = "1.0.1" @@ -2959,17 +2698,6 @@ dependencies = [ "time", ] -[[package]] -name = "simplelog" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0" -dependencies = [ - "log", - "termcolor", - "time", -] - [[package]] name = "slab" version = "0.4.12" @@ -2992,15 +2720,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "spin" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" -dependencies = [ - "lock_api", -] - [[package]] name = "sse-stream" version = "0.2.5" @@ -3026,24 +2745,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "subtle" version = "2.6.1" @@ -3066,17 +2767,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.119" @@ -3140,15 +2830,6 @@ dependencies = [ "libc", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "test-log" version = "0.2.21" @@ -3257,9 +2938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "libc", "num-conv", - "num_threads", "powerfmt", "serde_core", "time-core", @@ -3355,18 +3034,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "tokio-util" version = "0.7.19" @@ -3458,17 +3125,12 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "async-compression", "bitflags", "bytes", - "futures-core", "futures-util", "http", "http-body", - "http-body-util", "pin-project-lite", - "tokio", - "tokio-util", "tower", "tower-layer", "tower-service", @@ -3615,23 +3277,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.5", - "sha1", - "thiserror 2.0.19", - "utf-8", -] - [[package]] name = "typed-builder" version = "0.23.2" @@ -3701,12 +3346,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index c6422a67..6a211c8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ members = [ "crates/contextforge-data-plane-apis", "crates/contextforge-data-plane-cpex", "crates/contextforge-data-plane-lib", - "crates/contextforge-load-test", "crates/plugins/cpex-secrets-detection", ] resolver = "3" diff --git a/README.md b/README.md index db5c89db..814356db 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,12 @@ cargo run --release \ cargo run --features contextforge-data-plane-lib/with_tools \ -- \ --address 0.0.0.0:8080 \ ---redis-address 127.0.0.1 \ ---redis-port 6379 \ +--redis-address 127.0.0.1 \ +--redis-port 6379 \ --redis-mode plain-text \ --token-verification-private-key ./assets/jwt.key \ --token-verification-public-key ./assets/jwt.key.pub \ ---upstream-connection-mode plain-text-or-tls \ +--upstream-connection-mode plain-text-or-tls \ --tls-address 0.0.0.0:8443 \ --server-private-key ./assets/tls_key.pem \ --server-certificate ./assets/tls_certificate.pem @@ -103,12 +103,7 @@ The data plane exports OTLP traces and metrics. Local Langfuse, OTel Collector, ## Performance Tests -With a configured data plane running on port `8001`: - -```bash -cargo run --release --bin contextforge-load-test -- \ - --host http://127.0.0.1:8001 \ - -r 40 -u 120 --run-time 120s --report-file report.html -``` - -Existing benchmark results are under [reports](reports). +Performance testing uses the control-plane Locust suite through +[`cf-integration`](https://github.com/contextforge-org/contextforge-dev-tools). +See the [performance guide](_context/wiki/performance.md) for load and baseline +runs. diff --git a/_context/wiki/index.md b/_context/wiki/index.md index d16c93a6..049785c2 100644 --- a/_context/wiki/index.md +++ b/_context/wiki/index.md @@ -18,7 +18,7 @@ then follow only the links that are relevant. | [config.md](config.md) | Key CLI flags, JWT claims, UserConfig shape, plugin config, telemetry debugging, startup validation, local observability stack | | [deployment.md](deployment.md) | External-dataplane deployment checklist, health endpoint caveat, nginx routing, TLS choices, session affinity, Redis availability, image pinning | | [security.md](security.md) | Trust boundaries among the control plane, built-in dataplane, and external dataplane; Origin/Host validation; transport security; secrets handling | -| [performance.md](performance.md) | External-dataplane-only load testing (Goose), full-stack Locust runs, benchmark settings, built-in-dataplane baseline | +| [performance.md](performance.md) | Control-plane Locust load runs, benchmark settings, and built-in-dataplane baseline | | [testing.md](testing.md) | Workspace checks, in-repo integration tests, full-stack harness lanes, settings, and control-plane baseline | ## Quick orientation diff --git a/_context/wiki/performance.md b/_context/wiki/performance.md index 330c6f87..700584f8 100644 --- a/_context/wiki/performance.md +++ b/_context/wiki/performance.md @@ -1,27 +1,12 @@ # Performance And Load Testing -## Two Load Paths - -- **External-dataplane-only:** `contextforge-load-test` measures the ContextForge external dataplane in isolation. -- **Full-stack:** `cf-integration` measures the nginx → external dataplane → backend request path with Locust while the ContextForge control plane publishes configuration. - -Use the first to profile gateway changes; use the second to measure what users see. - -## External-Dataplane-Only (Goose) - -`crates/contextforge-load-test` is a [Goose](https://book.goose.rs/)-based driver that speaks full streamable HTTP MCP. Start the local stack and seed user config first (see [getting-started.md](getting-started.md)), then: - -```bash -cargo run --release --bin contextforge-load-test -- \ - --host 'http://127.0.0.1:8001' \ - -u 120 -r 40 --run-time 120s \ - --report-file report.html -``` - -`-u` = concurrent users, `-r` = spawn rate/s, `--report-file` = HTML report. Curated run reports live in `reports/`. - ## Full-Stack Load (Locust via cf-integration) +Performance testing uses the control-plane Locust suite through +[`cf-integration`](https://github.com/contextforge-org/contextforge-dev-tools). +It measures the nginx → external dataplane → backend request path while the +ContextForge control plane publishes configuration. + | Command | What it runs | | --- | --- | | `scripts/cf-integration.sh smoke` | 1 user for 10 s — quick sanity pass. | diff --git a/_context/wiki/project.md b/_context/wiki/project.md index c0884b68..1a96797c 100644 --- a/_context/wiki/project.md +++ b/_context/wiki/project.md @@ -121,7 +121,6 @@ Architecture context lives in the wiki. Key pages: | `contextforge-data-plane` (binary) | Process shell only: CLI flags, logging, runtime shape. No ContextForge external-dataplane logic. | | `contextforge-data-plane-apis` | Shared config shapes (`UserConfig`, `User`, plugin config). Regenerate JSON schemas after any change: `cargo run -p contextforge-data-plane-apis`. | | `contextforge-data-plane-cpex` | Plugin integration (CPEX hook factories). | -| `contextforge-load-test` | Performance harness: end-to-end MCP traffic driver. | **Key invariants:** - Redis/config access goes through `UserConfigStore` only — never leak Redis details into routing code. diff --git a/crates/contextforge-load-test/Cargo.toml b/crates/contextforge-load-test/Cargo.toml deleted file mode 100644 index f704f1ad..00000000 --- a/crates/contextforge-load-test/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "contextforge-load-test" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -keywords.workspace = true -homepage.workspace = true -readme.workspace = true -repository.workspace = true - - - -[dependencies] -goose = { git = "https://github.com/tag1consulting/goose" } -tokio.workspace = true -http.workspace = true -reqwest.workspace = true -uuid.workspace = true - - -[lints] -workspace = true diff --git a/crates/contextforge-load-test/src/main.rs b/crates/contextforge-load-test/src/main.rs deleted file mode 100644 index 3e8b916d..00000000 --- a/crates/contextforge-load-test/src/main.rs +++ /dev/null @@ -1,212 +0,0 @@ -use std::{env, time::Duration}; - -use goose::prelude::*; -use http::HeaderValue; -use reqwest::RequestBuilder; - -pub const INITIALIZE_MCP_SESSION: &str = r#"{"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"sampling":{},"elicitation":{},"roots":{"listChanged":true},"tasks":{"list":{},"cancel":{},"requests":{"sampling":{"createMessage":{}},"elicitation":{"create":{}}}}},"clientInfo":{"name":"inspector-client","version":"0.21.1"}},"jsonrpc":"2.0","id":0}"#; -pub const NOTIFY_MCP_SESSION: &str = r#"{"method":"notifications/initialized","jsonrpc":"2.0"}"#; -//pub const COUNTER_ONE_INC: &'static str = r#"{"method":"tools/call","params":{"name":"increment","arguments":{},"_meta":{"progressToken":1}},"jsonrpc":"2.0","id":5}"#; -pub const COUNTER_ONE_INC: &str = r#"{"method":"tools/call","params":{"name":"gateway-one-increment","arguments":{},"_meta":{"progressToken":1}},"jsonrpc":"2.0","id":1}"#; -pub const MCP_ENDPOINT: &str = "/contextforge-rs/servers/c0ffee00f001f00lf00ldeadbeefdead/mcp"; -pub const TOKEN_ENDPOINT: &str = - "/contextforge-rs/admin/tokens/11111111-1111-1111-1111-111111111111?email=admin%40example.com"; -//pub const MCP_ENDPOINT: &'static str = "/mcp"; -//pub const MCP_ENDPOINT: &'static str = "/servers/b97eaa969a39421685be13ce5fa06207/mcp"; - -#[derive(Clone, Debug)] -struct Session { - jwt_token: String, - mcp_session_id: Option, -} - -async fn get_token(user: &mut GooseUser) -> TransactionResult { - let response = match user.get(TOKEN_ENDPOINT).await?.response { - Ok(r) => match r.text().await { - Ok(j) => j, - Err(e) => return Err(Box::new(e.into())), - }, - Err(e) => return Err(Box::new(e.into())), - }; - - user.set_session_data(Session { jwt_token: response, mcp_session_id: None }); - - Ok(()) -} - -#[expect(dead_code, reason = "kept for load tests that use an externally supplied ContextForge JWT")] -#[expect(clippy::unused_async, reason = "Goose transaction functions are async")] -#[expect(clippy::unwrap_used, reason = "load test setup should fail fast when the JWT env var is missing")] -async fn get_cf_token(user: &mut GooseUser) -> TransactionResult { - let key = env::var("MCP_CF_JWT_KEY").unwrap(); - - user.set_session_data(Session { jwt_token: key, mcp_session_id: None }); - - Ok(()) -} - -fn build_mcp_request( - user: &mut GooseUser, - session: &Session, - body: &'static str, - method: GooseMethod, -) -> Result> { - let reqwest_request_builder = user.get_request_builder(&method, MCP_ENDPOINT)?.bearer_auth(&session.jwt_token); - - let request = reqwest_request_builder - .body(body) - .header(http::header::CONTENT_TYPE, "application/json") - .header(http::header::ACCEPT, "application/json;application/json; text/event-stream"); - - Ok(if let Some(mcp_session_id) = session.mcp_session_id.as_ref() { - request.header("Mcp-session-id", mcp_session_id) - } else { - request - }) -} - -fn initialize_mcp_request(user: &mut GooseUser, session: &Session) -> Result> { - build_mcp_request(user, session, INITIALIZE_MCP_SESSION, GooseMethod::Post) -} - -fn notify_mcp_request(user: &mut GooseUser, session: &Session) -> Result> { - build_mcp_request(user, session, NOTIFY_MCP_SESSION, GooseMethod::Post) -} - -fn counter_one_increase_request( - user: &mut GooseUser, - session: &Session, -) -> Result> { - build_mcp_request(user, session, COUNTER_ONE_INC, GooseMethod::Post) -} - -fn end_session_mcp_request(user: &mut GooseUser, session: &Session) -> Result> { - build_mcp_request(user, session, "", GooseMethod::Delete) -} - -async fn counter_call(user: &mut GooseUser) -> TransactionResult { - let session = user.get_session_data_unchecked::().clone(); - - if session.mcp_session_id.is_none() { - user.log_debug("counter_call: No Session id ", None, None, None)?; - return Err(Box::new(TransactionError::Custom("no session id".to_owned()))); - } - - let calls = 10; - for i in 0..calls { - let counter_one_inc_request = - GooseRequest::builder().set_request_builder(counter_one_increase_request(user, &session)?).build(); - - match user.request(counter_one_inc_request).await?.response { - Ok(r) => { - let status = r.status(); - let payload = r.text().await?; - if status == http::StatusCode::OK { - if !payload.contains("\"isError\":false") && !payload.contains(&format!("\"text\":\"{}\"", i + 1)) { - let _ = user.log_debug("Counter has a problem ", None, None, Some(&payload)); - return Err(Box::new(TransactionError::Custom("counting problem".to_owned()))); - } - } else { - return Err(Box::new(TransactionError::Custom("wrong response code ".to_owned()))); - } - }, - Err(e) => return Err(Box::new(e.into())), - } - } - - Ok(()) -} - -async fn delete_session(user: &mut GooseUser) -> TransactionResult { - let session = user.get_session_data_unchecked::().clone(); - if session.mcp_session_id.is_none() { - return Err(Box::new(TransactionError::Custom("no session id".to_owned()))); - } - let end_session = GooseRequest::builder().set_request_builder(end_session_mcp_request(user, &session)?).build(); - - let mut session = user.get_session_data_unchecked::().clone(); - session.mcp_session_id = None; - user.set_session_data(session); - - match user.request(end_session).await?.response { - Ok(r) => { - if r.status() == http::StatusCode::ACCEPTED || r.status() == http::StatusCode::OK { - Ok(()) - } else { - Err(Box::new(TransactionError::Custom("wrong response code ".to_owned()))) - } - }, - Err(e) => Err(Box::new(e.into())), - } -} - -async fn initialize_session(user: &mut GooseUser) -> TransactionResult { - let session = user.get_session_data_unchecked::(); - let mut session = session.clone(); - if session.mcp_session_id.is_some() { - _ = user.log_debug("intialize has session id ", None, None, None); - return Err(Box::new(TransactionError::Custom("initialize has session id ".to_owned()))); - } - let init_request = { - let builder = initialize_mcp_request(user, &session)?; - GooseRequest::builder().set_request_builder(builder).build() - }; - - let session_id = match user.request(init_request).await?.response { - Ok(r) => { - if r.status() == http::StatusCode::OK { - if let Some(session_id) = r.headers().get("mcp-session-id").cloned() { - session_id.clone() - } else { - //return Err(Box::new(TransactionError::Custom("can't find mcp-session-id".to_owned()))); - HeaderValue::from_str(&uuid::Uuid::new_v4().to_string()).expect("this should work") - } - } else { - return Err(Box::new(TransactionError::Custom("wrong response code ".to_owned()))); - } - }, - Err(e) => return Err(Box::new(e.into())), - }; - - tokio::time::sleep(Duration::from_millis(1)).await; - - session.mcp_session_id = Some(session_id.to_str().unwrap_or_default().to_owned()); - let notify_request = GooseRequest::builder().set_request_builder(notify_mcp_request(user, &session)?).build(); - match user.request(notify_request).await?.response { - Ok(r) => { - let status = r.status(); - let _payload = r.text().await?; - if status != http::StatusCode::ACCEPTED { - return Err(Box::new(TransactionError::Custom("wrong response code ".to_owned()))); - } - }, - Err(e) => return Err(Box::new(e.into())), - } - user.set_session_data(session); - - Ok(()) -} - -async fn all_session(user: &mut GooseUser) -> TransactionResult { - initialize_session(user).await?; - counter_call(user).await?; - delete_session(user).await?; - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<(), GooseError> { - GooseAttack::initialize()? - .register_scenario( - scenario!("ContextForgeMCPCounter") - .register_transaction(transaction!(get_token).set_on_start()) - .register_transaction(transaction!(all_session).set_name("All sessions").set_sequence(1)), - // .register_transaction(transaction!(initialize_session).set_name("MCP Init").set_sequence(1)) - // .register_transaction(transaction!(counter_call).set_name("MCP Counter Call").set_sequence(2)) - // .register_transaction(transaction!(delete_session).set_name("MCP Delete").set_sequence(3)), //.register_transaction(transaction!(all_session)), - ) - .execute() - .await?; - - Ok(()) -} diff --git a/deny.toml b/deny.toml index af2927a8..7466d44a 100644 --- a/deny.toml +++ b/deny.toml @@ -1,8 +1,3 @@ -[advisories] -ignore = [ - { id = "RUSTSEC-2026-0214", reason = "Transitive through the load-test-only Goose crate, which has no maintained CLI replacement yet" }, -] - ## This doesn't work at the moment. see https://github.com/EmbarkStudios/cargo-deny/issues/859 [[licenses.exceptions]] allow = ["CDLA-Permissive-2.0"] diff --git a/reports/cf-gateway-performance-report-2026-05-01.html b/reports/cf-gateway-performance-report-2026-05-01.html deleted file mode 100644 index b7506749..00000000 --- a/reports/cf-gateway-performance-report-2026-05-01.html +++ /dev/null @@ -1,1046 +0,0 @@ - - - - - Goose Attack Report - - - - -
-

Goose Attack Report

- -
-

Users: 120

-

Target Host: http://127.0.0.1:8001/

-

goose v0.19.0-dev

-

Plan overview

- - - - - - - - - - - - - -
ActionStartedStoppedElapsedUsers
Increasing26-05-01 11:14:3226-05-01 11:14:3500:00:030 → 120
Maintaining26-05-01 11:14:3526-05-01 11:24:3600:10:01120
Decreasing26-05-01 11:24:3626-05-01 11:24:3600:00:000 ← 120
-
- -
-

Request Metrics

- -
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodName# Requests# FailsAverage (ms)Min (ms)Max (ms)RPSFailures/s
DELETE118,392020.051134197.320.00
GET/contextforge-rs/admin/tokens/admin@example.com1011.0011110.000.00
POST1,419,973013.9812812366.620.00
  └─ 200 (91.7%)1,301,69712.991281
  └─ 202 (8.3%)118,27824.921206
Aggregated1,538,366014.4512812563.940.00
-
- - - -
-

Response Time Metrics

- -
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodName50%ile (ms)60%ile (ms)70%ile (ms)80%ile (ms)90%ile (ms)95%ile (ms)99%ile (ms)100%ile (ms)
DELETE19232834445270130
GET/contextforge-rs/admin/tokens/admin@example.com1111111111111111
POST112846110160280
Aggregated1231445100160270
-
- - - -
-

Status Code Metrics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodNameStatus Codes
DELETE118,392 [202]
GET/contextforge-rs/admin/tokens/admin@example.com1 [200]
POST1,301,697 [200], 118,278 [202]
Aggregated236,670 [202], 1,301,698 [200]
-
- -
-

Transaction Metrics

- -
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Transaction# Times Run# FailsAverage (ms)Min (ms)Max (ms)RPSFailures/s
ContextForgeMCPCounter
0.0 1011.0011110.000.00
0.1 All sessions118,3920608.55406945197.320.00
Aggregated118,3930187.7311945197.320.00
-
- -
-

Scenario Metrics

- -
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Scenario# Users# Times RunAverage (ms)Min (ms)Max (ms)Scenarios/sIterations
ContextForgeMCPCounter120118,272608.57406945197.12985.60
Aggregated120118,272608.57406945197.12985.60
-
- -
-

User Metrics

-
-
- - -
-
- - - - - -
- -