From 459689b4e48a1e77f06b9fcc0f67d8861ead6ffb Mon Sep 17 00:00:00 2001 From: "bxf12315@gmail.com" Date: Mon, 3 Nov 2025 22:33:46 +0800 Subject: [PATCH 1/3] TC-3071 scale test for put and patch advisory lables --- scenarios/full-20250604.json5 | 4 ++- src/main.rs | 4 +++ src/restapi.rs | 54 ++++++++++++++++++++++++++++++++++- src/scenario/mod.rs | 35 +++++++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/scenarios/full-20250604.json5 b/scenarios/full-20250604.json5 index 5ab8e87..77ff498 100644 --- a/scenarios/full-20250604.json5 +++ b/scenarios/full-20250604.json5 @@ -113,5 +113,7 @@ "urn:uuid:01973122-3bdd-78a3-898a-fb67c06387ed" ], "download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89" + "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", + "put_advisory_lables": "urn:uuid:24ae57c3-4b57-4f4e-82c1-83ae26059a89", + "patch_advisory_lables": "urn:uuid:9ffb1a1b-14e0-4abb-943a-76906daa54aa" } diff --git a/src/main.rs b/src/main.rs index b11c30d..15c3bee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -192,6 +192,10 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.post_vulnerability_analyze?(scenario.analyze_purl.clone())); tx!(s.get_purl_details?(scenario.get_purl_details.clone())); tx!(s.get_recommendations?(scenario.get_recommendations.clone())); + tx!(s.put_advisory_labels?(scenario.put_advisory_lables.clone())); + tx!(s.patch_advisory_labels?( + scenario.patch_advisory_lables.clone() + )); tx!(s.download_advisory?(scenario.download_advisory.clone())); tx!(s.get_advisory?(scenario.get_advisory.clone())); diff --git a/src/restapi.rs b/src/restapi.rs index a99de09..5f74b27 100644 --- a/src/restapi.rs +++ b/src/restapi.rs @@ -1,5 +1,7 @@ use crate::utils::DisplayVec; -use goose::goose::{GooseUser, TransactionResult}; +use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionResult}; +use reqwest::{Client, RequestBuilder}; + use serde_json::json; use std::sync::{ Arc, @@ -67,6 +69,56 @@ pub async fn search_advisory(user: &mut GooseUser) -> TransactionResult { Ok(()) } +async fn send_advisory_label_request( + advisory_id: String, + user: &mut GooseUser, + method: GooseMethod, + source: &str, + client_method: fn(&Client, String) -> RequestBuilder, +) -> TransactionResult { + let path = format!("/api/v2/advisory/{}/label", advisory_id); + let json = json!({ + "source": source, + "foo": "bar", + "space": "with space", + "empty": "", + }); + + let url = user.build_url(&path)?; + + let reqwest_request_builder = client_method(&user.client, url); + let goose_request = GooseRequest::builder() + .method(method) + .path(path.as_str()) + .set_request_builder(reqwest_request_builder.json(&json)) + .build(); + let _response = user.request(goose_request).await?; + + Ok(()) +} + +pub async fn put_advisory_labels(advisory_id: String, user: &mut GooseUser) -> TransactionResult { + send_advisory_label_request( + advisory_id, + user, + GooseMethod::Put, + "It's a put request", + Client::put, + ) + .await +} + +pub async fn patch_advisory_labels(advisory_id: String, user: &mut GooseUser) -> TransactionResult { + send_advisory_label_request( + advisory_id, + user, + GooseMethod::Patch, + "It's a patch request", + Client::patch, + ) + .await +} + pub async fn list_importer(user: &mut GooseUser) -> TransactionResult { let _response = user.get("/api/v2/importer").await?; diff --git a/src/scenario/mod.rs b/src/scenario/mod.rs index a370d2b..34def0c 100644 --- a/src/scenario/mod.rs +++ b/src/scenario/mod.rs @@ -102,6 +102,11 @@ pub(crate) struct Scenario { #[serde(with = "required")] pub get_advisory: Option, + + pub put_advisory_lables: Option, + + #[serde(with = "required")] + pub patch_advisory_lables: Option, } impl Scenario { @@ -144,6 +149,12 @@ impl Scenario { let download_advisory = Some(loader.download_advisory().await?); let get_advisory = Some(loader.download_advisory().await?); + let put_advisory_lables = Some(format!("urn:uuid:{}", loader.put_advisory_lables().await?)); + let patch_advisory_lables = Some(format!( + "urn:uuid:{}", + loader.patch_advisory_lables().await? + )); + Ok(Self { get_sbom: large_sbom_digest.clone(), get_sbom_advisories: large_sbom_digest.clone(), @@ -160,6 +171,8 @@ impl Scenario { delete_sbom_pool, download_advisory, get_advisory, + put_advisory_lables, + patch_advisory_lables, }) } } @@ -376,6 +389,28 @@ FROM public.advisory order by modified desc limit 1;"#, ) .await } + + /// A advisory ID for put labels + pub async fn put_advisory_lables(&self) -> anyhow::Result { + self.find( + r#" +SELECT id::text as result +FROM public.advisory where labels is not null order by modified desc limit 1; +"#, + ) + .await + } + + /// A advisory ID for patch labels + pub async fn patch_advisory_lables(&self) -> anyhow::Result { + self.find( + r#" +SELECT id::text as result +FROM public.advisory where labels is not null order by modified desc OFFSET 1 limit 1; +"#, + ) + .await + } } #[cfg(test)] From 7e12e20111e070c740945500f861a86957e0e27f Mon Sep 17 00:00:00 2001 From: "bxf12315@gmail.com" Date: Wed, 12 Nov 2025 13:59:04 +0800 Subject: [PATCH 2/3] Add a feature to randomly retrieve IDs from the ID pool. --- scenarios/full-20250604.json5 | 46 ++++++++++++++++++++++- src/main.rs | 23 +++++++++--- src/restapi.rs | 19 ++++++++-- src/scenario/mod.rs | 70 +++++++++++++++++++++-------------- 4 files changed, 120 insertions(+), 38 deletions(-) diff --git a/scenarios/full-20250604.json5 b/scenarios/full-20250604.json5 index 77ff498..699efda 100644 --- a/scenarios/full-20250604.json5 +++ b/scenarios/full-20250604.json5 @@ -114,6 +114,48 @@ ], "download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "put_advisory_lables": "urn:uuid:24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "patch_advisory_lables": "urn:uuid:9ffb1a1b-14e0-4abb-943a-76906daa54aa" + "put_advisory_lables": [ + "urn:uuid:24ae57c3-4b57-4f4e-82c1-83ae26059a89", + "urn:uuid:9ffb1a1b-14e0-4abb-943a-76906daa54aa", + "urn:uuid:7c932cc3-aa83-4bb4-bbaf-6030a198e271", + "urn:uuid:cac4862f-c87a-4a7d-8d67-f2e0db8f0273", + "urn:uuid:f80b5b7e-f2eb-4921-b254-a4873d69a9e9", + "urn:uuid:858a53a2-347a-491e-a86a-e24b9a4c1436", + "urn:uuid:15637501-2a0b-4076-aec3-d92fa2b959bf", + "urn:uuid:5364258d-418e-4cd8-add7-248207c4836f", + "urn:uuid:5066569d-c5a3-4d90-8cdc-d8680ee52db0", + "urn:uuid:869b1646-2f27-4b7a-b4e0-f6783651135d", + "urn:uuid:04313720-b5eb-40bc-b545-1f813f6e53b9", + "urn:uuid:2c6d41c1-f39d-47df-b37f-12b08af41577", + "urn:uuid:99bac490-3ff3-4c10-8466-2cc4947ac29c", + "urn:uuid:90a53b25-118d-4c2a-9b8d-a38ac1a85ab0", + "urn:uuid:4d3fd5be-eeb5-46f7-a55d-871be40c9837", + "urn:uuid:fd88f7cd-8b9e-4bba-b92b-726732545e82", + "urn:uuid:06a769a4-1f30-4fe5-9113-72080301e8ed", + "urn:uuid:871263d1-e7c3-4f4e-98b2-04aedeae048c", + "urn:uuid:35dc833f-fbb7-43f0-852a-9db14cd3a0f4", + "urn:uuid:acfbea2f-08d0-4f2f-aa22-12e635956dce" + ], + "patch_advisory_lables": [ + "urn:uuid:8a28d2fc-3e91-4989-add8-b9dd78fcf37d", + "urn:uuid:9e3eee98-a032-424d-9d0d-1645faca9d24", + "urn:uuid:66dde1a5-9575-46a4-8f7a-f6ab9904f0d1", + "urn:uuid:2070a289-c6bb-4a6c-bdb2-6fc951743e55", + "urn:uuid:59751bb1-b205-4ea4-9832-a48ab69e39e6", + "urn:uuid:0dd5f452-af2b-4740-905e-a00f22fffa7e", + "urn:uuid:08467200-8c82-4d38-9920-3cbf6c2d0568", + "urn:uuid:be6b4acf-fe7b-46a2-9f4c-fdd497d43188", + "urn:uuid:6ddb2cc0-bb10-4f23-96bf-cc409263d80d", + "urn:uuid:3973cb57-325a-4574-a60c-1910a30b44ac", + "urn:uuid:074881e9-d328-402c-8bd8-13df42a590e5", + "urn:uuid:2e8008f1-e3d2-401d-8ef6-87bad7d328f0", + "urn:uuid:d30949a9-1f9b-4005-93a8-288a3578e7f7", + "urn:uuid:8aa4555d-3723-4c4f-aee8-69c4007ec5fd", + "urn:uuid:6076bbcb-b352-4bcb-b2e3-8889e0310724", + "urn:uuid:5390d139-c00c-4d3a-858a-833d4b571d9a", + "urn:uuid:f1479e4b-839e-4d53-929c-d08b6b907430", + "urn:uuid:96c1b100-ec62-46c0-9156-819c37bd0445", + "urn:uuid:148db4ee-55ca-4292-b55d-a0c0a9d83b9c", + "urn:uuid:e98e73ab-4cfe-4861-8b46-06df5a84198f" + ] } diff --git a/src/main.rs b/src/main.rs index 15c3bee..0d07c1a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -192,10 +192,22 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.post_vulnerability_analyze?(scenario.analyze_purl.clone())); tx!(s.get_purl_details?(scenario.get_purl_details.clone())); tx!(s.get_recommendations?(scenario.get_recommendations.clone())); - tx!(s.put_advisory_labels?(scenario.put_advisory_lables.clone())); - tx!(s.patch_advisory_labels?( - scenario.patch_advisory_lables.clone() - )); + + // Register put Advisory labels transaction if pool is available + let put_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + if let Some(_advisory_ids) = scenario.put_advisory_lables.clone() { + tx!(s.put_advisory_labels?(scenario.put_advisory_lables.clone(), + put_advisory_labels_counter.clone()), + name: format!("put_advisory_labels")); + } + + // Register patch Advisory labels transaction if pool is available + let patch_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + if let Some(_advisory_ids) = scenario.patch_advisory_lables.clone() { + tx!(s.patch_advisory_labels?(scenario.patch_advisory_lables.clone(), + patch_advisory_labels_counter.clone()), + name: format!("patch_advisory_labels")); + } tx!(s.download_advisory?(scenario.download_advisory.clone())); tx!(s.get_advisory?(scenario.get_advisory.clone())); @@ -228,7 +240,8 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.delete_sbom_from_pool_sequential?( scenario.delete_sbom_pool.clone(), delete_counter.clone() - ), name: format!("delete_sbom_from_pool_sequential[{} SBOMs]", pool.len())) + ), + name: format!("delete_sbom_from_pool_sequential[{} SBOMs]", pool.len())) } s }) diff --git a/src/restapi.rs b/src/restapi.rs index 5f74b27..d3bdfad 100644 --- a/src/restapi.rs +++ b/src/restapi.rs @@ -69,6 +69,7 @@ pub async fn search_advisory(user: &mut GooseUser) -> TransactionResult { Ok(()) } +/// Send Advisory labels request async fn send_advisory_label_request( advisory_id: String, user: &mut GooseUser, @@ -97,9 +98,14 @@ async fn send_advisory_label_request( Ok(()) } -pub async fn put_advisory_labels(advisory_id: String, user: &mut GooseUser) -> TransactionResult { +/// Send Advisory labels request using PUT method +pub async fn put_advisory_labels( + advisory_ids: Vec, + counter: Arc, + user: &mut GooseUser, +) -> TransactionResult { send_advisory_label_request( - advisory_id, + advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(), user, GooseMethod::Put, "It's a put request", @@ -108,9 +114,14 @@ pub async fn put_advisory_labels(advisory_id: String, user: &mut GooseUser) -> T .await } -pub async fn patch_advisory_labels(advisory_id: String, user: &mut GooseUser) -> TransactionResult { +/// Send Advisory labels request using PATCH method +pub async fn patch_advisory_labels( + advisory_ids: Vec, + counter: Arc, + user: &mut GooseUser, +) -> TransactionResult { send_advisory_label_request( - advisory_id, + advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(), user, GooseMethod::Patch, "It's a patch request", diff --git a/src/scenario/mod.rs b/src/scenario/mod.rs index 34def0c..01f8f54 100644 --- a/src/scenario/mod.rs +++ b/src/scenario/mod.rs @@ -103,10 +103,11 @@ pub(crate) struct Scenario { #[serde(with = "required")] pub get_advisory: Option, - pub put_advisory_lables: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub put_advisory_lables: Option>, - #[serde(with = "required")] - pub patch_advisory_lables: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub patch_advisory_lables: Option>, } impl Scenario { @@ -149,11 +150,22 @@ impl Scenario { let download_advisory = Some(loader.download_advisory().await?); let get_advisory = Some(loader.download_advisory().await?); - let put_advisory_lables = Some(format!("urn:uuid:{}", loader.put_advisory_lables().await?)); - let patch_advisory_lables = Some(format!( - "urn:uuid:{}", - loader.patch_advisory_lables().await? - )); + let put_advisory_lables = Some( + loader + .put_advisory_lables() + .await? + .iter() + .map(|advisory_id| format!("urn:uuid:{advisory_id}")) + .collect(), + ); + let patch_advisory_lables = Some( + loader + .patch_advisory_lables() + .await? + .iter() + .map(|advisory_id| format!("urn:uuid:{advisory_id}")) + .collect(), + ); Ok(Self { get_sbom: large_sbom_digest.clone(), @@ -389,27 +401,31 @@ FROM public.advisory order by modified desc limit 1;"#, ) .await } - - /// A advisory ID for put labels - pub async fn put_advisory_lables(&self) -> anyhow::Result { - self.find( - r#" -SELECT id::text as result -FROM public.advisory where labels is not null order by modified desc limit 1; -"#, - ) - .await + + /// Advisory IDs for put labels + pub async fn put_advisory_lables(&self) -> anyhow::Result> { + let mut db = crate::db::connect(&self.db).await?; + + let rows = sqlx::query("SELECT id::text as id FROM public.advisory where labels is not null order by modified desc limit 20;") + .fetch_all(&mut db) + .await?; + Ok(rows + .into_iter() + .map(|row| row.get::("id")) + .collect()) } - /// A advisory ID for patch labels - pub async fn patch_advisory_lables(&self) -> anyhow::Result { - self.find( - r#" -SELECT id::text as result -FROM public.advisory where labels is not null order by modified desc OFFSET 1 limit 1; -"#, - ) - .await + /// Advisory IDs for patch labels + pub async fn patch_advisory_lables(&self) -> anyhow::Result> { + let mut db = crate::db::connect(&self.db).await?; + + let rows = sqlx::query("SELECT id::text as id FROM public.advisory where labels is not null order by modified desc OFFSET 20 limit 20;") + .fetch_all(&mut db) + .await?; + Ok(rows + .into_iter() + .map(|row| row.get::("id")) + .collect()) } } From e195f7d11a06ed2bf5d513b371c83af80ef18750 Mon Sep 17 00:00:00 2001 From: "bxf12315@gmail.com" Date: Thu, 27 Nov 2025 22:56:05 +0800 Subject: [PATCH 3/3] =?UTF-8?q?TC-3071=20randomly=20fetch=20advisory=20ID?= =?UTF-8?q?=EF=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + Cargo.toml | 1 + scenarios/full-20250604.json5 | 46 +---------------- src/main.rs | 20 ++------ src/restapi.rs | 96 ++++++++++++++++++++++++++++++----- src/scenario/mod.rs | 51 ------------------- 6 files changed, 89 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e81a9e..f433bbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1353,6 +1353,7 @@ dependencies = [ "log", "openid", "packageurl", + "rand 0.8.5", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index e48a30f..7e8a700 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "uuid", tokio = { version = "1.43.1", features = ["sync"] } urlencoding = "2" packageurl = "0.4.2" +rand = "0.8" [features] default = ["postgres"] diff --git a/scenarios/full-20250604.json5 b/scenarios/full-20250604.json5 index 699efda..5ab8e87 100644 --- a/scenarios/full-20250604.json5 +++ b/scenarios/full-20250604.json5 @@ -113,49 +113,5 @@ "urn:uuid:01973122-3bdd-78a3-898a-fb67c06387ed" ], "download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "put_advisory_lables": [ - "urn:uuid:24ae57c3-4b57-4f4e-82c1-83ae26059a89", - "urn:uuid:9ffb1a1b-14e0-4abb-943a-76906daa54aa", - "urn:uuid:7c932cc3-aa83-4bb4-bbaf-6030a198e271", - "urn:uuid:cac4862f-c87a-4a7d-8d67-f2e0db8f0273", - "urn:uuid:f80b5b7e-f2eb-4921-b254-a4873d69a9e9", - "urn:uuid:858a53a2-347a-491e-a86a-e24b9a4c1436", - "urn:uuid:15637501-2a0b-4076-aec3-d92fa2b959bf", - "urn:uuid:5364258d-418e-4cd8-add7-248207c4836f", - "urn:uuid:5066569d-c5a3-4d90-8cdc-d8680ee52db0", - "urn:uuid:869b1646-2f27-4b7a-b4e0-f6783651135d", - "urn:uuid:04313720-b5eb-40bc-b545-1f813f6e53b9", - "urn:uuid:2c6d41c1-f39d-47df-b37f-12b08af41577", - "urn:uuid:99bac490-3ff3-4c10-8466-2cc4947ac29c", - "urn:uuid:90a53b25-118d-4c2a-9b8d-a38ac1a85ab0", - "urn:uuid:4d3fd5be-eeb5-46f7-a55d-871be40c9837", - "urn:uuid:fd88f7cd-8b9e-4bba-b92b-726732545e82", - "urn:uuid:06a769a4-1f30-4fe5-9113-72080301e8ed", - "urn:uuid:871263d1-e7c3-4f4e-98b2-04aedeae048c", - "urn:uuid:35dc833f-fbb7-43f0-852a-9db14cd3a0f4", - "urn:uuid:acfbea2f-08d0-4f2f-aa22-12e635956dce" - ], - "patch_advisory_lables": [ - "urn:uuid:8a28d2fc-3e91-4989-add8-b9dd78fcf37d", - "urn:uuid:9e3eee98-a032-424d-9d0d-1645faca9d24", - "urn:uuid:66dde1a5-9575-46a4-8f7a-f6ab9904f0d1", - "urn:uuid:2070a289-c6bb-4a6c-bdb2-6fc951743e55", - "urn:uuid:59751bb1-b205-4ea4-9832-a48ab69e39e6", - "urn:uuid:0dd5f452-af2b-4740-905e-a00f22fffa7e", - "urn:uuid:08467200-8c82-4d38-9920-3cbf6c2d0568", - "urn:uuid:be6b4acf-fe7b-46a2-9f4c-fdd497d43188", - "urn:uuid:6ddb2cc0-bb10-4f23-96bf-cc409263d80d", - "urn:uuid:3973cb57-325a-4574-a60c-1910a30b44ac", - "urn:uuid:074881e9-d328-402c-8bd8-13df42a590e5", - "urn:uuid:2e8008f1-e3d2-401d-8ef6-87bad7d328f0", - "urn:uuid:d30949a9-1f9b-4005-93a8-288a3578e7f7", - "urn:uuid:8aa4555d-3723-4c4f-aee8-69c4007ec5fd", - "urn:uuid:6076bbcb-b352-4bcb-b2e3-8889e0310724", - "urn:uuid:5390d139-c00c-4d3a-858a-833d4b571d9a", - "urn:uuid:f1479e4b-839e-4d53-929c-d08b6b907430", - "urn:uuid:96c1b100-ec62-46c0-9156-819c37bd0445", - "urn:uuid:148db4ee-55ca-4292-b55d-a0c0a9d83b9c", - "urn:uuid:e98e73ab-4cfe-4861-8b46-06df5a84198f" - ] + "get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89" } diff --git a/src/main.rs b/src/main.rs index 0d07c1a..90e5fef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -180,7 +180,9 @@ async fn main() -> Result<(), anyhow::Error> { .register_transaction(tx!(list_sboms_paginated)) .register_transaction(tx!(get_analysis_status)) .register_transaction(tx!(get_analysis_latest_cpe)) - .register_transaction(tx!(list_advisory_labels)); + .register_transaction(tx!(list_advisory_labels)) + .register_transaction(tx!(patch_advisory_labels)) + .register_transaction(tx!(put_advisory_labels)); tx!(s.get_sbom?(scenario.get_sbom.clone())); tx!(s.get_sbom_advisories?(scenario.get_sbom_advisories.clone())); @@ -193,22 +195,6 @@ async fn main() -> Result<(), anyhow::Error> { tx!(s.get_purl_details?(scenario.get_purl_details.clone())); tx!(s.get_recommendations?(scenario.get_recommendations.clone())); - // Register put Advisory labels transaction if pool is available - let put_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); - if let Some(_advisory_ids) = scenario.put_advisory_lables.clone() { - tx!(s.put_advisory_labels?(scenario.put_advisory_lables.clone(), - put_advisory_labels_counter.clone()), - name: format!("put_advisory_labels")); - } - - // Register patch Advisory labels transaction if pool is available - let patch_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0)); - if let Some(_advisory_ids) = scenario.patch_advisory_lables.clone() { - tx!(s.patch_advisory_labels?(scenario.patch_advisory_lables.clone(), - patch_advisory_labels_counter.clone()), - name: format!("patch_advisory_labels")); - } - tx!(s.download_advisory?(scenario.download_advisory.clone())); tx!(s.get_advisory?(scenario.get_advisory.clone())); s diff --git a/src/restapi.rs b/src/restapi.rs index d3bdfad..186b885 100644 --- a/src/restapi.rs +++ b/src/restapi.rs @@ -1,14 +1,19 @@ use crate::utils::DisplayVec; -use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionResult}; +use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionError, TransactionResult}; use reqwest::{Client, RequestBuilder}; +use rand::Rng; use serde_json::json; use std::sync::{ Arc, atomic::{AtomicUsize, Ordering}, }; +use tokio::sync::OnceCell; use urlencoding::encode; +// Static variable to store advisory total count +static ADVISORY_TOTAL: OnceCell = OnceCell::const_new(); + pub async fn get_advisory(id: String, user: &mut GooseUser) -> TransactionResult { let uri = format!("/api/v2/advisory/{}", encode(&format!("urn:uuid:{}", id))); @@ -46,6 +51,46 @@ pub async fn list_advisory(user: &mut GooseUser) -> TransactionResult { Ok(()) } +/// Get advisory total count and store it in static OnceCell +async fn get_advisory_total(user: &mut GooseUser) -> Result> { + let response = user.get("/api/v2/advisory").await?; + let json_data = response.response?.json::().await?; + + // Extract total from the response + if let Some(total) = json_data.get("total").and_then(|t| t.as_u64()) { + log::info!("Advisory total count: {}", total); + return Ok(total); + } + + Err(Box::new(TransactionError::Custom( + "Failed to get advisory total count".to_string(), + ))) +} + +/// Get cached advisory total count, fetch if not available using get_or_init +async fn get_cached_advisory_total(user: &mut GooseUser) -> Result> { + // Try to get from cache first + if let Some(&total) = ADVISORY_TOTAL.get() { + return Ok(total); + } + + // If not cached, fetch it and handle errors properly + match get_advisory_total(user).await { + Ok(total) => { + // Store in cache for future use + let _ = ADVISORY_TOTAL.set(total); + Ok(total) + } + Err(e) => { + // Propagate the error with context instead of silently returning 0 + Err(Box::new(TransactionError::Custom(format!( + "Failed to get advisory total count: {}", + e + )))) + } + } +} + pub async fn list_advisory_paginated(user: &mut GooseUser) -> TransactionResult { let _response = user.get("/api/v2/advisory?offset=100&limit=10").await?; @@ -69,6 +114,37 @@ pub async fn search_advisory(user: &mut GooseUser) -> TransactionResult { Ok(()) } +/// List advisory with random offset and limit=1, return advisory ID +async fn list_advisory_random_single( + user: &mut GooseUser, +) -> Result> { + let total = get_cached_advisory_total(user).await?; + // Generate random offset + let offset = rand::thread_rng().gen_range(0..=total); + let url = format!("/api/v2/advisory?offset={}&limit=1", offset); + + let response = user.get(&url).await?; + let json_data = response.response?.json::().await?; + + // Extract advisory ID from the response + if let Some(items) = json_data.get("items").and_then(|i| i.as_array()) { + if let Some(first_item) = items.first() { + if let Some(id) = first_item.get("uuid").and_then(|u| u.as_str()) { + log::info!("Listing advisory with offset {}: {}", offset, id); + return Ok(id.to_string()); + } + } + } + + // Return error if no advisory found + Err(Box::new(TransactionError::Custom(format!( + "No advisory found at offset: {}", + offset + )))) +} + +// + /// Send Advisory labels request async fn send_advisory_label_request( advisory_id: String, @@ -99,13 +175,10 @@ async fn send_advisory_label_request( } /// Send Advisory labels request using PUT method -pub async fn put_advisory_labels( - advisory_ids: Vec, - counter: Arc, - user: &mut GooseUser, -) -> TransactionResult { +pub async fn put_advisory_labels(user: &mut GooseUser) -> TransactionResult { + let advisory_id = list_advisory_random_single(user).await?; send_advisory_label_request( - advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(), + advisory_id, user, GooseMethod::Put, "It's a put request", @@ -115,13 +188,10 @@ pub async fn put_advisory_labels( } /// Send Advisory labels request using PATCH method -pub async fn patch_advisory_labels( - advisory_ids: Vec, - counter: Arc, - user: &mut GooseUser, -) -> TransactionResult { +pub async fn patch_advisory_labels(user: &mut GooseUser) -> TransactionResult { + let advisory_id = list_advisory_random_single(user).await?; send_advisory_label_request( - advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(), + advisory_id, user, GooseMethod::Patch, "It's a patch request", diff --git a/src/scenario/mod.rs b/src/scenario/mod.rs index 01f8f54..a370d2b 100644 --- a/src/scenario/mod.rs +++ b/src/scenario/mod.rs @@ -102,12 +102,6 @@ pub(crate) struct Scenario { #[serde(with = "required")] pub get_advisory: Option, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub put_advisory_lables: Option>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub patch_advisory_lables: Option>, } impl Scenario { @@ -150,23 +144,6 @@ impl Scenario { let download_advisory = Some(loader.download_advisory().await?); let get_advisory = Some(loader.download_advisory().await?); - let put_advisory_lables = Some( - loader - .put_advisory_lables() - .await? - .iter() - .map(|advisory_id| format!("urn:uuid:{advisory_id}")) - .collect(), - ); - let patch_advisory_lables = Some( - loader - .patch_advisory_lables() - .await? - .iter() - .map(|advisory_id| format!("urn:uuid:{advisory_id}")) - .collect(), - ); - Ok(Self { get_sbom: large_sbom_digest.clone(), get_sbom_advisories: large_sbom_digest.clone(), @@ -183,8 +160,6 @@ impl Scenario { delete_sbom_pool, download_advisory, get_advisory, - put_advisory_lables, - patch_advisory_lables, }) } } @@ -401,32 +376,6 @@ FROM public.advisory order by modified desc limit 1;"#, ) .await } - - /// Advisory IDs for put labels - pub async fn put_advisory_lables(&self) -> anyhow::Result> { - let mut db = crate::db::connect(&self.db).await?; - - let rows = sqlx::query("SELECT id::text as id FROM public.advisory where labels is not null order by modified desc limit 20;") - .fetch_all(&mut db) - .await?; - Ok(rows - .into_iter() - .map(|row| row.get::("id")) - .collect()) - } - - /// Advisory IDs for patch labels - pub async fn patch_advisory_lables(&self) -> anyhow::Result> { - let mut db = crate::db::connect(&self.db).await?; - - let rows = sqlx::query("SELECT id::text as id FROM public.advisory where labels is not null order by modified desc OFFSET 20 limit 20;") - .fetch_all(&mut db) - .await?; - Ok(rows - .into_iter() - .map(|row| row.get::("id")) - .collect()) - } } #[cfg(test)]