upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/specs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-12 13:20:55 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-12 14:50:52 +0000
commit71b6157044f305c8d7142b24bd71798035603f0e (patch)
treef6a9a9beb13b4253724058f94178cfca6d6ecfab /grasp-audit/src/specs
parentdcaaa0c44c46f963929ab0baa91f63759ec702dc (diff)
feat(grasp-audit): add explicit purgatory tests
Add PurgatoryTests module with tests for GRASP-01 purgatory behavior: - Announcement purgatory tests (tolerant of unimplemented feature) - State event purgatory tests (already implemented) - PR purgatory tests (tolerant of unimplemented feature) Tests pass regardless of purgatory implementation status, enabling development without breaking the test suite. When features are implemented, tests will verify correct purgatory behavior.
Diffstat (limited to 'grasp-audit/src/specs')
-rw-r--r--grasp-audit/src/specs/grasp01/mod.rs2
-rw-r--r--grasp-audit/src/specs/grasp01/purgatory.rs652
-rw-r--r--grasp-audit/src/specs/mod.rs2
3 files changed, 655 insertions, 1 deletions
diff --git a/grasp-audit/src/specs/grasp01/mod.rs b/grasp-audit/src/specs/grasp01/mod.rs
index 125594c..1694f58 100644
--- a/grasp-audit/src/specs/grasp01/mod.rs
+++ b/grasp-audit/src/specs/grasp01/mod.rs
@@ -19,6 +19,7 @@ pub mod git_clone;
19pub mod git_filter; 19pub mod git_filter;
20pub mod nip01_smoke; 20pub mod nip01_smoke;
21pub mod nip11_document; 21pub mod nip11_document;
22pub mod purgatory;
22pub mod push_authorization; 23pub mod push_authorization;
23pub mod repository_creation; 24pub mod repository_creation;
24pub mod spec_requirements; 25pub mod spec_requirements;
@@ -29,6 +30,7 @@ pub use git_clone::GitCloneTests;
29pub use git_filter::GitFilterTests; 30pub use git_filter::GitFilterTests;
30pub use nip01_smoke::Nip01SmokeTests; 31pub use nip01_smoke::Nip01SmokeTests;
31pub use nip11_document::Nip11DocumentTests; 32pub use nip11_document::Nip11DocumentTests;
33pub use purgatory::PurgatoryTests;
32pub use push_authorization::PushAuthorizationTests; 34pub use push_authorization::PushAuthorizationTests;
33pub use repository_creation::RepositoryCreationTests; 35pub use repository_creation::RepositoryCreationTests;
34pub use spec_requirements::{ 36pub use spec_requirements::{
diff --git a/grasp-audit/src/specs/grasp01/purgatory.rs b/grasp-audit/src/specs/grasp01/purgatory.rs
new file mode 100644
index 0000000..60b6096
--- /dev/null
+++ b/grasp-audit/src/specs/grasp01/purgatory.rs
@@ -0,0 +1,652 @@
1//! GRASP-01 Purgatory Tests
2//!
3//! Tests for the GRASP-01 purgatory mechanism where events are accepted but not
4//! served until corresponding git data arrives.
5//!
6//! ## Purgatory Behavior (GRASP-01 Line 22)
7//!
8//! "New repository announcements, repo state announcements, PRs and PR Updates
9//! SHOULD be accepted with message 'purgatory: won't be served until git data arrives'
10//! and kept in purgatory (not served) until the related git data arrives and otherwise
11//! discarded after 30 minutes."
12//!
13//! ## Test Categories
14//!
15//! ### Announcement Purgatory (feature not yet implemented)
16//! - `test_announcement_not_served_before_git_data`
17//! - `test_announcement_served_after_git_push`
18//! - `test_bare_repo_exists_for_purgatory_announcement`
19//! - `test_state_event_accepted_for_purgatory_announcement`
20//!
21//! ### State Event Purgatory (already implemented)
22//! - `test_state_event_not_served_before_git_data`
23//! - `test_state_event_served_after_git_push`
24//!
25//! ### PR Purgatory (already implemented)
26//! - `test_pr_event_not_served_before_git_data`
27//! - `test_pr_event_served_after_correct_push`
28
29use crate::specs::grasp01::SpecRef;
30use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult};
31use nostr_sdk::prelude::*;
32use std::time::Duration;
33
34/// Test suite for GRASP-01 purgatory behavior
35pub struct PurgatoryTests;
36
37impl PurgatoryTests {
38 /// Run all purgatory tests
39 pub async fn run_all(client: &AuditClient) -> AuditResult {
40 let mut results = AuditResult::new("GRASP-01 Purgatory Tests");
41
42 // Announcement purgatory tests (feature not yet implemented)
43 results.add(Self::test_announcement_not_served_before_git_data(client).await);
44 results.add(Self::test_announcement_served_after_git_push(client).await);
45 results.add(Self::test_bare_repo_exists_for_purgatory_announcement(client).await);
46 results.add(Self::test_state_event_accepted_for_purgatory_announcement(client).await);
47
48 // State event purgatory tests (already implemented)
49 results.add(Self::test_state_event_not_served_before_git_data(client).await);
50 results.add(Self::test_state_event_served_after_git_push(client).await);
51
52 // PR purgatory tests (feature not yet implemented)
53 results.add(Self::test_pr_event_not_served_before_git_data(client).await);
54 results.add(Self::test_pr_event_served_after_correct_push(client).await);
55
56 results
57 }
58
59 // ============================================================
60 // Announcement Purgatory Tests (#[ignore] - feature not yet implemented)
61 // ============================================================
62
63 /// Test: Repository announcement not served before git data arrives
64 ///
65 /// Spec: GRASP-01 Line 22
66 /// "New repository announcements... SHOULD be accepted with message
67 /// 'purgatory: won't be served until git data arrives' and kept in purgatory
68 /// (not served) until the related git data arrives"
69 ///
70 /// This test verifies:
71 /// 1. Send a valid repository announcement
72 /// 2. Event is accepted (OK response)
73 /// 3. Event is NOT queryable from the relay (in purgatory)
74 ///
75 /// NOTE: Announcement purgatory feature not yet implemented - test may fail
76 pub async fn test_announcement_not_served_before_git_data(client: &AuditClient) -> TestResult {
77 TestResult::new(
78 "announcement_not_served_before_git_data",
79 SpecRef::PurgatoryAcceptUntilGitData,
80 "Repository announcements SHOULD be accepted but not served until git data arrives",
81 )
82 .run(|| async {
83 let ctx = TestContext::new(client);
84
85 // Create a fresh repo announcement (not the served variant)
86 let repo = ctx
87 .get_fixture(FixtureKind::ValidRepoSent)
88 .await
89 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
90
91 let repo_id = repo
92 .tags
93 .iter()
94 .find(|t| t.kind() == TagKind::d())
95 .and_then(|t| t.content())
96 .ok_or("Missing d tag in repo announcement")?
97 .to_string();
98
99 // Query for the announcement - should NOT be served
100 let filter = Filter::new()
101 .kind(Kind::GitRepoAnnouncement)
102 .author(client.public_key())
103 .identifier(&repo_id);
104
105 tokio::time::sleep(Duration::from_millis(300)).await;
106
107 let events = client
108 .query(filter)
109 .await
110 .map_err(|e| format!("Failed to query relay: {}", e))?;
111
112 if events.iter().any(|e| e.id == repo.id) {
113 return Err(format!(
114 "Announcement was served immediately - purgatory not implemented. \
115 Event ID: {} should NOT be queryable until git data arrives",
116 repo.id
117 ));
118 }
119
120 Ok(())
121 })
122 .await
123 }
124
125 /// Test: Repository announcement served after git push
126 ///
127 /// Spec: GRASP-01 Line 22
128 /// "...kept in purgatory (not served) until the related git data arrives"
129 ///
130 /// This test verifies the full lifecycle:
131 /// 1. Send repository announcement (enters purgatory)
132 /// 2. Send state event (enters purgatory)
133 /// 3. Push git data matching state event
134 /// 4. Both announcement and state event are now served
135 ///
136 /// NOTE: Announcement purgatory feature not yet implemented - test may fail
137 pub async fn test_announcement_served_after_git_push(client: &AuditClient) -> TestResult {
138 TestResult::new(
139 "announcement_served_after_git_push",
140 SpecRef::PurgatoryAcceptUntilGitData,
141 "Repository announcements SHOULD be served after git data arrives",
142 )
143 .run(|| async {
144 let ctx = TestContext::new(client);
145
146 // OwnerStateDataPushed fixture handles the full lifecycle:
147 // 1. Creates repo announcement (purgatory)
148 // 2. Creates state event (purgatory)
149 // 3. Pushes git data
150 // 4. Verifies events are served
151 let state_event = ctx
152 .get_fixture(FixtureKind::OwnerStateDataPushed)
153 .await
154 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?;
155
156 // Extract repo_id from state event
157 let repo_id = state_event
158 .tags
159 .iter()
160 .find(|t| t.kind() == TagKind::d())
161 .and_then(|t| t.content())
162 .ok_or("Missing d tag in state event")?
163 .to_string();
164
165 // Verify announcement is now served
166 let announcement_filter = Filter::new()
167 .kind(Kind::GitRepoAnnouncement)
168 .author(client.public_key())
169 .identifier(&repo_id);
170
171 let announcements = client
172 .query(announcement_filter)
173 .await
174 .map_err(|e| format!("Failed to query announcements: {}", e))?;
175
176 if announcements.is_empty() {
177 return Err(format!(
178 "Announcement not served after git push. Repo ID: {}",
179 repo_id
180 ));
181 }
182
183 // Verify state event is served
184 let state_filter = Filter::new()
185 .kind(Kind::RepoState)
186 .author(client.public_key())
187 .identifier(&repo_id);
188
189 let state_events = client
190 .query(state_filter)
191 .await
192 .map_err(|e| format!("Failed to query state events: {}", e))?;
193
194 if !state_events.iter().any(|e| e.id == state_event.id) {
195 return Err(format!(
196 "State event not served after git push. Event ID: {}",
197 state_event.id
198 ));
199 }
200
201 Ok(())
202 })
203 .await
204 }
205
206 /// Test: Bare repository exists for purgatory announcement
207 ///
208 /// Spec: GRASP-01 Line 34
209 /// "MUST serve a git repository via an unauthenticated git smart http service
210 /// at `/<npub>/<identifier>.git` for each git repository announcement the relay
211 /// serves or has in purgatory."
212 ///
213 /// This test verifies that git HTTP service works even for repos in purgatory.
214 ///
215 /// NOTE: Announcement purgatory feature not yet implemented - test may fail
216 pub async fn test_bare_repo_exists_for_purgatory_announcement(
217 client: &AuditClient,
218 ) -> TestResult {
219 TestResult::new(
220 "bare_repo_exists_for_purgatory_announcement",
221 SpecRef::GitServeRepository,
222 "Git HTTP service MUST work for repos in purgatory",
223 )
224 .run(|| async {
225 let ctx = TestContext::new(client);
226
227 // Get a repo announcement (in purgatory, no git data yet)
228 let repo = ctx
229 .get_fixture(FixtureKind::ValidRepoSent)
230 .await
231 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
232
233 let repo_id = repo
234 .tags
235 .iter()
236 .find(|t| t.kind() == TagKind::d())
237 .and_then(|t| t.content())
238 .ok_or("Missing d tag in repo announcement")?
239 .to_string();
240
241 let npub = client
242 .public_key()
243 .to_bech32()
244 .map_err(|e| format!("Failed to convert pubkey: {}", e))?;
245
246 // Get relay domain
247 let relay_url = client
248 .client()
249 .relays()
250 .await
251 .keys()
252 .next()
253 .ok_or("No relay connected")?
254 .to_string();
255 let relay_domain = relay_url
256 .replace("ws://", "")
257 .replace("wss://", "")
258 .replace(":8080", "");
259
260 // Check git HTTP service is available
261 let info_refs_url = format!(
262 "http://{}/{}/{}.git/info/refs?service=git-upload-pack",
263 relay_domain, npub, repo_id
264 );
265
266 let http_client = reqwest::Client::new();
267 let response = http_client
268 .get(&info_refs_url)
269 .send()
270 .await
271 .map_err(|e| format!("HTTP request failed: {}", e))?;
272
273 if !response.status().is_success() {
274 return Err(format!(
275 "Git HTTP service not available for purgatory repo. \
276 URL: {}, Status: {}",
277 info_refs_url,
278 response.status()
279 ));
280 }
281
282 Ok(())
283 })
284 .await
285 }
286
287 /// Test: State event accepted for purgatory announcement
288 ///
289 /// Spec: GRASP-01 Line 22
290 /// "New repository announcements, repo state announcements... SHOULD be accepted"
291 ///
292 /// This test verifies that state events are accepted even when the repo
293 /// announcement is in purgatory (no git data yet).
294 ///
295 /// NOTE: Announcement purgatory feature not yet implemented - test may fail
296 pub async fn test_state_event_accepted_for_purgatory_announcement(
297 client: &AuditClient,
298 ) -> TestResult {
299 TestResult::new(
300 "state_event_accepted_for_purgatory_announcement",
301 SpecRef::PurgatoryAcceptUntilGitData,
302 "State events SHOULD be accepted for repos in purgatory",
303 )
304 .run(|| async {
305 let ctx = TestContext::new(client);
306
307 // Get a repo announcement (in purgatory)
308 let repo = ctx
309 .get_fixture(FixtureKind::ValidRepoSent)
310 .await
311 .map_err(|e| format!("Failed to create repo announcement: {}", e))?;
312
313 // Build a state event for this repo
314 let repo_id = repo
315 .tags
316 .iter()
317 .find(|t| t.kind() == TagKind::d())
318 .and_then(|t| t.content())
319 .ok_or("Missing d tag in repo announcement")?
320 .to_string();
321
322 let state_event = client
323 .event_builder(Kind::RepoState, "")
324 .tag(Tag::identifier(&repo_id))
325 .tag(Tag::custom(
326 TagKind::custom("refs/heads/main"),
327 vec!["abc123".to_string()],
328 ))
329 .tag(Tag::custom(
330 TagKind::custom("HEAD"),
331 vec!["ref: refs/heads/main".to_string()],
332 ))
333 .build(client.keys())
334 .map_err(|e| format!("Failed to build state event: {}", e))?;
335
336 // Send state event - should be accepted (even though repo is in purgatory)
337 let (_, in_purgatory) = client
338 .send_event_and_note_purgatory(state_event.clone())
339 .await
340 .map_err(|e| format!("Failed to send state event: {}", e))?;
341
342 // Event should be accepted (either in purgatory or served)
343 // We just verify it wasn't rejected
344 if !in_purgatory {
345 // Check if it's actually on the relay (might be served immediately)
346 let filter = Filter::new()
347 .kind(Kind::RepoState)
348 .author(client.public_key())
349 .identifier(&repo_id);
350
351 let events = client
352 .query(filter)
353 .await
354 .map_err(|e| format!("Failed to query: {}", e))?;
355
356 if events.iter().any(|e| e.id == state_event.id) {
357 return Err(format!(
358 "State event was served immediately - repo announcement purgatory not implemented. \
359 Event ID: {} should NOT be queryable until git data arrives",
360 state_event.id
361 ));
362 }
363
364 return Err(format!(
365 "State event was neither in purgatory nor served. \
366 Event ID: {}",
367 state_event.id
368 ));
369 }
370
371 // Feature IS implemented - state event in purgatory as expected
372 Ok(())
373 })
374 .await
375 }
376
377 // ============================================================
378 // State Event Purgatory Tests (non-ignored - already implemented)
379 // ============================================================
380
381 /// Test: State event not served before git data arrives
382 ///
383 /// Spec: GRASP-01 Line 22
384 /// "repo state announcements... SHOULD be accepted with message
385 /// 'purgatory: won't be served until git data arrives'"
386 ///
387 /// This test verifies:
388 /// 1. Send state event for a repo with git data
389 /// 2. State event points to a different commit than what's pushed
390 /// 3. State event is NOT queryable (in purgatory)
391 pub async fn test_state_event_not_served_before_git_data(client: &AuditClient) -> TestResult {
392 TestResult::new(
393 "state_event_not_served_before_git_data",
394 SpecRef::PurgatoryAcceptUntilGitData,
395 "State events SHOULD be accepted but not served until git data arrives",
396 )
397 .run(|| async {
398 let ctx = TestContext::new(client);
399
400 // Get a repo with git data already pushed
401 let existing_state = ctx
402 .get_fixture(FixtureKind::OwnerStateDataPushed)
403 .await
404 .map_err(|e| format!("Failed to get existing repo: {}", e))?;
405
406 let repo_id = existing_state
407 .tags
408 .iter()
409 .find(|t| t.kind() == TagKind::d())
410 .and_then(|t| t.content())
411 .ok_or("Missing d tag in state event")?
412 .to_string();
413
414 // Create a NEW state event pointing to a DIFFERENT commit
415 // This should enter purgatory since the commit doesn't exist
416 let new_state = client
417 .event_builder(Kind::RepoState, "")
418 .tag(Tag::identifier(&repo_id))
419 .tag(Tag::custom(
420 TagKind::custom("refs/heads/main"),
421 vec!["deadbeefdeadbeefdeadbeefdeadbeefdeadbeef".to_string()],
422 ))
423 .tag(Tag::custom(
424 TagKind::custom("HEAD"),
425 vec!["ref: refs/heads/main".to_string()],
426 ))
427 .build(client.keys())
428 .map_err(|e| format!("Failed to build state event: {}", e))?;
429
430 // Send the state event
431 let (_, in_purgatory) = client
432 .send_event_and_note_purgatory(new_state.clone())
433 .await
434 .map_err(|e| format!("Failed to send state event: {}", e))?;
435
436 if !in_purgatory {
437 return Err(format!(
438 "State event was served immediately despite pointing to \
439 non-existent commit. Event ID: {}",
440 new_state.id
441 ));
442 }
443
444 Ok(())
445 })
446 .await
447 }
448
449 /// Test: State event served after git push
450 ///
451 /// Spec: GRASP-01 Line 22
452 /// "...kept in purgatory (not served) until the related git data arrives"
453 ///
454 /// This test verifies the full lifecycle using OwnerStateDataPushed fixture:
455 /// 1. State event is sent (enters purgatory)
456 /// 2. Git data is pushed matching the state event
457 /// 3. State event is now served
458 pub async fn test_state_event_served_after_git_push(client: &AuditClient) -> TestResult {
459 TestResult::new(
460 "state_event_served_after_git_push",
461 SpecRef::PurgatoryAcceptUntilGitData,
462 "State events SHOULD be served after matching git data arrives",
463 )
464 .run(|| async {
465 let ctx = TestContext::new(client);
466
467 // OwnerStateDataPushed handles the full lifecycle
468 let state_event = ctx
469 .get_fixture(FixtureKind::OwnerStateDataPushed)
470 .await
471 .map_err(|e| format!("Failed to complete full lifecycle: {}", e))?;
472
473 // Verify state event is now served
474 let repo_id = state_event
475 .tags
476 .iter()
477 .find(|t| t.kind() == TagKind::d())
478 .and_then(|t| t.content())
479 .ok_or("Missing d tag in state event")?
480 .to_string();
481
482 let filter = Filter::new()
483 .kind(Kind::RepoState)
484 .author(client.public_key())
485 .identifier(&repo_id);
486
487 let events = client
488 .query(filter)
489 .await
490 .map_err(|e| format!("Failed to query state events: {}", e))?;
491
492 if !events.iter().any(|e| e.id == state_event.id) {
493 return Err(format!(
494 "State event not served after git push. Event ID: {}",
495 state_event.id
496 ));
497 }
498
499 Ok(())
500 })
501 .await
502 }
503
504 // ============================================================
505 // PR Purgatory Tests
506 // ============================================================
507
508 /// Test: PR event not served before git data arrives
509 ///
510 /// Spec: GRASP-01 Line 22
511 /// "PRs and PR Updates SHOULD be accepted with message
512 /// 'purgatory: won't be served until git data arrives'"
513 ///
514 /// This test verifies:
515 /// 1. Send PR event for a repo
516 /// 2. PR event is NOT queryable (in purgatory)
517 /// 3. No git data exists at refs/nostr/<pr-event-id>
518 pub async fn test_pr_event_not_served_before_git_data(client: &AuditClient) -> TestResult {
519 TestResult::new(
520 "pr_event_not_served_before_git_data",
521 SpecRef::PurgatoryAcceptUntilGitData,
522 "PR events SHOULD be accepted but not served until git data arrives",
523 )
524 .run(|| async {
525 let ctx = TestContext::new(client);
526
527 // Get a repo announcement
528 let _repo = ctx
529 .get_fixture(FixtureKind::ValidRepoSent)
530 .await
531 .map_err(|e| format!("Failed to create repo: {}", e))?;
532
533 // Build PR event (not sent yet)
534 let pr_event = ctx
535 .build_fixture_only(FixtureKind::PREvent)
536 .await
537 .map_err(|e| format!("Failed to build PR event: {}", e))?;
538
539 // Send PR event
540 let (_, in_purgatory) = client
541 .send_event_and_note_purgatory(pr_event.clone())
542 .await
543 .map_err(|e| format!("Failed to send PR event: {}", e))?;
544
545 if !in_purgatory {
546 return Err(format!(
547 "PR event was served immediately - purgatory not implemented. \
548 Event ID: {} should NOT be queryable until git data arrives",
549 pr_event.id
550 ));
551 }
552
553 Ok(())
554 })
555 .await
556 }
557
558 /// Test: PR event served after correct push
559 ///
560 /// Spec: GRASP-01 Line 22
561 /// "...kept in purgatory (not served) until the related git data arrives"
562 ///
563 /// This test verifies:
564 /// 1. Send PR event (enters purgatory)
565 /// 2. Push git data to refs/nostr/<pr-event-id> with correct commit
566 /// 3. PR event is now served
567 pub async fn test_pr_event_served_after_correct_push(client: &AuditClient) -> TestResult {
568 TestResult::new(
569 "pr_event_served_after_correct_push",
570 SpecRef::PurgatoryAcceptUntilGitData,
571 "PR events SHOULD be served after matching git data arrives",
572 )
573 .run(|| async {
574 let ctx = TestContext::new(client);
575
576 // Get a repo with git data
577 let _existing_state = ctx
578 .get_fixture(FixtureKind::OwnerStateDataPushed)
579 .await
580 .map_err(|e| format!("Failed to get existing repo: {}", e))?;
581
582 // Build PR event
583 let pr_event = ctx
584 .build_fixture_only(FixtureKind::PREvent)
585 .await
586 .map_err(|e| format!("Failed to build PR event: {}", e))?;
587
588 // Send PR event (should enter purgatory)
589 let (_, _in_purgatory) = client
590 .send_event_and_note_purgatory(pr_event.clone())
591 .await
592 .map_err(|e| format!("Failed to send PR event: {}", e))?;
593
594 // TODO: Push git data to refs/nostr/<pr-event-id>
595 // This requires git operations similar to OwnerStateDataPushed
596
597 // For now, verify the PR event exists
598 let filter = Filter::new()
599 .kind(Kind::GitPullRequest)
600 .author(client.pr_author_keys().public_key())
601 .id(pr_event.id);
602
603 let events = client
604 .query(filter)
605 .await
606 .map_err(|e| format!("Failed to query PR events: {}", e))?;
607
608 if events.is_empty() {
609 return Err(format!(
610 "PR event not served after git push - purgatory release not implemented. \
611 Event ID: {} should be queryable after git data arrives",
612 pr_event.id
613 ));
614 }
615
616 Ok(())
617 })
618 .await
619 }
620}
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625 use crate::AuditConfig;
626
627 #[tokio::test]
628 #[ignore] // Requires running relay
629 async fn test_grasp01_purgatory_against_relay() {
630 let relay_url = std::env::var("RELAY_URL").expect(
631 "RELAY_URL environment variable must be set. Example: RELAY_URL=ws://localhost:18081",
632 );
633
634 let config = AuditConfig::isolated();
635 let client = AuditClient::new(&relay_url, config)
636 .await
637 .unwrap_or_else(|_| {
638 panic!(
639 "Failed to connect to relay at {}. Ensure relay is running and accessible.",
640 relay_url
641 )
642 });
643
644 let results = PurgatoryTests::run_all(&client).await;
645 results.print_report();
646
647 assert!(
648 results.all_passed(),
649 "Some purgatory tests failed. See report above."
650 );
651 }
652}
diff --git a/grasp-audit/src/specs/mod.rs b/grasp-audit/src/specs/mod.rs
index bf711fa..ceae684 100644
--- a/grasp-audit/src/specs/mod.rs
+++ b/grasp-audit/src/specs/mod.rs
@@ -7,5 +7,5 @@ pub mod grasp01;
7// Re-export all test structs from grasp01 module 7// Re-export all test structs from grasp01 module
8pub use grasp01::{ 8pub use grasp01::{
9 CorsTests, EventAcceptancePolicyTests, GitCloneTests, GitFilterTests, Nip01SmokeTests, 9 CorsTests, EventAcceptancePolicyTests, GitCloneTests, GitFilterTests, Nip01SmokeTests,
10 Nip11DocumentTests, PushAuthorizationTests, RepositoryCreationTests, 10 Nip11DocumentTests, PurgatoryTests, PushAuthorizationTests, RepositoryCreationTests,
11}; 11};