From 98c6fa4bfa897ff0b8f9c95ea698d4d065b5e9f3 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 18 Nov 2025 16:50:02 +0000 Subject: docs: switch focus onto grasp implementation --- docs/archive/2025-11-05-grasp01-test-plan.md | 752 +++++++++++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 docs/archive/2025-11-05-grasp01-test-plan.md (limited to 'docs/archive/2025-11-05-grasp01-test-plan.md') diff --git a/docs/archive/2025-11-05-grasp01-test-plan.md b/docs/archive/2025-11-05-grasp01-test-plan.md new file mode 100644 index 0000000..4148f1d --- /dev/null +++ b/docs/archive/2025-11-05-grasp01-test-plan.md @@ -0,0 +1,752 @@ +# GRASP-01 Test Plan + +**Date:** November 5, 2025 +**Status:** Planning Phase +**Scope:** Complete test coverage for GRASP-01 Core Service Requirements + +--- + +## Overview + +This document outlines all tests needed to validate GRASP-01 compliance. Each test maps directly to requirements in `../grasp/01.md`. + +**Test Strategy:** +1. Build tests against ngit-relay reference implementation FIRST +2. Each requirement = one or more test functions +3. All tests reference specific spec line numbers +4. Tests organized by spec sections + +--- + +## Test Organization + +``` +grasp-audit/src/specs/ +├── mod.rs # Export all test modules +├── nip01_smoke.rs # ✅ DONE - Basic relay functionality +├── grasp01_nostr_relay.rs # NEW - Nostr relay requirements +├── grasp01_git_http.rs # NEW - Git Smart HTTP requirements +└── grasp01_cors.rs # NEW - CORS requirements +``` + +--- + +## 1. NIP-01 Smoke Tests (✅ COMPLETE) + +**File:** `grasp-audit/src/specs/nip01_smoke.rs` + +**Status:** Already implemented and working + +**Coverage:** +- ✅ WebSocket connection +- ✅ Send/receive events +- ✅ Subscriptions (REQ/CLOSE) +- ✅ Event validation (signatures, IDs) + +**Note:** These are smoke tests only. We don't comprehensively test NIP-01 since rust-nostr already has 1000+ tests. + +--- + +## 2. GRASP-01 Nostr Relay Tests (🔜 TO DO) + +**File:** `grasp-audit/src/specs/grasp01_nostr_relay.rs` + +**Spec Reference:** Lines 1-14 of `../grasp/01.md` + +### Test Functions to Implement: + +#### 2.1 Repository Announcement Acceptance + +```rust +/// Test: Accept valid repository announcements +/// Spec: Lines 3-5 +/// Requirement: MUST accept repo announcements listing service in clone & relays tags +async fn test_accept_valid_repo_announcement() +``` + +**Test Details:** +- Create kind 30617 event with valid tags +- Include service URL in both `clone` and `relays` tags +- Send to relay +- Verify acceptance (OK response) +- Query back to confirm stored + +```rust +/// Test: Reject repo announcements not listing service (unless GRASP-05) +/// Spec: Line 5 +/// Requirement: MUST reject announcements not listing service +async fn test_reject_repo_announcement_missing_clone_tag() +``` + +**Test Details:** +- Create kind 30617 event WITHOUT service in `clone` tag +- Send to relay +- Verify rejection (error response) +- Confirm not stored in relay + +```rust +/// Test: Reject repo announcements not listing service in relays tag +/// Spec: Line 5 +/// Requirement: MUST reject announcements not listing service in relays +async fn test_reject_repo_announcement_missing_relays_tag() +``` + +**Test Details:** +- Create kind 30617 event WITHOUT service in `relays` tag +- Send to relay +- Verify rejection +- Confirm not stored + +#### 2.2 Repository State Announcement Acceptance + +```rust +/// Test: Accept valid repository state announcements +/// Spec: Line 3 +/// Requirement: MUST accept repo state announcements +async fn test_accept_valid_repo_state_announcement() +``` + +**Test Details:** +- First send valid kind 30617 (repo announcement) +- Then send kind 30618 (state announcement) with matching `d` tag +- Include `refs/heads/main` and `HEAD` tags +- Verify acceptance +- Query back to confirm + +```rust +/// Test: Accept state announcement with multiple refs +/// Spec: Line 3 +/// Requirement: MUST accept state announcements with multiple refs +async fn test_accept_state_announcement_multiple_refs() +``` + +**Test Details:** +- Send kind 30618 with multiple `refs/heads/*` tags +- Include `refs/tags/*` tags +- Verify all refs are stored + +```rust +/// Test: Accept state announcement with no refs (stop tracking) +/// Spec: NIP-34 spec +/// Requirement: Support stopping state tracking +async fn test_accept_state_announcement_no_refs() +``` + +**Test Details:** +- Send kind 30618 with only `d` tag (no refs) +- Verify acceptance (allows author to stop tracking) + +#### 2.3 Related Event Acceptance + +```rust +/// Test: Accept events tagging accepted repo announcements +/// Spec: Lines 7-9 +/// Requirement: MUST accept events that tag accepted repo announcements +async fn test_accept_event_tagging_repo_announcement() +``` + +**Test Details:** +- Create and accept kind 30617 (repo announcement) +- Create kind 1621 (issue) with `a` tag pointing to repo +- Verify issue is accepted + +```rust +/// Test: Accept events tagged by repo announcements +/// Spec: Lines 7-9 +/// Requirement: MUST accept events tagged by accepted announcements +async fn test_accept_event_tagged_by_repo() +``` + +**Test Details:** +- Create event (e.g., kind 1 note) +- Create kind 30617 that tags the note +- Verify note is accepted/retained + +```rust +/// Test: Accept patches (kind 1617) for accepted repos +/// Spec: Lines 8-9 +/// Requirement: MUST accept patches for accepted repos +async fn test_accept_patch_for_repo() +``` + +**Test Details:** +- Create kind 30617 repo announcement +- Create kind 1617 patch with `a` tag to repo +- Verify patch acceptance + +```rust +/// Test: Accept pull requests (kind 1618) for accepted repos +/// Spec: Lines 8-9 +/// Requirement: MUST accept PRs for accepted repos +async fn test_accept_pull_request_for_repo() +``` + +**Test Details:** +- Create kind 30617 repo announcement +- Create kind 1618 PR with `a` tag to repo +- Include required tags: `c` (commit), `clone`, etc. +- Verify PR acceptance + +```rust +/// Test: Accept issues (kind 1621) for accepted repos +/// Spec: Lines 8-9 +/// Requirement: MUST accept issues for accepted repos +async fn test_accept_issue_for_repo() +``` + +**Test Details:** +- Create kind 30617 repo announcement +- Create kind 1621 issue with `a` tag to repo +- Verify issue acceptance + +```rust +/// Test: Accept replies to accepted patches/PRs/issues +/// Spec: Lines 8-9 +/// Requirement: MUST accept replies to accepted events +async fn test_accept_reply_to_issue() +``` + +**Test Details:** +- Create kind 1621 issue +- Create NIP-22 comment (kind 1111) replying to issue +- Verify reply acceptance + +#### 2.4 NIP-11 Relay Information + +```rust +/// Test: Serve NIP-11 document at /.well-known/nostr.json +/// Spec: Line 11 +/// Requirement: MUST serve NIP-11 document +async fn test_nip11_document_exists() +``` + +**Test Details:** +- HTTP GET to `/.well-known/nostr.json` or `https://domain/` with `Accept: application/nostr+json` +- Verify 200 response +- Verify valid JSON + +```rust +/// Test: NIP-11 includes supported_grasps field +/// Spec: Line 12 +/// Requirement: MUST list supported GRASPs as string array +async fn test_nip11_supported_grasps_field() +``` + +**Test Details:** +- Fetch NIP-11 document +- Verify `supported_grasps` field exists +- Verify it's a string array +- Verify includes "GRASP-01" +- Format check: each entry matches `GRASP-XX` pattern + +```rust +/// Test: NIP-11 includes repo_acceptance_criteria field +/// Spec: Line 13 +/// Requirement: MUST list repository acceptance criteria +async fn test_nip11_repo_acceptance_criteria_field() +``` + +**Test Details:** +- Fetch NIP-11 document +- Verify `repo_acceptance_criteria` field exists +- Verify it's a human-readable string +- Verify non-empty + +```rust +/// Test: NIP-11 curation field handling +/// Spec: Line 14 +/// Requirement: MUST include curation if curated, omit otherwise +async fn test_nip11_curation_field() +``` + +**Test Details:** +- Fetch NIP-11 document +- If `curation` field exists, verify it's a non-empty string +- Document behavior (present or absent is both valid) + +#### 2.5 Event Rejection Policies + +```rust +/// Test: MAY reject based on custom criteria +/// Spec: Line 6 +/// Requirement: Document that custom rejection is allowed +async fn test_custom_rejection_allowed() +``` + +**Test Details:** +- This is a policy test, not a functional test +- Verify relay can reject for reasons like: + - Pre-payment required + - Quota exceeded + - WoT filtering + - Whitelist + - SPAM prevention +- Document in test that this is implementation-specific + +```rust +/// Test: MAY reject/delete for SPAM prevention +/// Spec: Line 10 +/// Requirement: Generic SPAM prevention allowed +async fn test_spam_prevention_allowed() +``` + +**Test Details:** +- Document that relay may reject/delete for SPAM +- This is permissive, not mandatory +- Test should document the policy, not enforce specific behavior + +--- + +## 3. GRASP-01 Git Smart HTTP Tests (🔜 TO DO) + +**File:** `grasp-audit/src/specs/grasp01_git_http.rs` + +**Spec Reference:** Lines 15-31 of `../grasp/01.md` + +### Test Functions to Implement: + +#### 3.1 Repository Serving + +```rust +/// Test: Serve git repo at //.git +/// Spec: Line 17 +/// Requirement: MUST serve git repo at correct path +async fn test_serve_git_repo_at_correct_path() +``` + +**Test Details:** +- Create kind 30617 announcement with `d` tag = "test-repo" +- Push git data to repository +- HTTP GET to `//test-repo.git/info/refs?service=git-upload-pack` +- Verify 200 response +- Verify git smart HTTP response format + +```rust +/// Test: Unauthenticated git-upload-pack (clone/fetch) +/// Spec: Line 17 +/// Requirement: MUST allow unauthenticated clone/fetch +async fn test_unauthenticated_clone() +``` + +**Test Details:** +- Create and push repository +- Perform git clone without authentication +- Verify clone succeeds +- Verify repository contents match + +```rust +/// Test: Repository only served for accepted announcements +/// Spec: Line 17 +/// Requirement: Only serve repos with accepted announcements +async fn test_no_git_repo_without_announcement() +``` + +**Test Details:** +- Try to access `//nonexistent.git/info/refs` +- Verify 404 response +- Verify no git data served + +#### 3.2 Push Authorization + +```rust +/// Test: Accept push matching latest state announcement +/// Spec: Line 19 +/// Requirement: MUST accept pushes matching state announcement +async fn test_accept_push_matching_state() +``` + +**Test Details:** +- Create kind 30617 repo announcement +- Create kind 30618 state with `refs/heads/main` = commit A +- Attempt git push updating main to commit B (child of A) +- Verify push accepted +- Verify repository updated + +```rust +/// Test: Reject push not matching state announcement +/// Spec: Line 19 +/// Requirement: Implicit - only accept matching pushes +async fn test_reject_push_not_matching_state() +``` + +**Test Details:** +- Create kind 30618 state with `refs/heads/main` = commit A +- Attempt git push updating main to commit X (unrelated) +- Verify push rejected +- Verify repository unchanged + +```rust +/// Test: Respect recursive maintainer set +/// Spec: Line 19 +/// Requirement: MUST respect recursive maintainer set +async fn test_push_authorization_maintainer_set() +``` + +**Test Details:** +- Create repo announcement by user A +- Add user B to `maintainers` tag +- User B creates state announcement +- User B pushes matching state +- Verify push accepted +- Test recursion: B lists C as maintainer, C can push + +```rust +/// Test: Reject push from non-maintainer +/// Spec: Line 19 (implicit) +/// Requirement: Only maintainers can push +async fn test_reject_push_from_non_maintainer() +``` + +**Test Details:** +- Create repo announcement by user A +- User B (not in maintainers) creates state announcement +- User B attempts push +- Verify push rejected + +#### 3.3 HEAD Management + +```rust +/// Test: Set HEAD per state announcement +/// Spec: Line 21 +/// Requirement: MUST set HEAD when git data received +async fn test_set_head_from_state_announcement() +``` + +**Test Details:** +- Create kind 30618 with `HEAD = ref: refs/heads/develop` +- Push git data for develop branch +- Clone repository +- Verify HEAD points to develop (not main) + +```rust +/// Test: Update HEAD when state changes +/// Spec: Line 21 +/// Requirement: Update HEAD as soon as git data available +async fn test_update_head_when_state_changes() +``` + +**Test Details:** +- Initial state: HEAD = main +- Push new state: HEAD = develop +- Push git data for develop +- Verify HEAD updates to develop + +#### 3.4 Pull Request Refs + +```rust +/// Test: Accept push to refs/nostr/ +/// Spec: Line 23 +/// Requirement: MUST accept pushes to PR refs +async fn test_accept_push_to_pr_ref() +``` + +**Test Details:** +- Create kind 1618 PR event +- Push to `refs/nostr/` +- Verify push accepted +- Verify ref exists in repository + +```rust +/// Test: Reject PR ref if event has different tip +/// Spec: Line 23 +/// Requirement: SHOULD reject if tip mismatch +async fn test_reject_pr_ref_tip_mismatch() +``` + +**Test Details:** +- Create kind 1618 PR with `c` tag = commit A +- Push to `refs/nostr/` with commit B +- Verify push rejected (or document if accepted) + +```rust +/// Test: Delete PR ref if no event within 20 minutes +/// Spec: Line 23 +/// Requirement: SHOULD delete orphaned PR refs +async fn test_delete_orphaned_pr_ref() +``` + +**Test Details:** +- Push to `refs/nostr/` +- Wait 20+ minutes without sending kind 1618/1619 event +- Check if ref is deleted +- Note: This is SHOULD, not MUST - document behavior + +```rust +/// Test: Keep PR ref if event exists +/// Spec: Line 23 (implicit) +/// Requirement: Keep ref if valid PR/update event exists +async fn test_keep_pr_ref_with_event() +``` + +**Test Details:** +- Push to `refs/nostr/` +- Send kind 1618 PR event with matching `c` tag +- Wait 20+ minutes +- Verify ref still exists + +#### 3.5 Git Protocol Features + +```rust +/// Test: Advertise allow-reachable-sha1-in-want +/// Spec: Line 25 +/// Requirement: MUST advertise and serve capability +async fn test_advertise_reachable_sha1_in_want() +``` + +**Test Details:** +- GET `/repo.git/info/refs?service=git-upload-pack` +- Parse git protocol response +- Verify `allow-reachable-sha1-in-want` in capabilities + +```rust +/// Test: Advertise allow-tip-sha1-in-want +/// Spec: Line 25 +/// Requirement: MUST advertise and serve capability +async fn test_advertise_tip_sha1_in_want() +``` + +**Test Details:** +- GET `/repo.git/info/refs?service=git-upload-pack` +- Parse git protocol response +- Verify `allow-tip-sha1-in-want` in capabilities + +```rust +/// Test: Serve available OIDs by SHA1 +/// Spec: Line 25 +/// Requirement: MUST serve available OIDs +async fn test_serve_oids_by_sha1() +``` + +**Test Details:** +- Push repository with known commits +- Perform git fetch with specific SHA1 want +- Verify server provides the object + +#### 3.6 Web Interface + +```rust +/// Test: Serve webpage at repo endpoint +/// Spec: Line 27 +/// Requirement: SHOULD serve webpage with links +async fn test_serve_webpage_at_repo_endpoint() +``` + +**Test Details:** +- HTTP GET to `//.git` with `Accept: text/html` +- Verify HTML response (not git protocol) +- Verify links to git nostr clients (optional check) + +```rust +/// Test: Serve 404 for non-existent repos +/// Spec: Line 27 +/// Requirement: SHOULD serve 404 for missing repos +async fn test_serve_404_for_missing_repo() +``` + +**Test Details:** +- HTTP GET to `//nonexistent.git` with `Accept: text/html` +- Verify 404 response +- Verify helpful error message + +--- + +## 4. GRASP-01 CORS Tests (🔜 TO DO) + +**File:** `grasp-audit/src/specs/grasp01_cors.rs` + +**Spec Reference:** Lines 32-40 of `../grasp/01.md` + +### Test Functions to Implement: + +```rust +/// Test: Access-Control-Allow-Origin on all responses +/// Spec: Line 35 +/// Requirement: MUST set ACAO: * on ALL responses +async fn test_cors_allow_origin_on_all_responses() +``` + +**Test Details:** +- Test multiple endpoints: + - WebSocket upgrade (Nostr relay) + - Git HTTP endpoints (info/refs, upload-pack, receive-pack) + - NIP-11 endpoint + - Web interface +- Verify ALL include `Access-Control-Allow-Origin: *` + +```rust +/// Test: Access-Control-Allow-Methods on all responses +/// Spec: Line 36 +/// Requirement: MUST set ACAM: GET, POST on ALL responses +async fn test_cors_allow_methods_on_all_responses() +``` + +**Test Details:** +- Test same endpoints as above +- Verify ALL include `Access-Control-Allow-Methods: GET, POST` + +```rust +/// Test: Access-Control-Allow-Headers on all responses +/// Spec: Line 37 +/// Requirement: MUST set ACAH: Content-Type on ALL responses +async fn test_cors_allow_headers_on_all_responses() +``` + +**Test Details:** +- Test same endpoints as above +- Verify ALL include `Access-Control-Allow-Headers: Content-Type` + +```rust +/// Test: OPTIONS requests return 204 No Content +/// Spec: Line 38 +/// Requirement: MUST respond to OPTIONS with 204 +async fn test_cors_options_request() +``` + +**Test Details:** +- Send OPTIONS request to various endpoints +- Verify 204 No Content response +- Verify CORS headers present on OPTIONS response + +```rust +/// Test: CORS headers on error responses +/// Spec: Line 35 (ALL responses) +/// Requirement: CORS headers even on errors +async fn test_cors_headers_on_error_responses() +``` + +**Test Details:** +- Trigger various error conditions: + - 404 not found + - 403 forbidden (unauthorized push) + - 400 bad request +- Verify CORS headers present on all error responses + +```rust +/// Test: Preflight request handling +/// Spec: Lines 35-38 +/// Requirement: Full preflight support for web clients +async fn test_cors_preflight_request() +``` + +**Test Details:** +- Send OPTIONS with Origin and Access-Control-Request-Method headers +- Verify proper preflight response +- Verify subsequent actual request succeeds + +--- + +## Implementation Priority + +### Phase 1: Core Nostr Relay Tests (Complete these first) +1. ✅ NIP-01 smoke tests (DONE) +2. Repository announcement acceptance/rejection +3. Repository state announcement acceptance +4. NIP-11 relay information document +5. Related event acceptance (issues, patches, PRs) + +### Phase 2: Git Smart HTTP Tests +1. Repository serving at correct paths +2. Unauthenticated clone/fetch +3. Push authorization and maintainer sets +4. HEAD management +5. Git protocol features (SHA1 capabilities) + +### Phase 3: Advanced Git Features +1. Pull request refs (refs/nostr/) +2. PR ref lifecycle (creation, validation, deletion) +3. Web interface (optional) + +### Phase 4: CORS Tests +1. CORS headers on all endpoints +2. OPTIONS request handling +3. Preflight requests +4. Error response CORS + +--- + +## Test Execution Plan + +### Against ngit-relay Reference Implementation + +```bash +# 1. Start ngit-relay +cd ../ngit-relay +docker-compose up -d + +# 2. Run tests +cd ../ngit-grasp/grasp-audit +cargo test --lib # Unit tests + +# Run integration tests by category +cargo test --test grasp01_nostr_relay +cargo test --test grasp01_git_http +cargo test --test grasp01_cors + +# 3. Run full audit +cargo run -- --url ws://localhost:8081 +``` + +### Test Data Requirements + +For comprehensive testing, we need: +- Multiple test keypairs (maintainers, contributors, non-maintainers) +- Sample git repositories with known commit history +- Valid NIP-34 event templates +- Test data for edge cases + +--- + +## Success Criteria + +- [ ] All GRASP-01 requirements have corresponding tests +- [ ] All tests reference specific spec line numbers +- [ ] All tests pass against ngit-relay reference implementation +- [ ] Tests are organized logically by spec sections +- [ ] Clear test output shows what requirement is being tested +- [ ] Tests can be run individually or as full suite +- [ ] Documentation explains what each test validates + +--- + +## Notes + +### Spec Line Number References + +When implementing tests, use this format: + +```rust +/// Test: +/// Spec: Lines X-Y of ../grasp/01.md +/// Requirement: +async fn test_name() { + // Implementation +} +``` + +### Test Naming Convention + +- `test_accept_*` - Tests that verify acceptance of valid input +- `test_reject_*` - Tests that verify rejection of invalid input +- `test_serve_*` - Tests that verify correct serving of data +- `test_cors_*` - Tests for CORS functionality +- `test_nip11_*` - Tests for NIP-11 relay information + +### Edge Cases to Consider + +1. **Concurrent updates** - Multiple maintainers pushing simultaneously +2. **Large repositories** - Performance with large git data +3. **Invalid git data** - Corrupted pack files, invalid refs +4. **Event ordering** - State announcement before repo announcement +5. **Deleted events** - What happens when announcement is deleted? +6. **Network failures** - Partial push, interrupted clone +7. **Recursive maintainers** - Deep maintainer chains, circular references + +--- + +**Next Steps:** +1. Implement Phase 1 tests (Nostr relay) +2. Run against ngit-relay to validate +3. Fix any failing tests +4. Move to Phase 2 (Git HTTP) +5. Iterate until all tests pass + -- cgit v1.2.3