upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-26 10:23:47 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-26 10:23:47 +0000
commit158d3f0722e731f2b534951069c322c5cbb5a721 (patch)
tree6170dcbc978f795ec52a7c6a832671db13ff33f1
parenta6edb42dfc653b6826b59b7f296e0d0c4ee74557 (diff)
feat(fixtures): reuse prerequisite fixtures in production mode
Fixtures now reuse their prerequisites in Shared (production) mode, significantly reducing events published to production relays: Before: Each fixture created its own prerequisite events - ValidRepo: 1 event - RepoWithIssue: 2 events (repo + issue) - RepoWithComment: 3 events (repo + issue + comment) - RepoState: 2 events (repo + state) After: Fixtures share prerequisites via caching - ValidRepo: 1 event - RepoWithIssue: 1 new event (issue), reuses cached repo - RepoWithComment: 1 new event (comment), reuses cached repo+issue - RepoState: 1 new event (state), reuses cached repo Total for all 4 fixtures: 8 events → 4 events (50% reduction) In CI/Isolated mode, each test still gets fresh fixtures for test isolation - behavior unchanged. Implemented via get_or_create_repo() and get_or_create_issue() helpers that handle mode-aware caching without async recursion.
-rw-r--r--grasp-audit/src/fixtures.rs108
1 files changed, 79 insertions, 29 deletions
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs
index 4b30cb6..9ccd703 100644
--- a/grasp-audit/src/fixtures.rs
+++ b/grasp-audit/src/fixtures.rs
@@ -214,6 +214,72 @@ impl<'a> TestContext<'a> {
214 Ok(event) 214 Ok(event)
215 } 215 }
216 216
217 /// Get or create a ValidRepo, with mode-appropriate caching.
218 /// This is a helper method that avoids async recursion by not going
219 /// through get_fixture. It handles the repo specifically.
220 async fn get_or_create_repo(&self) -> Result<Event> {
221 // In Shared mode, check cache first
222 if self.mode == ContextMode::Shared {
223 let cache = self.cache.lock().unwrap();
224 if let Some(event) = cache.get(&FixtureKind::ValidRepo) {
225 return Ok(event.clone());
226 }
227 }
228
229 // Create a new repo
230 let test_name = format!(
231 "fixture-{:?}-{}",
232 FixtureKind::ValidRepo,
233 &uuid::Uuid::new_v4().to_string()[..8]
234 );
235 let repo = self.client.create_repo_announcement(&test_name).await?;
236
237 // Send it
238 self.client.send_event(repo.clone()).await?;
239
240 // Cache it in Shared mode
241 if self.mode == ContextMode::Shared {
242 let mut cache = self.cache.lock().unwrap();
243 cache.insert(FixtureKind::ValidRepo, repo.clone());
244 }
245
246 Ok(repo)
247 }
248
249 /// Get or create a RepoWithIssue, with mode-appropriate caching.
250 /// Returns the issue event (repo is already sent/cached via get_or_create_repo).
251 async fn get_or_create_issue(&self) -> Result<Event> {
252 // In Shared mode, check cache first
253 if self.mode == ContextMode::Shared {
254 let cache = self.cache.lock().unwrap();
255 if let Some(event) = cache.get(&FixtureKind::RepoWithIssue) {
256 return Ok(event.clone());
257 }
258 }
259
260 // Get or create repo (reuses cached in Shared mode)
261 let repo = self.get_or_create_repo().await?;
262
263 // Create the issue
264 let issue = self.client.create_issue(
265 &repo,
266 "Test Issue",
267 "Issue content for testing",
268 vec![],
269 )?;
270
271 // Send it
272 self.client.send_event(issue.clone()).await?;
273
274 // Cache it in Shared mode
275 if self.mode == ContextMode::Shared {
276 let mut cache = self.cache.lock().unwrap();
277 cache.insert(FixtureKind::RepoWithIssue, issue.clone());
278 }
279
280 Ok(issue)
281 }
282
217 /// Build a fixture event (doesn't send it) 283 /// Build a fixture event (doesn't send it)
218 async fn build_fixture(&self, kind: FixtureKind) -> Result<Event> { 284 async fn build_fixture(&self, kind: FixtureKind) -> Result<Event> {
219 match kind { 285 match kind {
@@ -227,14 +293,11 @@ impl<'a> TestContext<'a> {
227 } 293 }
228 294
229 FixtureKind::RepoWithIssue => { 295 FixtureKind::RepoWithIssue => {
230 // First create and send repo 296 // Reuse ValidRepo fixture - this leverages caching in Shared mode
231 let test_name = format!( 297 // In Isolated mode: creates fresh repo
232 "fixture-{:?}-{}", 298 // In Shared mode: returns cached repo (no duplicate events!)
233 FixtureKind::ValidRepo, 299 // Uses direct helper to avoid async recursion through get_fixture
234 &uuid::Uuid::new_v4().to_string()[..8] 300 let repo = self.get_or_create_repo().await?;
235 );
236 let repo = self.client.create_repo_announcement(&test_name).await?;
237 self.client.send_event(repo.clone()).await?;
238 301
239 // Then create issue referencing it - this will have 'a' tag to repo 302 // Then create issue referencing it - this will have 'a' tag to repo
240 // Note: We build the issue but DON'T send it here - the caller will send it 303 // Note: We build the issue but DON'T send it here - the caller will send it
@@ -251,35 +314,21 @@ impl<'a> TestContext<'a> {
251 } 314 }
252 315
253 FixtureKind::RepoWithComment => { 316 FixtureKind::RepoWithComment => {
254 // First create repo with issue 317 // Reuse RepoWithIssue fixture - this leverages caching in Shared mode
255 let test_name = format!( 318 // In Isolated mode: creates fresh repo + issue
256 "fixture-{:?}-{}", 319 // In Shared mode: returns cached issue (repo already cached too!)
257 FixtureKind::ValidRepo, 320 let issue = self.get_or_create_issue().await?;
258 &uuid::Uuid::new_v4().to_string()[..8]
259 );
260 let repo = self.client.create_repo_announcement(&test_name).await?;
261 self.client.send_event(repo.clone()).await?;
262
263 let issue =
264 self.client
265 .create_issue(&repo, "Test Issue", "Issue content", vec![])?;
266 self.client.send_event(issue.clone()).await?;
267 321
268 // Then create comment on issue 322 // Then create comment on issue
323 // Note: We build the comment but DON'T send it here - the caller will send it
269 self.client.create_comment(&issue, "Test comment", vec![]) 324 self.client.create_comment(&issue, "Test comment", vec![])
270 } 325 }
271 326
272 FixtureKind::RepoState => { 327 FixtureKind::RepoState => {
273 use nostr_sdk::prelude::*; 328 use nostr_sdk::prelude::*;
274 329
275 // First create repo announcement 330 // Reuse ValidRepo fixture - this leverages caching in Shared mode
276 let test_name = format!( 331 let repo = self.get_or_create_repo().await?;
277 "fixture-{:?}-{}",
278 FixtureKind::ValidRepo,
279 &uuid::Uuid::new_v4().to_string()[..8]
280 );
281 let repo = self.client.create_repo_announcement(&test_name).await?;
282 self.client.send_event(repo.clone()).await?;
283 332
284 // Extract repo_id from repo announcement 333 // Extract repo_id from repo announcement
285 let repo_id = repo 334 let repo_id = repo
@@ -292,6 +341,7 @@ impl<'a> TestContext<'a> {
292 341
293 // Create state announcement with deterministic commit hash 342 // Create state announcement with deterministic commit hash
294 // Tag format: ["refs/heads/main", "<commit_hash>"] 343 // Tag format: ["refs/heads/main", "<commit_hash>"]
344 // Note: We build the state but DON'T send it here - the caller will send it
295 self.client 345 self.client
296 .event_builder(Kind::Custom(30618), "") 346 .event_builder(Kind::Custom(30618), "")
297 .tag(Tag::identifier(&repo_id)) 347 .tag(Tag::identifier(&repo_id))