diff options
Diffstat (limited to 'docs/archive/2025-11-05-grasp01-test-plan.md')
| -rw-r--r-- | docs/archive/2025-11-05-grasp01-test-plan.md | 752 |
1 files changed, 752 insertions, 0 deletions
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 @@ | |||
| 1 | # GRASP-01 Test Plan | ||
| 2 | |||
| 3 | **Date:** November 5, 2025 | ||
| 4 | **Status:** Planning Phase | ||
| 5 | **Scope:** Complete test coverage for GRASP-01 Core Service Requirements | ||
| 6 | |||
| 7 | --- | ||
| 8 | |||
| 9 | ## Overview | ||
| 10 | |||
| 11 | This document outlines all tests needed to validate GRASP-01 compliance. Each test maps directly to requirements in `../grasp/01.md`. | ||
| 12 | |||
| 13 | **Test Strategy:** | ||
| 14 | 1. Build tests against ngit-relay reference implementation FIRST | ||
| 15 | 2. Each requirement = one or more test functions | ||
| 16 | 3. All tests reference specific spec line numbers | ||
| 17 | 4. Tests organized by spec sections | ||
| 18 | |||
| 19 | --- | ||
| 20 | |||
| 21 | ## Test Organization | ||
| 22 | |||
| 23 | ``` | ||
| 24 | grasp-audit/src/specs/ | ||
| 25 | ├── mod.rs # Export all test modules | ||
| 26 | ├── nip01_smoke.rs # ✅ DONE - Basic relay functionality | ||
| 27 | ├── grasp01_nostr_relay.rs # NEW - Nostr relay requirements | ||
| 28 | ├── grasp01_git_http.rs # NEW - Git Smart HTTP requirements | ||
| 29 | └── grasp01_cors.rs # NEW - CORS requirements | ||
| 30 | ``` | ||
| 31 | |||
| 32 | --- | ||
| 33 | |||
| 34 | ## 1. NIP-01 Smoke Tests (✅ COMPLETE) | ||
| 35 | |||
| 36 | **File:** `grasp-audit/src/specs/nip01_smoke.rs` | ||
| 37 | |||
| 38 | **Status:** Already implemented and working | ||
| 39 | |||
| 40 | **Coverage:** | ||
| 41 | - ✅ WebSocket connection | ||
| 42 | - ✅ Send/receive events | ||
| 43 | - ✅ Subscriptions (REQ/CLOSE) | ||
| 44 | - ✅ Event validation (signatures, IDs) | ||
| 45 | |||
| 46 | **Note:** These are smoke tests only. We don't comprehensively test NIP-01 since rust-nostr already has 1000+ tests. | ||
| 47 | |||
| 48 | --- | ||
| 49 | |||
| 50 | ## 2. GRASP-01 Nostr Relay Tests (🔜 TO DO) | ||
| 51 | |||
| 52 | **File:** `grasp-audit/src/specs/grasp01_nostr_relay.rs` | ||
| 53 | |||
| 54 | **Spec Reference:** Lines 1-14 of `../grasp/01.md` | ||
| 55 | |||
| 56 | ### Test Functions to Implement: | ||
| 57 | |||
| 58 | #### 2.1 Repository Announcement Acceptance | ||
| 59 | |||
| 60 | ```rust | ||
| 61 | /// Test: Accept valid repository announcements | ||
| 62 | /// Spec: Lines 3-5 | ||
| 63 | /// Requirement: MUST accept repo announcements listing service in clone & relays tags | ||
| 64 | async fn test_accept_valid_repo_announcement() | ||
| 65 | ``` | ||
| 66 | |||
| 67 | **Test Details:** | ||
| 68 | - Create kind 30617 event with valid tags | ||
| 69 | - Include service URL in both `clone` and `relays` tags | ||
| 70 | - Send to relay | ||
| 71 | - Verify acceptance (OK response) | ||
| 72 | - Query back to confirm stored | ||
| 73 | |||
| 74 | ```rust | ||
| 75 | /// Test: Reject repo announcements not listing service (unless GRASP-05) | ||
| 76 | /// Spec: Line 5 | ||
| 77 | /// Requirement: MUST reject announcements not listing service | ||
| 78 | async fn test_reject_repo_announcement_missing_clone_tag() | ||
| 79 | ``` | ||
| 80 | |||
| 81 | **Test Details:** | ||
| 82 | - Create kind 30617 event WITHOUT service in `clone` tag | ||
| 83 | - Send to relay | ||
| 84 | - Verify rejection (error response) | ||
| 85 | - Confirm not stored in relay | ||
| 86 | |||
| 87 | ```rust | ||
| 88 | /// Test: Reject repo announcements not listing service in relays tag | ||
| 89 | /// Spec: Line 5 | ||
| 90 | /// Requirement: MUST reject announcements not listing service in relays | ||
| 91 | async fn test_reject_repo_announcement_missing_relays_tag() | ||
| 92 | ``` | ||
| 93 | |||
| 94 | **Test Details:** | ||
| 95 | - Create kind 30617 event WITHOUT service in `relays` tag | ||
| 96 | - Send to relay | ||
| 97 | - Verify rejection | ||
| 98 | - Confirm not stored | ||
| 99 | |||
| 100 | #### 2.2 Repository State Announcement Acceptance | ||
| 101 | |||
| 102 | ```rust | ||
| 103 | /// Test: Accept valid repository state announcements | ||
| 104 | /// Spec: Line 3 | ||
| 105 | /// Requirement: MUST accept repo state announcements | ||
| 106 | async fn test_accept_valid_repo_state_announcement() | ||
| 107 | ``` | ||
| 108 | |||
| 109 | **Test Details:** | ||
| 110 | - First send valid kind 30617 (repo announcement) | ||
| 111 | - Then send kind 30618 (state announcement) with matching `d` tag | ||
| 112 | - Include `refs/heads/main` and `HEAD` tags | ||
| 113 | - Verify acceptance | ||
| 114 | - Query back to confirm | ||
| 115 | |||
| 116 | ```rust | ||
| 117 | /// Test: Accept state announcement with multiple refs | ||
| 118 | /// Spec: Line 3 | ||
| 119 | /// Requirement: MUST accept state announcements with multiple refs | ||
| 120 | async fn test_accept_state_announcement_multiple_refs() | ||
| 121 | ``` | ||
| 122 | |||
| 123 | **Test Details:** | ||
| 124 | - Send kind 30618 with multiple `refs/heads/*` tags | ||
| 125 | - Include `refs/tags/*` tags | ||
| 126 | - Verify all refs are stored | ||
| 127 | |||
| 128 | ```rust | ||
| 129 | /// Test: Accept state announcement with no refs (stop tracking) | ||
| 130 | /// Spec: NIP-34 spec | ||
| 131 | /// Requirement: Support stopping state tracking | ||
| 132 | async fn test_accept_state_announcement_no_refs() | ||
| 133 | ``` | ||
| 134 | |||
| 135 | **Test Details:** | ||
| 136 | - Send kind 30618 with only `d` tag (no refs) | ||
| 137 | - Verify acceptance (allows author to stop tracking) | ||
| 138 | |||
| 139 | #### 2.3 Related Event Acceptance | ||
| 140 | |||
| 141 | ```rust | ||
| 142 | /// Test: Accept events tagging accepted repo announcements | ||
| 143 | /// Spec: Lines 7-9 | ||
| 144 | /// Requirement: MUST accept events that tag accepted repo announcements | ||
| 145 | async fn test_accept_event_tagging_repo_announcement() | ||
| 146 | ``` | ||
| 147 | |||
| 148 | **Test Details:** | ||
| 149 | - Create and accept kind 30617 (repo announcement) | ||
| 150 | - Create kind 1621 (issue) with `a` tag pointing to repo | ||
| 151 | - Verify issue is accepted | ||
| 152 | |||
| 153 | ```rust | ||
| 154 | /// Test: Accept events tagged by repo announcements | ||
| 155 | /// Spec: Lines 7-9 | ||
| 156 | /// Requirement: MUST accept events tagged by accepted announcements | ||
| 157 | async fn test_accept_event_tagged_by_repo() | ||
| 158 | ``` | ||
| 159 | |||
| 160 | **Test Details:** | ||
| 161 | - Create event (e.g., kind 1 note) | ||
| 162 | - Create kind 30617 that tags the note | ||
| 163 | - Verify note is accepted/retained | ||
| 164 | |||
| 165 | ```rust | ||
| 166 | /// Test: Accept patches (kind 1617) for accepted repos | ||
| 167 | /// Spec: Lines 8-9 | ||
| 168 | /// Requirement: MUST accept patches for accepted repos | ||
| 169 | async fn test_accept_patch_for_repo() | ||
| 170 | ``` | ||
| 171 | |||
| 172 | **Test Details:** | ||
| 173 | - Create kind 30617 repo announcement | ||
| 174 | - Create kind 1617 patch with `a` tag to repo | ||
| 175 | - Verify patch acceptance | ||
| 176 | |||
| 177 | ```rust | ||
| 178 | /// Test: Accept pull requests (kind 1618) for accepted repos | ||
| 179 | /// Spec: Lines 8-9 | ||
| 180 | /// Requirement: MUST accept PRs for accepted repos | ||
| 181 | async fn test_accept_pull_request_for_repo() | ||
| 182 | ``` | ||
| 183 | |||
| 184 | **Test Details:** | ||
| 185 | - Create kind 30617 repo announcement | ||
| 186 | - Create kind 1618 PR with `a` tag to repo | ||
| 187 | - Include required tags: `c` (commit), `clone`, etc. | ||
| 188 | - Verify PR acceptance | ||
| 189 | |||
| 190 | ```rust | ||
| 191 | /// Test: Accept issues (kind 1621) for accepted repos | ||
| 192 | /// Spec: Lines 8-9 | ||
| 193 | /// Requirement: MUST accept issues for accepted repos | ||
| 194 | async fn test_accept_issue_for_repo() | ||
| 195 | ``` | ||
| 196 | |||
| 197 | **Test Details:** | ||
| 198 | - Create kind 30617 repo announcement | ||
| 199 | - Create kind 1621 issue with `a` tag to repo | ||
| 200 | - Verify issue acceptance | ||
| 201 | |||
| 202 | ```rust | ||
| 203 | /// Test: Accept replies to accepted patches/PRs/issues | ||
| 204 | /// Spec: Lines 8-9 | ||
| 205 | /// Requirement: MUST accept replies to accepted events | ||
| 206 | async fn test_accept_reply_to_issue() | ||
| 207 | ``` | ||
| 208 | |||
| 209 | **Test Details:** | ||
| 210 | - Create kind 1621 issue | ||
| 211 | - Create NIP-22 comment (kind 1111) replying to issue | ||
| 212 | - Verify reply acceptance | ||
| 213 | |||
| 214 | #### 2.4 NIP-11 Relay Information | ||
| 215 | |||
| 216 | ```rust | ||
| 217 | /// Test: Serve NIP-11 document at /.well-known/nostr.json | ||
| 218 | /// Spec: Line 11 | ||
| 219 | /// Requirement: MUST serve NIP-11 document | ||
| 220 | async fn test_nip11_document_exists() | ||
| 221 | ``` | ||
| 222 | |||
| 223 | **Test Details:** | ||
| 224 | - HTTP GET to `/.well-known/nostr.json` or `https://domain/` with `Accept: application/nostr+json` | ||
| 225 | - Verify 200 response | ||
| 226 | - Verify valid JSON | ||
| 227 | |||
| 228 | ```rust | ||
| 229 | /// Test: NIP-11 includes supported_grasps field | ||
| 230 | /// Spec: Line 12 | ||
| 231 | /// Requirement: MUST list supported GRASPs as string array | ||
| 232 | async fn test_nip11_supported_grasps_field() | ||
| 233 | ``` | ||
| 234 | |||
| 235 | **Test Details:** | ||
| 236 | - Fetch NIP-11 document | ||
| 237 | - Verify `supported_grasps` field exists | ||
| 238 | - Verify it's a string array | ||
| 239 | - Verify includes "GRASP-01" | ||
| 240 | - Format check: each entry matches `GRASP-XX` pattern | ||
| 241 | |||
| 242 | ```rust | ||
| 243 | /// Test: NIP-11 includes repo_acceptance_criteria field | ||
| 244 | /// Spec: Line 13 | ||
| 245 | /// Requirement: MUST list repository acceptance criteria | ||
| 246 | async fn test_nip11_repo_acceptance_criteria_field() | ||
| 247 | ``` | ||
| 248 | |||
| 249 | **Test Details:** | ||
| 250 | - Fetch NIP-11 document | ||
| 251 | - Verify `repo_acceptance_criteria` field exists | ||
| 252 | - Verify it's a human-readable string | ||
| 253 | - Verify non-empty | ||
| 254 | |||
| 255 | ```rust | ||
| 256 | /// Test: NIP-11 curation field handling | ||
| 257 | /// Spec: Line 14 | ||
| 258 | /// Requirement: MUST include curation if curated, omit otherwise | ||
| 259 | async fn test_nip11_curation_field() | ||
| 260 | ``` | ||
| 261 | |||
| 262 | **Test Details:** | ||
| 263 | - Fetch NIP-11 document | ||
| 264 | - If `curation` field exists, verify it's a non-empty string | ||
| 265 | - Document behavior (present or absent is both valid) | ||
| 266 | |||
| 267 | #### 2.5 Event Rejection Policies | ||
| 268 | |||
| 269 | ```rust | ||
| 270 | /// Test: MAY reject based on custom criteria | ||
| 271 | /// Spec: Line 6 | ||
| 272 | /// Requirement: Document that custom rejection is allowed | ||
| 273 | async fn test_custom_rejection_allowed() | ||
| 274 | ``` | ||
| 275 | |||
| 276 | **Test Details:** | ||
| 277 | - This is a policy test, not a functional test | ||
| 278 | - Verify relay can reject for reasons like: | ||
| 279 | - Pre-payment required | ||
| 280 | - Quota exceeded | ||
| 281 | - WoT filtering | ||
| 282 | - Whitelist | ||
| 283 | - SPAM prevention | ||
| 284 | - Document in test that this is implementation-specific | ||
| 285 | |||
| 286 | ```rust | ||
| 287 | /// Test: MAY reject/delete for SPAM prevention | ||
| 288 | /// Spec: Line 10 | ||
| 289 | /// Requirement: Generic SPAM prevention allowed | ||
| 290 | async fn test_spam_prevention_allowed() | ||
| 291 | ``` | ||
| 292 | |||
| 293 | **Test Details:** | ||
| 294 | - Document that relay may reject/delete for SPAM | ||
| 295 | - This is permissive, not mandatory | ||
| 296 | - Test should document the policy, not enforce specific behavior | ||
| 297 | |||
| 298 | --- | ||
| 299 | |||
| 300 | ## 3. GRASP-01 Git Smart HTTP Tests (🔜 TO DO) | ||
| 301 | |||
| 302 | **File:** `grasp-audit/src/specs/grasp01_git_http.rs` | ||
| 303 | |||
| 304 | **Spec Reference:** Lines 15-31 of `../grasp/01.md` | ||
| 305 | |||
| 306 | ### Test Functions to Implement: | ||
| 307 | |||
| 308 | #### 3.1 Repository Serving | ||
| 309 | |||
| 310 | ```rust | ||
| 311 | /// Test: Serve git repo at /<npub>/<identifier>.git | ||
| 312 | /// Spec: Line 17 | ||
| 313 | /// Requirement: MUST serve git repo at correct path | ||
| 314 | async fn test_serve_git_repo_at_correct_path() | ||
| 315 | ``` | ||
| 316 | |||
| 317 | **Test Details:** | ||
| 318 | - Create kind 30617 announcement with `d` tag = "test-repo" | ||
| 319 | - Push git data to repository | ||
| 320 | - HTTP GET to `/<npub>/test-repo.git/info/refs?service=git-upload-pack` | ||
| 321 | - Verify 200 response | ||
| 322 | - Verify git smart HTTP response format | ||
| 323 | |||
| 324 | ```rust | ||
| 325 | /// Test: Unauthenticated git-upload-pack (clone/fetch) | ||
| 326 | /// Spec: Line 17 | ||
| 327 | /// Requirement: MUST allow unauthenticated clone/fetch | ||
| 328 | async fn test_unauthenticated_clone() | ||
| 329 | ``` | ||
| 330 | |||
| 331 | **Test Details:** | ||
| 332 | - Create and push repository | ||
| 333 | - Perform git clone without authentication | ||
| 334 | - Verify clone succeeds | ||
| 335 | - Verify repository contents match | ||
| 336 | |||
| 337 | ```rust | ||
| 338 | /// Test: Repository only served for accepted announcements | ||
| 339 | /// Spec: Line 17 | ||
| 340 | /// Requirement: Only serve repos with accepted announcements | ||
| 341 | async fn test_no_git_repo_without_announcement() | ||
| 342 | ``` | ||
| 343 | |||
| 344 | **Test Details:** | ||
| 345 | - Try to access `/<npub>/nonexistent.git/info/refs` | ||
| 346 | - Verify 404 response | ||
| 347 | - Verify no git data served | ||
| 348 | |||
| 349 | #### 3.2 Push Authorization | ||
| 350 | |||
| 351 | ```rust | ||
| 352 | /// Test: Accept push matching latest state announcement | ||
| 353 | /// Spec: Line 19 | ||
| 354 | /// Requirement: MUST accept pushes matching state announcement | ||
| 355 | async fn test_accept_push_matching_state() | ||
| 356 | ``` | ||
| 357 | |||
| 358 | **Test Details:** | ||
| 359 | - Create kind 30617 repo announcement | ||
| 360 | - Create kind 30618 state with `refs/heads/main` = commit A | ||
| 361 | - Attempt git push updating main to commit B (child of A) | ||
| 362 | - Verify push accepted | ||
| 363 | - Verify repository updated | ||
| 364 | |||
| 365 | ```rust | ||
| 366 | /// Test: Reject push not matching state announcement | ||
| 367 | /// Spec: Line 19 | ||
| 368 | /// Requirement: Implicit - only accept matching pushes | ||
| 369 | async fn test_reject_push_not_matching_state() | ||
| 370 | ``` | ||
| 371 | |||
| 372 | **Test Details:** | ||
| 373 | - Create kind 30618 state with `refs/heads/main` = commit A | ||
| 374 | - Attempt git push updating main to commit X (unrelated) | ||
| 375 | - Verify push rejected | ||
| 376 | - Verify repository unchanged | ||
| 377 | |||
| 378 | ```rust | ||
| 379 | /// Test: Respect recursive maintainer set | ||
| 380 | /// Spec: Line 19 | ||
| 381 | /// Requirement: MUST respect recursive maintainer set | ||
| 382 | async fn test_push_authorization_maintainer_set() | ||
| 383 | ``` | ||
| 384 | |||
| 385 | **Test Details:** | ||
| 386 | - Create repo announcement by user A | ||
| 387 | - Add user B to `maintainers` tag | ||
| 388 | - User B creates state announcement | ||
| 389 | - User B pushes matching state | ||
| 390 | - Verify push accepted | ||
| 391 | - Test recursion: B lists C as maintainer, C can push | ||
| 392 | |||
| 393 | ```rust | ||
| 394 | /// Test: Reject push from non-maintainer | ||
| 395 | /// Spec: Line 19 (implicit) | ||
| 396 | /// Requirement: Only maintainers can push | ||
| 397 | async fn test_reject_push_from_non_maintainer() | ||
| 398 | ``` | ||
| 399 | |||
| 400 | **Test Details:** | ||
| 401 | - Create repo announcement by user A | ||
| 402 | - User B (not in maintainers) creates state announcement | ||
| 403 | - User B attempts push | ||
| 404 | - Verify push rejected | ||
| 405 | |||
| 406 | #### 3.3 HEAD Management | ||
| 407 | |||
| 408 | ```rust | ||
| 409 | /// Test: Set HEAD per state announcement | ||
| 410 | /// Spec: Line 21 | ||
| 411 | /// Requirement: MUST set HEAD when git data received | ||
| 412 | async fn test_set_head_from_state_announcement() | ||
| 413 | ``` | ||
| 414 | |||
| 415 | **Test Details:** | ||
| 416 | - Create kind 30618 with `HEAD = ref: refs/heads/develop` | ||
| 417 | - Push git data for develop branch | ||
| 418 | - Clone repository | ||
| 419 | - Verify HEAD points to develop (not main) | ||
| 420 | |||
| 421 | ```rust | ||
| 422 | /// Test: Update HEAD when state changes | ||
| 423 | /// Spec: Line 21 | ||
| 424 | /// Requirement: Update HEAD as soon as git data available | ||
| 425 | async fn test_update_head_when_state_changes() | ||
| 426 | ``` | ||
| 427 | |||
| 428 | **Test Details:** | ||
| 429 | - Initial state: HEAD = main | ||
| 430 | - Push new state: HEAD = develop | ||
| 431 | - Push git data for develop | ||
| 432 | - Verify HEAD updates to develop | ||
| 433 | |||
| 434 | #### 3.4 Pull Request Refs | ||
| 435 | |||
| 436 | ```rust | ||
| 437 | /// Test: Accept push to refs/nostr/<event-id> | ||
| 438 | /// Spec: Line 23 | ||
| 439 | /// Requirement: MUST accept pushes to PR refs | ||
| 440 | async fn test_accept_push_to_pr_ref() | ||
| 441 | ``` | ||
| 442 | |||
| 443 | **Test Details:** | ||
| 444 | - Create kind 1618 PR event | ||
| 445 | - Push to `refs/nostr/<pr-event-id>` | ||
| 446 | - Verify push accepted | ||
| 447 | - Verify ref exists in repository | ||
| 448 | |||
| 449 | ```rust | ||
| 450 | /// Test: Reject PR ref if event has different tip | ||
| 451 | /// Spec: Line 23 | ||
| 452 | /// Requirement: SHOULD reject if tip mismatch | ||
| 453 | async fn test_reject_pr_ref_tip_mismatch() | ||
| 454 | ``` | ||
| 455 | |||
| 456 | **Test Details:** | ||
| 457 | - Create kind 1618 PR with `c` tag = commit A | ||
| 458 | - Push to `refs/nostr/<pr-event-id>` with commit B | ||
| 459 | - Verify push rejected (or document if accepted) | ||
| 460 | |||
| 461 | ```rust | ||
| 462 | /// Test: Delete PR ref if no event within 20 minutes | ||
| 463 | /// Spec: Line 23 | ||
| 464 | /// Requirement: SHOULD delete orphaned PR refs | ||
| 465 | async fn test_delete_orphaned_pr_ref() | ||
| 466 | ``` | ||
| 467 | |||
| 468 | **Test Details:** | ||
| 469 | - Push to `refs/nostr/<event-id>` | ||
| 470 | - Wait 20+ minutes without sending kind 1618/1619 event | ||
| 471 | - Check if ref is deleted | ||
| 472 | - Note: This is SHOULD, not MUST - document behavior | ||
| 473 | |||
| 474 | ```rust | ||
| 475 | /// Test: Keep PR ref if event exists | ||
| 476 | /// Spec: Line 23 (implicit) | ||
| 477 | /// Requirement: Keep ref if valid PR/update event exists | ||
| 478 | async fn test_keep_pr_ref_with_event() | ||
| 479 | ``` | ||
| 480 | |||
| 481 | **Test Details:** | ||
| 482 | - Push to `refs/nostr/<event-id>` | ||
| 483 | - Send kind 1618 PR event with matching `c` tag | ||
| 484 | - Wait 20+ minutes | ||
| 485 | - Verify ref still exists | ||
| 486 | |||
| 487 | #### 3.5 Git Protocol Features | ||
| 488 | |||
| 489 | ```rust | ||
| 490 | /// Test: Advertise allow-reachable-sha1-in-want | ||
| 491 | /// Spec: Line 25 | ||
| 492 | /// Requirement: MUST advertise and serve capability | ||
| 493 | async fn test_advertise_reachable_sha1_in_want() | ||
| 494 | ``` | ||
| 495 | |||
| 496 | **Test Details:** | ||
| 497 | - GET `/repo.git/info/refs?service=git-upload-pack` | ||
| 498 | - Parse git protocol response | ||
| 499 | - Verify `allow-reachable-sha1-in-want` in capabilities | ||
| 500 | |||
| 501 | ```rust | ||
| 502 | /// Test: Advertise allow-tip-sha1-in-want | ||
| 503 | /// Spec: Line 25 | ||
| 504 | /// Requirement: MUST advertise and serve capability | ||
| 505 | async fn test_advertise_tip_sha1_in_want() | ||
| 506 | ``` | ||
| 507 | |||
| 508 | **Test Details:** | ||
| 509 | - GET `/repo.git/info/refs?service=git-upload-pack` | ||
| 510 | - Parse git protocol response | ||
| 511 | - Verify `allow-tip-sha1-in-want` in capabilities | ||
| 512 | |||
| 513 | ```rust | ||
| 514 | /// Test: Serve available OIDs by SHA1 | ||
| 515 | /// Spec: Line 25 | ||
| 516 | /// Requirement: MUST serve available OIDs | ||
| 517 | async fn test_serve_oids_by_sha1() | ||
| 518 | ``` | ||
| 519 | |||
| 520 | **Test Details:** | ||
| 521 | - Push repository with known commits | ||
| 522 | - Perform git fetch with specific SHA1 want | ||
| 523 | - Verify server provides the object | ||
| 524 | |||
| 525 | #### 3.6 Web Interface | ||
| 526 | |||
| 527 | ```rust | ||
| 528 | /// Test: Serve webpage at repo endpoint | ||
| 529 | /// Spec: Line 27 | ||
| 530 | /// Requirement: SHOULD serve webpage with links | ||
| 531 | async fn test_serve_webpage_at_repo_endpoint() | ||
| 532 | ``` | ||
| 533 | |||
| 534 | **Test Details:** | ||
| 535 | - HTTP GET to `/<npub>/<identifier>.git` with `Accept: text/html` | ||
| 536 | - Verify HTML response (not git protocol) | ||
| 537 | - Verify links to git nostr clients (optional check) | ||
| 538 | |||
| 539 | ```rust | ||
| 540 | /// Test: Serve 404 for non-existent repos | ||
| 541 | /// Spec: Line 27 | ||
| 542 | /// Requirement: SHOULD serve 404 for missing repos | ||
| 543 | async fn test_serve_404_for_missing_repo() | ||
| 544 | ``` | ||
| 545 | |||
| 546 | **Test Details:** | ||
| 547 | - HTTP GET to `/<npub>/nonexistent.git` with `Accept: text/html` | ||
| 548 | - Verify 404 response | ||
| 549 | - Verify helpful error message | ||
| 550 | |||
| 551 | --- | ||
| 552 | |||
| 553 | ## 4. GRASP-01 CORS Tests (🔜 TO DO) | ||
| 554 | |||
| 555 | **File:** `grasp-audit/src/specs/grasp01_cors.rs` | ||
| 556 | |||
| 557 | **Spec Reference:** Lines 32-40 of `../grasp/01.md` | ||
| 558 | |||
| 559 | ### Test Functions to Implement: | ||
| 560 | |||
| 561 | ```rust | ||
| 562 | /// Test: Access-Control-Allow-Origin on all responses | ||
| 563 | /// Spec: Line 35 | ||
| 564 | /// Requirement: MUST set ACAO: * on ALL responses | ||
| 565 | async fn test_cors_allow_origin_on_all_responses() | ||
| 566 | ``` | ||
| 567 | |||
| 568 | **Test Details:** | ||
| 569 | - Test multiple endpoints: | ||
| 570 | - WebSocket upgrade (Nostr relay) | ||
| 571 | - Git HTTP endpoints (info/refs, upload-pack, receive-pack) | ||
| 572 | - NIP-11 endpoint | ||
| 573 | - Web interface | ||
| 574 | - Verify ALL include `Access-Control-Allow-Origin: *` | ||
| 575 | |||
| 576 | ```rust | ||
| 577 | /// Test: Access-Control-Allow-Methods on all responses | ||
| 578 | /// Spec: Line 36 | ||
| 579 | /// Requirement: MUST set ACAM: GET, POST on ALL responses | ||
| 580 | async fn test_cors_allow_methods_on_all_responses() | ||
| 581 | ``` | ||
| 582 | |||
| 583 | **Test Details:** | ||
| 584 | - Test same endpoints as above | ||
| 585 | - Verify ALL include `Access-Control-Allow-Methods: GET, POST` | ||
| 586 | |||
| 587 | ```rust | ||
| 588 | /// Test: Access-Control-Allow-Headers on all responses | ||
| 589 | /// Spec: Line 37 | ||
| 590 | /// Requirement: MUST set ACAH: Content-Type on ALL responses | ||
| 591 | async fn test_cors_allow_headers_on_all_responses() | ||
| 592 | ``` | ||
| 593 | |||
| 594 | **Test Details:** | ||
| 595 | - Test same endpoints as above | ||
| 596 | - Verify ALL include `Access-Control-Allow-Headers: Content-Type` | ||
| 597 | |||
| 598 | ```rust | ||
| 599 | /// Test: OPTIONS requests return 204 No Content | ||
| 600 | /// Spec: Line 38 | ||
| 601 | /// Requirement: MUST respond to OPTIONS with 204 | ||
| 602 | async fn test_cors_options_request() | ||
| 603 | ``` | ||
| 604 | |||
| 605 | **Test Details:** | ||
| 606 | - Send OPTIONS request to various endpoints | ||
| 607 | - Verify 204 No Content response | ||
| 608 | - Verify CORS headers present on OPTIONS response | ||
| 609 | |||
| 610 | ```rust | ||
| 611 | /// Test: CORS headers on error responses | ||
| 612 | /// Spec: Line 35 (ALL responses) | ||
| 613 | /// Requirement: CORS headers even on errors | ||
| 614 | async fn test_cors_headers_on_error_responses() | ||
| 615 | ``` | ||
| 616 | |||
| 617 | **Test Details:** | ||
| 618 | - Trigger various error conditions: | ||
| 619 | - 404 not found | ||
| 620 | - 403 forbidden (unauthorized push) | ||
| 621 | - 400 bad request | ||
| 622 | - Verify CORS headers present on all error responses | ||
| 623 | |||
| 624 | ```rust | ||
| 625 | /// Test: Preflight request handling | ||
| 626 | /// Spec: Lines 35-38 | ||
| 627 | /// Requirement: Full preflight support for web clients | ||
| 628 | async fn test_cors_preflight_request() | ||
| 629 | ``` | ||
| 630 | |||
| 631 | **Test Details:** | ||
| 632 | - Send OPTIONS with Origin and Access-Control-Request-Method headers | ||
| 633 | - Verify proper preflight response | ||
| 634 | - Verify subsequent actual request succeeds | ||
| 635 | |||
| 636 | --- | ||
| 637 | |||
| 638 | ## Implementation Priority | ||
| 639 | |||
| 640 | ### Phase 1: Core Nostr Relay Tests (Complete these first) | ||
| 641 | 1. ✅ NIP-01 smoke tests (DONE) | ||
| 642 | 2. Repository announcement acceptance/rejection | ||
| 643 | 3. Repository state announcement acceptance | ||
| 644 | 4. NIP-11 relay information document | ||
| 645 | 5. Related event acceptance (issues, patches, PRs) | ||
| 646 | |||
| 647 | ### Phase 2: Git Smart HTTP Tests | ||
| 648 | 1. Repository serving at correct paths | ||
| 649 | 2. Unauthenticated clone/fetch | ||
| 650 | 3. Push authorization and maintainer sets | ||
| 651 | 4. HEAD management | ||
| 652 | 5. Git protocol features (SHA1 capabilities) | ||
| 653 | |||
| 654 | ### Phase 3: Advanced Git Features | ||
| 655 | 1. Pull request refs (refs/nostr/<event-id>) | ||
| 656 | 2. PR ref lifecycle (creation, validation, deletion) | ||
| 657 | 3. Web interface (optional) | ||
| 658 | |||
| 659 | ### Phase 4: CORS Tests | ||
| 660 | 1. CORS headers on all endpoints | ||
| 661 | 2. OPTIONS request handling | ||
| 662 | 3. Preflight requests | ||
| 663 | 4. Error response CORS | ||
| 664 | |||
| 665 | --- | ||
| 666 | |||
| 667 | ## Test Execution Plan | ||
| 668 | |||
| 669 | ### Against ngit-relay Reference Implementation | ||
| 670 | |||
| 671 | ```bash | ||
| 672 | # 1. Start ngit-relay | ||
| 673 | cd ../ngit-relay | ||
| 674 | docker-compose up -d | ||
| 675 | |||
| 676 | # 2. Run tests | ||
| 677 | cd ../ngit-grasp/grasp-audit | ||
| 678 | cargo test --lib # Unit tests | ||
| 679 | |||
| 680 | # Run integration tests by category | ||
| 681 | cargo test --test grasp01_nostr_relay | ||
| 682 | cargo test --test grasp01_git_http | ||
| 683 | cargo test --test grasp01_cors | ||
| 684 | |||
| 685 | # 3. Run full audit | ||
| 686 | cargo run -- --url ws://localhost:8081 | ||
| 687 | ``` | ||
| 688 | |||
| 689 | ### Test Data Requirements | ||
| 690 | |||
| 691 | For comprehensive testing, we need: | ||
| 692 | - Multiple test keypairs (maintainers, contributors, non-maintainers) | ||
| 693 | - Sample git repositories with known commit history | ||
| 694 | - Valid NIP-34 event templates | ||
| 695 | - Test data for edge cases | ||
| 696 | |||
| 697 | --- | ||
| 698 | |||
| 699 | ## Success Criteria | ||
| 700 | |||
| 701 | - [ ] All GRASP-01 requirements have corresponding tests | ||
| 702 | - [ ] All tests reference specific spec line numbers | ||
| 703 | - [ ] All tests pass against ngit-relay reference implementation | ||
| 704 | - [ ] Tests are organized logically by spec sections | ||
| 705 | - [ ] Clear test output shows what requirement is being tested | ||
| 706 | - [ ] Tests can be run individually or as full suite | ||
| 707 | - [ ] Documentation explains what each test validates | ||
| 708 | |||
| 709 | --- | ||
| 710 | |||
| 711 | ## Notes | ||
| 712 | |||
| 713 | ### Spec Line Number References | ||
| 714 | |||
| 715 | When implementing tests, use this format: | ||
| 716 | |||
| 717 | ```rust | ||
| 718 | /// Test: <Short description> | ||
| 719 | /// Spec: Lines X-Y of ../grasp/01.md | ||
| 720 | /// Requirement: <Exact quote or paraphrase from spec> | ||
| 721 | async fn test_name() { | ||
| 722 | // Implementation | ||
| 723 | } | ||
| 724 | ``` | ||
| 725 | |||
| 726 | ### Test Naming Convention | ||
| 727 | |||
| 728 | - `test_accept_*` - Tests that verify acceptance of valid input | ||
| 729 | - `test_reject_*` - Tests that verify rejection of invalid input | ||
| 730 | - `test_serve_*` - Tests that verify correct serving of data | ||
| 731 | - `test_cors_*` - Tests for CORS functionality | ||
| 732 | - `test_nip11_*` - Tests for NIP-11 relay information | ||
| 733 | |||
| 734 | ### Edge Cases to Consider | ||
| 735 | |||
| 736 | 1. **Concurrent updates** - Multiple maintainers pushing simultaneously | ||
| 737 | 2. **Large repositories** - Performance with large git data | ||
| 738 | 3. **Invalid git data** - Corrupted pack files, invalid refs | ||
| 739 | 4. **Event ordering** - State announcement before repo announcement | ||
| 740 | 5. **Deleted events** - What happens when announcement is deleted? | ||
| 741 | 6. **Network failures** - Partial push, interrupted clone | ||
| 742 | 7. **Recursive maintainers** - Deep maintainer chains, circular references | ||
| 743 | |||
| 744 | --- | ||
| 745 | |||
| 746 | **Next Steps:** | ||
| 747 | 1. Implement Phase 1 tests (Nostr relay) | ||
| 748 | 2. Run against ngit-relay to validate | ||
| 749 | 3. Fix any failing tests | ||
| 750 | 4. Move to Phase 2 (Git HTTP) | ||
| 751 | 5. Iterate until all tests pass | ||
| 752 | |||