diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-05 14:43:30 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-11-05 14:45:02 +0000 |
| commit | b22cb23928ef799b0a5d362003d3084d2ab267b4 (patch) | |
| tree | 26ae31724472e7d5d2b2f61df47e728c25f8d908 /grasp-audit | |
| parent | ebdf1779697e1b3d0fa0e1068af4340a16eabf2b (diff) | |
test: consolidate state announcement tests with GRASP-01 spec format
Refactor repository state announcement testing to better match GRASP-01
specification requirements:
- Consolidate multiple refs testing into accept_valid_repo_state_announcement
- Remove separate accept_state_announcement_multiple_refs test (redundant)
- Update tag format to match spec: use git reference as tag kind
Example: ["refs/heads/main", "<commit-sha>"] instead of ["r", "refs/heads/main", "<commit-sha>"]
- Ensure repo announcement (kind 30617) is sent before state announcement
- Test multiple branch refs (main, develop), tag refs (v1.0.0), and HEAD
- Simplify test validation by removing redundant assertions
This approach provides better spec compliance and reduces test redundancy
while maintaining comprehensive coverage of multiple reference scenarios.
Diffstat (limited to 'grasp-audit')
| -rw-r--r-- | grasp-audit/src/specs/grasp01_nostr_relay.rs | 134 |
1 files changed, 37 insertions, 97 deletions
diff --git a/grasp-audit/src/specs/grasp01_nostr_relay.rs b/grasp-audit/src/specs/grasp01_nostr_relay.rs index 19322cb..247850b 100644 --- a/grasp-audit/src/specs/grasp01_nostr_relay.rs +++ b/grasp-audit/src/specs/grasp01_nostr_relay.rs | |||
| @@ -25,8 +25,6 @@ impl Grasp01NostrRelayTests { | |||
| 25 | 25 | ||
| 26 | // Repository state announcement tests | 26 | // Repository state announcement tests |
| 27 | results.add(Self::test_accept_valid_repo_state_announcement(client).await); | 27 | results.add(Self::test_accept_valid_repo_state_announcement(client).await); |
| 28 | results.add(Self::test_accept_state_announcement_multiple_refs(client).await); | ||
| 29 | results.add(Self::test_accept_state_announcement_no_refs(client).await); | ||
| 30 | 28 | ||
| 31 | // Related event acceptance tests | 29 | // Related event acceptance tests |
| 32 | results.add(Self::test_accept_event_tagging_repo_announcement(client).await); | 30 | results.add(Self::test_accept_event_tagging_repo_announcement(client).await); |
| @@ -277,29 +275,53 @@ impl Grasp01NostrRelayTests { | |||
| 277 | TestResult::new( | 275 | TestResult::new( |
| 278 | "accept_valid_repo_state_announcement", | 276 | "accept_valid_repo_state_announcement", |
| 279 | "GRASP-01:nostr-relay:6-7", | 277 | "GRASP-01:nostr-relay:6-7", |
| 280 | "Accept valid repository state announcements with required tags", | 278 | "Accept valid state announcements after repo announcement accepted", |
| 281 | ) | 279 | ) |
| 282 | .run(|| async { | 280 | .run(|| async { |
| 283 | // Create unique repository identifier | 281 | // First, create a repository announcement (kind 30617) by the same author |
| 284 | let timestamp = Timestamp::now().as_u64(); | 282 | let test_name = format!("test-repo-multi-refs-{}", Timestamp::now().as_u64()); |
| 285 | let repo_id = format!("test-repo-state-{}", timestamp); | 283 | let repo_event = client.create_repo_announcement(&test_name).await |
| 284 | .map_err(|e| format!("Failed to create repository announcement: {}", e))?; | ||
| 285 | |||
| 286 | // Extract repo_id from the repository announcement | ||
| 287 | let repo_id = repo_event.tags.iter() | ||
| 288 | .find(|t| t.kind() == TagKind::d()) | ||
| 289 | .and_then(|t| t.content()) | ||
| 290 | .ok_or("Missing d tag in repository announcement")? | ||
| 291 | .to_string(); | ||
| 286 | 292 | ||
| 287 | // Create kind 30618 repository state announcement with required tags | 293 | // Get maintainer npub |
| 288 | let npub = client.public_key().to_bech32() | 294 | let npub = client.public_key().to_bech32() |
| 289 | .map_err(|e| format!("Failed to convert public key to bech32: {}", e))?; | 295 | .map_err(|e| format!("Failed to convert public key to bech32: {}", e))?; |
| 290 | 296 | ||
| 297 | // Create kind 30618 repository state announcement with multiple refs | ||
| 298 | // Format: ["r", "refs/heads/main", "<commit-id>"] | ||
| 291 | let event = client.event_builder(Kind::Custom(30618), "") | 299 | let event = client.event_builder(Kind::Custom(30618), "") |
| 292 | .tag(Tag::identifier(&repo_id)) | 300 | .tag(Tag::identifier(&repo_id)) |
| 293 | .tag(Tag::custom(TagKind::custom("maintainers"), vec![npub])) | 301 | .tag(Tag::custom(TagKind::custom("refs/heads/main"), vec![ |
| 294 | .tag(Tag::custom(TagKind::custom("r"), vec!["refs/heads/main".to_string()])) | 302 | "abc123def456789012345678901234567890abcd" |
| 303 | ])) | ||
| 304 | .tag(Tag::custom(TagKind::custom("refs/heads/develop"), vec![ | ||
| 305 | "def456789012345678901234567890abcdef123" | ||
| 306 | ])) | ||
| 307 | .tag(Tag::custom(TagKind::custom("refs/tags/v1.0.0"), vec![ | ||
| 308 | "123456789012345678901234567890abcdef456" | ||
| 309 | ])) | ||
| 310 | .tag(Tag::custom(TagKind::custom("HEAD"), vec![ | ||
| 311 | "ref: refs/heads/main" | ||
| 312 | ])) | ||
| 295 | .build(client.keys()) | 313 | .build(client.keys()) |
| 296 | .map_err(|e| format!("Failed to build repository state announcement: {}", e))?; | 314 | .map_err(|e| format!("Failed to build state announcement: {}", e))?; |
| 297 | 315 | ||
| 298 | let event_id = event.id; | 316 | let event_id = event.id; |
| 299 | 317 | ||
| 300 | // Send the event | 318 | // Send the repo announcement event |
| 319 | client.send_event(repo_event.clone()).await | ||
| 320 | .map_err(|e| format!("Failed to send state announcement to relay: {}", e))?; | ||
| 321 | |||
| 322 | // Send the state event | ||
| 301 | client.send_event(event.clone()).await | 323 | client.send_event(event.clone()).await |
| 302 | .map_err(|e| format!("Failed to send repository state announcement to relay: {}", e))?; | 324 | .map_err(|e| format!("Failed to send state announcement to relay: {}", e))?; |
| 303 | 325 | ||
| 304 | // Query back to verify it was accepted and stored | 326 | // Query back to verify it was accepted and stored |
| 305 | let filter = Filter::new() | 327 | let filter = Filter::new() |
| @@ -317,95 +339,13 @@ impl Grasp01NostrRelayTests { | |||
| 317 | event_id, repo_id | 339 | event_id, repo_id |
| 318 | )); | 340 | )); |
| 319 | } | 341 | } |
| 320 | 342 | ||
| 321 | // Verify it's the same event | ||
| 322 | let stored_event = events.iter() | ||
| 323 | .find(|e| e.id == event_id) | ||
| 324 | .ok_or(format!( | ||
| 325 | "Stored event ID doesn't match sent event. Expected: {}, Got {} events", | ||
| 326 | event_id, events.len() | ||
| 327 | ))?; | ||
| 328 | |||
| 329 | // Verify required tags are present | ||
| 330 | let has_d_tag = stored_event.tags.iter() | ||
| 331 | .any(|t| t.kind() == TagKind::d() && t.content() == Some(&repo_id)); | ||
| 332 | |||
| 333 | let has_maintainers_tag = stored_event.tags.iter() | ||
| 334 | .any(|t| t.kind() == TagKind::custom("maintainers")); | ||
| 335 | |||
| 336 | let has_r_tag = stored_event.tags.iter() | ||
| 337 | .any(|t| { | ||
| 338 | t.kind() == TagKind::custom("r") && | ||
| 339 | t.content().map(|c| c.contains("refs/heads/main")).unwrap_or(false) | ||
| 340 | }); | ||
| 341 | |||
| 342 | if !has_d_tag { | ||
| 343 | return Err(format!("Stored event missing d tag with repo identifier ({})", repo_id)); | ||
| 344 | } | ||
| 345 | |||
| 346 | if !has_maintainers_tag { | ||
| 347 | return Err("Stored event missing maintainers tag".to_string()); | ||
| 348 | } | ||
| 349 | |||
| 350 | if !has_r_tag { | ||
| 351 | return Err("Stored event missing r tag with git reference".to_string()); | ||
| 352 | } | ||
| 353 | |||
| 354 | Ok(()) | 343 | Ok(()) |
| 355 | }) | 344 | }) |
| 356 | .await | 345 | .await |
| 357 | } | 346 | } |
| 358 | 347 | ||
| 359 | /// Test: Accept state announcement with multiple refs | 348 | |
| 360 | /// | ||
| 361 | /// Spec: Line 3 of ../grasp/01.md | ||
| 362 | /// Requirement: MUST accept state announcements with multiple refs | ||
| 363 | async fn test_accept_state_announcement_multiple_refs(client: &AuditClient) -> TestResult { | ||
| 364 | TestResult::new( | ||
| 365 | "accept_state_announcement_multiple_refs", | ||
| 366 | "GRASP-01:nostr-relay:3", | ||
| 367 | "Accept state announcements with multiple branch and tag refs", | ||
| 368 | ) | ||
| 369 | .run(|| async { | ||
| 370 | // TODO: Implementation | ||
| 371 | // 1. Send valid kind 30617 repo announcement | ||
| 372 | // 2. Create kind 30618 with multiple refs: | ||
| 373 | // - refs/heads/main: "{commit-sha-1}" | ||
| 374 | // - refs/heads/develop: "{commit-sha-2}" | ||
| 375 | // - refs/tags/v1.0.0: "{commit-sha-3}" | ||
| 376 | // - refs/tags/v2.0.0: "{commit-sha-4}" | ||
| 377 | // - HEAD: "ref: refs/heads/main" | ||
| 378 | // 3. Send and verify acceptance | ||
| 379 | // 4. Query back and verify all refs are stored | ||
| 380 | |||
| 381 | Err("Not implemented yet".to_string()) | ||
| 382 | }) | ||
| 383 | .await | ||
| 384 | } | ||
| 385 | |||
| 386 | /// Test: Accept state announcement with no refs (stop tracking) | ||
| 387 | /// | ||
| 388 | /// Spec: NIP-34 repository state announcements | ||
| 389 | /// Requirement: Support stopping state tracking by sending event with no refs | ||
| 390 | async fn test_accept_state_announcement_no_refs(client: &AuditClient) -> TestResult { | ||
| 391 | TestResult::new( | ||
| 392 | "accept_state_announcement_no_refs", | ||
| 393 | "GRASP-01:nostr-relay:3", | ||
| 394 | "Accept state announcements with no refs (stop tracking)", | ||
| 395 | ) | ||
| 396 | .run(|| async { | ||
| 397 | // TODO: Implementation | ||
| 398 | // 1. Send valid kind 30617 repo announcement | ||
| 399 | // 2. Send kind 30618 with refs (establish state) | ||
| 400 | // 3. Send kind 30618 with ONLY d tag (no refs) | ||
| 401 | // 4. Verify acceptance (allows author to stop tracking) | ||
| 402 | // 5. Query to confirm latest state has no refs | ||
| 403 | |||
| 404 | Err("Not implemented yet".to_string()) | ||
| 405 | }) | ||
| 406 | .await | ||
| 407 | } | ||
| 408 | |||
| 409 | // ========================================================================= | 349 | // ========================================================================= |
| 410 | // Related Event Acceptance Tests | 350 | // Related Event Acceptance Tests |
| 411 | // ========================================================================= | 351 | // ========================================================================= |