Skip to content

Commit e195f7d

Browse files
committed
TC-3071 randomly fetch advisory ID
1 parent 7e12e20 commit e195f7d

File tree

6 files changed

+89
-126
lines changed

6 files changed

+89
-126
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "uuid",
2222
tokio = { version = "1.43.1", features = ["sync"] }
2323
urlencoding = "2"
2424
packageurl = "0.4.2"
25+
rand = "0.8"
2526

2627
[features]
2728
default = ["postgres"]

scenarios/full-20250604.json5

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -113,49 +113,5 @@
113113
"urn:uuid:01973122-3bdd-78a3-898a-fb67c06387ed"
114114
],
115115
"download_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89",
116-
"get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89",
117-
"put_advisory_lables": [
118-
"urn:uuid:24ae57c3-4b57-4f4e-82c1-83ae26059a89",
119-
"urn:uuid:9ffb1a1b-14e0-4abb-943a-76906daa54aa",
120-
"urn:uuid:7c932cc3-aa83-4bb4-bbaf-6030a198e271",
121-
"urn:uuid:cac4862f-c87a-4a7d-8d67-f2e0db8f0273",
122-
"urn:uuid:f80b5b7e-f2eb-4921-b254-a4873d69a9e9",
123-
"urn:uuid:858a53a2-347a-491e-a86a-e24b9a4c1436",
124-
"urn:uuid:15637501-2a0b-4076-aec3-d92fa2b959bf",
125-
"urn:uuid:5364258d-418e-4cd8-add7-248207c4836f",
126-
"urn:uuid:5066569d-c5a3-4d90-8cdc-d8680ee52db0",
127-
"urn:uuid:869b1646-2f27-4b7a-b4e0-f6783651135d",
128-
"urn:uuid:04313720-b5eb-40bc-b545-1f813f6e53b9",
129-
"urn:uuid:2c6d41c1-f39d-47df-b37f-12b08af41577",
130-
"urn:uuid:99bac490-3ff3-4c10-8466-2cc4947ac29c",
131-
"urn:uuid:90a53b25-118d-4c2a-9b8d-a38ac1a85ab0",
132-
"urn:uuid:4d3fd5be-eeb5-46f7-a55d-871be40c9837",
133-
"urn:uuid:fd88f7cd-8b9e-4bba-b92b-726732545e82",
134-
"urn:uuid:06a769a4-1f30-4fe5-9113-72080301e8ed",
135-
"urn:uuid:871263d1-e7c3-4f4e-98b2-04aedeae048c",
136-
"urn:uuid:35dc833f-fbb7-43f0-852a-9db14cd3a0f4",
137-
"urn:uuid:acfbea2f-08d0-4f2f-aa22-12e635956dce"
138-
],
139-
"patch_advisory_lables": [
140-
"urn:uuid:8a28d2fc-3e91-4989-add8-b9dd78fcf37d",
141-
"urn:uuid:9e3eee98-a032-424d-9d0d-1645faca9d24",
142-
"urn:uuid:66dde1a5-9575-46a4-8f7a-f6ab9904f0d1",
143-
"urn:uuid:2070a289-c6bb-4a6c-bdb2-6fc951743e55",
144-
"urn:uuid:59751bb1-b205-4ea4-9832-a48ab69e39e6",
145-
"urn:uuid:0dd5f452-af2b-4740-905e-a00f22fffa7e",
146-
"urn:uuid:08467200-8c82-4d38-9920-3cbf6c2d0568",
147-
"urn:uuid:be6b4acf-fe7b-46a2-9f4c-fdd497d43188",
148-
"urn:uuid:6ddb2cc0-bb10-4f23-96bf-cc409263d80d",
149-
"urn:uuid:3973cb57-325a-4574-a60c-1910a30b44ac",
150-
"urn:uuid:074881e9-d328-402c-8bd8-13df42a590e5",
151-
"urn:uuid:2e8008f1-e3d2-401d-8ef6-87bad7d328f0",
152-
"urn:uuid:d30949a9-1f9b-4005-93a8-288a3578e7f7",
153-
"urn:uuid:8aa4555d-3723-4c4f-aee8-69c4007ec5fd",
154-
"urn:uuid:6076bbcb-b352-4bcb-b2e3-8889e0310724",
155-
"urn:uuid:5390d139-c00c-4d3a-858a-833d4b571d9a",
156-
"urn:uuid:f1479e4b-839e-4d53-929c-d08b6b907430",
157-
"urn:uuid:96c1b100-ec62-46c0-9156-819c37bd0445",
158-
"urn:uuid:148db4ee-55ca-4292-b55d-a0c0a9d83b9c",
159-
"urn:uuid:e98e73ab-4cfe-4861-8b46-06df5a84198f"
160-
]
116+
"get_advisory": "24ae57c3-4b57-4f4e-82c1-83ae26059a89"
161117
}

