From 519fdc66930280cd1772417dca327ed858333d64 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 20 Nov 2025 22:15:03 +0000 Subject: refactor: isolate each grasp-audit lib test with minimal boilerplate - Add isolated_test! macro pattern to nip34_announcements.rs and nip01_compliance.rs - Each test runs with its own fresh relay instance for complete isolation - Make all individual test functions public in grasp-audit library (nip01_smoke.rs, event_acceptance_policy.rs) - Eliminates 122 lines of boilerplate across integration tests - Tests: 15 GRASP-01 event acceptance policy tests + 6 NIP-01 smoke tests - Ensures tests don't interfere with each other, preventing flakiness --- tests/nip01_compliance.rs | 120 ++++++++++--------------------- tests/nip34_announcements.rs | 168 ++++++++++++------------------------------- 2 files changed, 83 insertions(+), 205 deletions(-) (limited to 'tests') diff --git a/tests/nip01_compliance.rs b/tests/nip01_compliance.rs index 4cb2af4..6fb721a 100644 --- a/tests/nip01_compliance.rs +++ b/tests/nip01_compliance.rs @@ -1,13 +1,13 @@ //! NIP-01 Compliance Integration Tests //! //! Tests ngit-grasp relay's NIP-01 compliance using grasp-audit library. -//! Avoids code duplication by delegating to grasp-audit's test suite. +//! Uses isolated test pattern for complete test independence. //! //! # Test Strategy //! -//! - Uses TestRelay fixture for ngit-grasp relay lifecycle management -//! - Uses grasp-audit's Nip01SmokeTests for actual test logic -//! - Minimal duplication - single source of truth in grasp-audit +//! - Each test runs in complete isolation with its own fresh relay instance +//! - Uses macro to eliminate boilerplate while maintaining test isolation +//! - Calls individual test methods from grasp-audit for minimal duplication //! //! # Running Tests //! @@ -16,7 +16,7 @@ //! cargo test --test nip01_compliance //! //! # Run specific test -//! cargo test --test nip01_compliance test_nip01_smoke +//! cargo test --test nip01_compliance test_websocket_connection //! //! # With output //! cargo test --test nip01_compliance -- --nocapture @@ -27,87 +27,41 @@ mod common; use common::TestRelay; use grasp_audit::*; -/// Test NIP-01 smoke tests against ngit-grasp relay +/// Macro to generate isolated integration tests /// -/// This test runs all NIP-01 smoke tests from grasp-audit against -/// the ngit-grasp relay implementation. -/// -/// Tests cover: -/// - WebSocket connection -/// - Event send/receive -/// - Subscriptions (REQ/CLOSE) -/// - Event validation (signature, ID) -#[tokio::test] -async fn test_nip01_smoke() { - // Start test relay - let relay = TestRelay::start().await; - - // Create audit client in CI mode (isolated testing) - let config = AuditConfig::ci(); - let client = AuditClient::new(relay.url(), config) - .await - .expect("Failed to create audit client"); - - // Run all NIP-01 smoke tests - let results = specs::Nip01SmokeTests::run_all(&client).await; - - // Print detailed report - results.print_report(); - - // Stop relay - relay.stop().await; - - // Assert all tests passed - assert!( - results.all_passed(), - "NIP-01 smoke tests failed: {}/{} passed", - results.passed_count(), - results.total_count() - ); +/// Each test runs with its own fresh relay instance to ensure complete isolation. +/// This eliminates flakiness and ensures tests don't interfere with each other. +macro_rules! isolated_test { + ($test_name:ident) => { + #[tokio::test] + async fn $test_name() { + let relay = TestRelay::start().await; + let config = AuditConfig::ci(); + let client = AuditClient::new(relay.url(), config) + .await + .expect("Failed to create audit client"); + + let result = specs::Nip01SmokeTests::$test_name(&client).await; + + relay.stop().await; + + assert!( + result.passed, + "{} failed: {}", + stringify!($test_name), + result.error.as_deref().unwrap_or("unknown error") + ); + } + }; } -/// Test that relay properly validates events -/// -/// Critical security test - ensures relay validates: -/// - Event signatures -/// - Event IDs -/// - Other NIP-01 requirements -#[tokio::test] -async fn test_relay_validates_events() { - let relay = TestRelay::start().await; - let config = AuditConfig::ci(); - let client = AuditClient::new(relay.url(), config) - .await - .expect("Failed to create audit client"); - - // Run smoke tests which include validation tests - let results = specs::Nip01SmokeTests::run_all(&client).await; - - relay.stop().await; - - // Filter to validation tests - let validation_tests: Vec<_> = results - .results - .iter() - .filter(|t| t.name.contains("reject") || t.name.contains("invalid")) - .collect(); - - // Should have validation tests - assert!( - !validation_tests.is_empty(), - "No validation tests found (these are critical for security)" - ); - - // All validation tests should pass - for test in validation_tests { - assert!( - test.passed, - "Validation test failed: {} - {}\nThis is a security issue!", - test.name, - test.error.as_deref().unwrap_or("unknown error") - ); - } -} +// Generate isolated tests for all NIP-01 smoke tests +isolated_test!(test_websocket_connection); +isolated_test!(test_send_receive_event); +isolated_test!(test_create_subscription); +isolated_test!(test_close_subscription); +isolated_test!(test_reject_invalid_signature); +isolated_test!(test_reject_invalid_event_id); /// Test relay lifecycle management /// diff --git a/tests/nip34_announcements.rs b/tests/nip34_announcements.rs index f1cbd05..09d9c8f 100644 --- a/tests/nip34_announcements.rs +++ b/tests/nip34_announcements.rs @@ -5,9 +5,9 @@ //! //! # Test Strategy //! -//! - Uses TestRelay fixture for ngit-grasp relay lifecycle management -//! - Uses grasp-audit's EventAcceptancePolicyTests for actual test logic -//! - Minimal duplication - single source of truth in grasp-audit +//! - Each test runs in complete isolation with its own fresh relay instance +//! - Uses macro to eliminate boilerplate while maintaining test isolation +//! - Calls individual test methods from grasp-audit for minimal duplication //! //! # Running Tests //! @@ -16,7 +16,7 @@ //! cargo test --test nip34_announcements //! //! # Run specific test -//! cargo test --test nip34_announcements test_grasp01_event_acceptance +//! cargo test --test nip34_announcements test_reject_orphan_kind1 //! //! # With output //! cargo test --test nip34_announcements -- --nocapture @@ -27,124 +27,48 @@ mod common; use common::TestRelay; use grasp_audit::*; -/// Test GRASP-01 event acceptance policy against ngit-grasp relay +/// Macro to generate isolated integration tests /// -/// This test runs all GRASP-01 event acceptance policy tests from grasp-audit -/// against the ngit-grasp relay implementation. -/// -/// Tests cover: -/// - Repository announcement acceptance/rejection -/// - Repository state announcement acceptance -/// - Events tagging accepted repositories -/// - Transitive event acceptance (events tagging accepted events) -/// - Forward reference acceptance (events tagged by accepted events) -/// - Rejection of unrelated events -#[tokio::test] -async fn test_grasp01_event_acceptance() { - // Start test relay - let relay = TestRelay::start().await; - - // Create audit client in CI mode (isolated testing) - let config = AuditConfig::ci(); - let client = AuditClient::new(relay.url(), config) - .await - .expect("Failed to create audit client"); - - // Run all GRASP-01 event acceptance policy tests - let results = specs::EventAcceptancePolicyTests::run_all(&client).await; - - // Print detailed report - results.print_report(); - - // Stop relay - relay.stop().await; - - // Assert all tests passed - assert!( - results.all_passed(), - "GRASP-01 event acceptance tests failed: {}/{} passed", - results.passed_count(), - results.total_count() - ); +/// Each test runs with its own fresh relay instance to ensure complete isolation. +/// This eliminates rate-limiting issues and ensures tests don't interfere with each other. +macro_rules! isolated_test { + ($test_name:ident) => { + #[tokio::test] + async fn $test_name() { + let relay = TestRelay::start().await; + let config = AuditConfig::ci(); + let client = AuditClient::new(relay.url(), config) + .await + .expect("Failed to create audit client"); + + let result = specs::EventAcceptancePolicyTests::$test_name(&client).await; + + relay.stop().await; + + assert!( + result.passed, + "{} failed: {}", + stringify!($test_name), + result.error.as_deref().unwrap_or("unknown error") + ); + } + }; } -/// Test that relay accepts valid repository announcements -/// -/// Demonstrates running individual test categories from the suite -#[tokio::test] -async fn test_accepts_repository_announcements() { - let relay = TestRelay::start().await; - let config = AuditConfig::ci(); - let client = AuditClient::new(relay.url(), config) - .await - .expect("Failed to create audit client"); - - // Run all tests - let results = specs::EventAcceptancePolicyTests::run_all(&client).await; - - relay.stop().await; - - // Filter to only repository announcement tests - let announcement_tests: Vec<_> = results - .results - .iter() - .filter(|t| { - t.spec_ref.contains("repo") || t.name.contains("announcement") || t.name.contains("state") - }) - .collect(); - - // Verify we have announcement tests - assert!( - !announcement_tests.is_empty(), - "No repository announcement tests found" - ); - - // All should pass - for test in announcement_tests { - assert!( - test.passed, - "Repository test failed: {} - {}", - test.name, - test.error.as_deref().unwrap_or("unknown error") - ); - } -} - -/// Test that relay properly validates clone and relays tags -/// -/// This is a critical security requirement for GRASP-01 -#[tokio::test] -async fn test_validates_service_tags() { - let relay = TestRelay::start().await; - let config = AuditConfig::ci(); - let client = AuditClient::new(relay.url(), config) - .await - .expect("Failed to create audit client"); - - let results = specs::EventAcceptancePolicyTests::run_all(&client).await; - - relay.stop().await; - - // Filter to rejection tests (these verify tag validation) - let rejection_tests: Vec<_> = results - .results - .iter() - .filter(|t| t.name.contains("reject")) - .collect(); - - // Should have rejection tests - assert!( - !rejection_tests.is_empty(), - "No rejection tests found (these are critical for security)" - ); - - // All rejection tests should pass - for test in rejection_tests { - assert!( - test.passed, - "Rejection test failed: {} - {}\nThis is a security issue!", - test.name, - test.error.as_deref().unwrap_or("unknown error") - ); - } -} +// Generate isolated tests for all GRASP-01 event acceptance policy tests +isolated_test!(test_accept_valid_repo_announcement); +isolated_test!(test_reject_repo_announcement_missing_clone_tag); +isolated_test!(test_reject_repo_announcement_missing_relays_tag); +isolated_test!(test_accept_valid_repo_state_announcement); +isolated_test!(test_accept_issue_via_a_tag); +isolated_test!(test_accept_comment_via_capital_a_tag); +isolated_test!(test_accept_kind1_via_q_tag); +isolated_test!(test_accept_issue_quoting_issue_via_q); +isolated_test!(test_accept_comment_via_capital_e_tag); +isolated_test!(test_accept_kind1_via_e_tag); +isolated_test!(test_accept_kind1_referenced_in_issue); +isolated_test!(test_accept_comment_referenced_in_comment); +isolated_test!(test_accept_kind1_referenced_in_kind1); +isolated_test!(test_reject_orphan_issue); +isolated_test!(test_reject_orphan_kind1); +isolated_test!(test_reject_comment_quoting_other_repo); \ No newline at end of file -- cgit v1.2.3