src/main.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ async fn main() -> Result<(), anyhow::Error> {
180180
.register_transaction(tx!(list_sboms_paginated))
181181
.register_transaction(tx!(get_analysis_status))
182182
.register_transaction(tx!(get_analysis_latest_cpe))
183-
.register_transaction(tx!(list_advisory_labels));
183+
.register_transaction(tx!(list_advisory_labels))
184+
.register_transaction(tx!(patch_advisory_labels))
185+
.register_transaction(tx!(put_advisory_labels));
184186

185187
tx!(s.get_sbom?(scenario.get_sbom.clone()));
186188
tx!(s.get_sbom_advisories?(scenario.get_sbom_advisories.clone()));
@@ -193,22 +195,6 @@ async fn main() -> Result<(), anyhow::Error> {
193195
tx!(s.get_purl_details?(scenario.get_purl_details.clone()));
194196
tx!(s.get_recommendations?(scenario.get_recommendations.clone()));
195197

196-
// Register put Advisory labels transaction if pool is available
197-
let put_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
198-
if let Some(_advisory_ids) = scenario.put_advisory_lables.clone() {
199-
tx!(s.put_advisory_labels?(scenario.put_advisory_lables.clone(),
200-
put_advisory_labels_counter.clone()),
201-
name: format!("put_advisory_labels"));
202-
}
203-
204-
// Register patch Advisory labels transaction if pool is available
205-
let patch_advisory_labels_counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
206-
if let Some(_advisory_ids) = scenario.patch_advisory_lables.clone() {
207-
tx!(s.patch_advisory_labels?(scenario.patch_advisory_lables.clone(),
208-
patch_advisory_labels_counter.clone()),
209-
name: format!("patch_advisory_labels"));
210-
}
211-
212198
tx!(s.download_advisory?(scenario.download_advisory.clone()));
213199
tx!(s.get_advisory?(scenario.get_advisory.clone()));
214200
s

src/restapi.rs

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
use crate::utils::DisplayVec;
2-
use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionResult};
2+
use goose::goose::{GooseMethod, GooseRequest, GooseUser, TransactionError, TransactionResult};
33
use reqwest::{Client, RequestBuilder};
44

5+
use rand::Rng;
56
use serde_json::json;
67
use std::sync::{
78
Arc,
89
atomic::{AtomicUsize, Ordering},
910
};
11+
use tokio::sync::OnceCell;
1012
use urlencoding::encode;
1113

14+
// Static variable to store advisory total count
15+
static ADVISORY_TOTAL: OnceCell<u64> = OnceCell::const_new();
16+
1217
pub async fn get_advisory(id: String, user: &mut GooseUser) -> TransactionResult {
1318
let uri = format!("/api/v2/advisory/{}", encode(&format!("urn:uuid:{}", id)));
1419

@@ -46,6 +51,46 @@ pub async fn list_advisory(user: &mut GooseUser) -> TransactionResult {
4651
Ok(())
4752
}
4853

54+
/// Get advisory total count and store it in static OnceCell
55+
async fn get_advisory_total(user: &mut GooseUser) -> Result<u64, Box<TransactionError>> {
56+
let response = user.get("/api/v2/advisory").await?;
57+
let json_data = response.response?.json::<serde_json::Value>().await?;
58+
59+
// Extract total from the response
60+
if let Some(total) = json_data.get("total").and_then(|t| t.as_u64()) {
61+
log::info!("Advisory total count: {}", total);
62+
return Ok(total);
63+
}
64+
65+
Err(Box::new(TransactionError::Custom(
66+
"Failed to get advisory total count".to_string(),
67+
)))
68+
}
69+
70+
/// Get cached advisory total count, fetch if not available using get_or_init
71+
async fn get_cached_advisory_total(user: &mut GooseUser) -> Result<u64, Box<TransactionError>> {
72+
// Try to get from cache first
73+
if let Some(&total) = ADVISORY_TOTAL.get() {
74+
return Ok(total);
75+
}
76+
77+
// If not cached, fetch it and handle errors properly
78+
match get_advisory_total(user).await {
79+
Ok(total) => {
80+
// Store in cache for future use
81+
let _ = ADVISORY_TOTAL.set(total);
82+
Ok(total)
83+
}
84+
Err(e) => {
85+
// Propagate the error with context instead of silently returning 0
86+
Err(Box::new(TransactionError::Custom(format!(
87+
"Failed to get advisory total count: {}",
88+
e
89+
))))
90+
}
91+
}
92+
}
93+
4994
pub async fn list_advisory_paginated(user: &mut GooseUser) -> TransactionResult {
5095
let _response = user.get("/api/v2/advisory?offset=100&limit=10").await?;
5196

@@ -69,6 +114,37 @@ pub async fn search_advisory(user: &mut GooseUser) -> TransactionResult {
69114
Ok(())
70115
}
71116

117+
/// List advisory with random offset and limit=1, return advisory ID
118+
async fn list_advisory_random_single(
119+
user: &mut GooseUser,
120+
) -> Result<String, Box<TransactionError>> {
121+
let total = get_cached_advisory_total(user).await?;
122+
// Generate random offset
123+
let offset = rand::thread_rng().gen_range(0..=total);
124+
let url = format!("/api/v2/advisory?offset={}&limit=1", offset);
125+
126+
let response = user.get(&url).await?;
127+
let json_data = response.response?.json::<serde_json::Value>().await?;
128+
129+
// Extract advisory ID from the response
130+
if let Some(items) = json_data.get("items").and_then(|i| i.as_array()) {
131+
if let Some(first_item) = items.first() {
132+
if let Some(id) = first_item.get("uuid").and_then(|u| u.as_str()) {
133+
log::info!("Listing advisory with offset {}: {}", offset, id);
134+
return Ok(id.to_string());
135+
}
136+
}
137+
}
138+
139+
// Return error if no advisory found
140+
Err(Box::new(TransactionError::Custom(format!(
141+
"No advisory found at offset: {}",
142+
offset
143+
))))
144+
}
145+
146+
//
147+
72148
/// Send Advisory labels request
73149
async fn send_advisory_label_request(
74150
advisory_id: String,
@@ -99,13 +175,10 @@ async fn send_advisory_label_request(
99175
}
100176

101177
/// Send Advisory labels request using PUT method
102-
pub async fn put_advisory_labels(
103-
advisory_ids: Vec<String>,
104-
counter: Arc<AtomicUsize>,
105-
user: &mut GooseUser,
106-
) -> TransactionResult {
178+
pub async fn put_advisory_labels(user: &mut GooseUser) -> TransactionResult {
179+
let advisory_id = list_advisory_random_single(user).await?;
107180
send_advisory_label_request(
108-
advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(),
181+
advisory_id,
109182
user,
110183
GooseMethod::Put,
111184
"It's a put request",
@@ -115,13 +188,10 @@ pub async fn put_advisory_labels(
115188
}
116189

117190
/// Send Advisory labels request using PATCH method
118-
pub async fn patch_advisory_labels(
119-
advisory_ids: Vec<String>,
120-
counter: Arc<AtomicUsize>,
121-
user: &mut GooseUser,
122-
) -> TransactionResult {
191+
pub async fn patch_advisory_labels(user: &mut GooseUser) -> TransactionResult {
192+
let advisory_id = list_advisory_random_single(user).await?;
123193
send_advisory_label_request(
124-
advisory_ids[counter.fetch_add(1, Ordering::Relaxed) % advisory_ids.len()].clone(),
194+
advisory_id,
125195
user,
126196
GooseMethod::Patch,
127197
"It's a patch request",

src/scenario/mod.rs

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,6 @@ pub(crate) struct Scenario {
102102

103103
#[serde(with = "required")]
104104
pub get_advisory: Option<String>,
105-
106-
#[serde(default, skip_serializing_if = "Option::is_none")]
107-
pub put_advisory_lables: Option<Vec<String>>,
108-
109-
#[serde(default, skip_serializing_if = "Option::is_none")]
110-
pub patch_advisory_lables: Option<Vec<String>>,
111105
}
112106

113107
impl Scenario {
@@ -150,23 +144,6 @@ impl Scenario {
150144
let download_advisory = Some(loader.download_advisory().await?);
151145
let get_advisory = Some(loader.download_advisory().await?);
152146

153-
let put_advisory_lables = Some(
154-
loader
155-
.put_advisory_lables()
156-
.await?
157-
.iter()
158-
.map(|advisory_id| format!("urn:uuid:{advisory_id}"))
159-
.collect(),
160-
);
161-
let patch_advisory_lables = Some(
162-
loader
163-
.patch_advisory_lables()
164-
.await?
165-
.iter()
166-
.map(|advisory_id| format!("urn:uuid:{advisory_id}"))
167-
.collect(),
168-
);
169-
170147
Ok(Self {
171148
get_sbom: large_sbom_digest.clone(),
172149
get_sbom_advisories: large_sbom_digest.clone(),
@@ -183,8 +160,6 @@ impl Scenario {
183160
delete_sbom_pool,
184161
download_advisory,
185162
get_advisory,
186-
put_advisory_lables,
187-
patch_advisory_lables,
188163
})
189164
}
190165
}
@@ -401,32 +376,6 @@ FROM public.advisory order by modified desc limit 1;"#,
401376
)
402377
.await
403378
}
404-
405-
/// Advisory IDs for put labels
406-
pub async fn put_advisory_lables(&self) -> anyhow::Result<Vec<String>> {
407-
let mut db = crate::db::connect(&self.db).await?;
408-
409-
let rows = sqlx::query("SELECT id::text as id FROM public.advisory where labels is not null order by modified desc limit 20;")
410-
.fetch_all(&mut db)
411-
.await?;
412-
Ok(rows
413-
.into_iter()
414-
.map(|row| row.get::<String, _>("id"))
415-
.collect())
416-
}
417-
418-
/// Advisory IDs for patch labels
419-
pub async fn patch_advisory_lables(&self) -> anyhow::Result<Vec<String>> {
420-
let mut db = crate::db::connect(&self.db).await?;
421-
422-
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;")
423-
.fetch_all(&mut db)
424-
.await?;
425-
Ok(rows
426-
.into_iter()
427-
.map(|row| row.get::<String, _>("id"))
428-
.collect())
429-
}
430379
}
431380

432381
#[cfg(test)]

0 commit comments

Comments
 (0)