upleb.uk

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

summaryrefslogtreecommitdiff
path: root/docs/archive/2025-11-05-grasp01-smoke-test-design.md
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-11-18 16:50:02 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-11-18 16:50:02 +0000
commit98c6fa4bfa897ff0b8f9c95ea698d4d065b5e9f3 (patch)
tree14e94bbbfa1658229dbcd6ce9a03b3eb5cce4aef /docs/archive/2025-11-05-grasp01-smoke-test-design.md
parent1fe9c179d5dd73d443ab4792d4c2fbd19690afcb (diff)
docs: switch focus onto grasp implementation
Diffstat (limited to 'docs/archive/2025-11-05-grasp01-smoke-test-design.md')
-rw-r--r--docs/archive/2025-11-05-grasp01-smoke-test-design.md503
1 files changed, 503 insertions, 0 deletions
diff --git a/docs/archive/2025-11-05-grasp01-smoke-test-design.md b/docs/archive/2025-11-05-grasp01-smoke-test-design.md
new file mode 100644
index 0000000..ffff411
--- /dev/null
+++ b/docs/archive/2025-11-05-grasp01-smoke-test-design.md
@@ -0,0 +1,503 @@
1# GRASP-01 Event Relationship Smoke Tests Design
2
3**Version:** 1.0
4**Date:** 2025-11-05
5**Status:** Ready for Implementation
6
7## Overview
8
9This document specifies a focused suite of **smoke tests** for GRASP-01 event reference validation (lines 7-9). These tests validate the basic acceptance/rejection behavior based on event tagging relationships, separate from the comprehensive test suite.
10
11**Key Principle:** Events are accepted if they tag OR are tagged by accepted repositories.
12
13---
14
15## File Location
16
17**Proposed Path:** `grasp-audit/src/specs/grasp01/event-acceptance-policy.rs`
18
19**Rationale:**
20- Separate from comprehensive suite
21- Clear naming indicates purpose (smoke tests for event acceptance policy)
22- Lives in `grasp01/` subdirectory for organization
23- Can be run independently or as part of full suite
24
25---
26
27## Test Scenarios
28
29### Scenario Group 1: Accept Events Tagging Accepted Repositories
30
31Events that reference an already-accepted repo should be accepted.
32
33#### Test 1.1: `test_accept_issue_via_a_tag`
34**Tags Issue → Repo via `a` tag**
35
36```rust
37Setup:
381. Create and send repo announcement (kind 30617)
392. Create issue (kind 1621) with:
40 - ["a", "30617:{pubkey}:{repo-id}"]
413. Send issue
42
43Expected: Issue SHOULD be stored (query returns it)
44```
45
46---
47
48#### Test 1.2: `test_accept_comment_via_A_tag`
49**Tags Comment → Repo via `A` tag (NIP-22 root)**
50
51```rust
52Setup:
531. Create and send repo announcement
542. Create comment (kind 1111) with:
55 - ["A", "30617:{pubkey}:{repo-id}"] // Root
56 - ["K", "30617"]
57 - ["P", "{repo-pubkey}"]
583. Send comment
59
60Expected: Comment SHOULD be stored
61```
62
63---
64
65#### Test 1.3: `test_accept_kind1_via_q_tag`
66**Tags Kind 1 → Repo via `q` tag (quote)**
67
68```rust
69Setup:
701. Create and send repo announcement
712. Create kind 1 text note with:
72 - ["q", "30617:{pubkey}:{repo-id}"]
73 - content: "Check out this repo!"
743. Send kind 1
75
76Expected: Kind 1 SHOULD be stored
77```
78
79---
80
81### Scenario Group 2: Accept Events Tagging Accepted Events
82
83Events that reference other accepted events should be accepted (transitive acceptance).
84
85#### Test 2.1: `test_accept_issue_quoting_issue_via_q`
86**Issue referencing unaccepted repo but quoting accepted issue**
87
88```rust
89Setup:
901. Create and send repo A announcement
912. Create and send issue A (for repo A)
923. Create repo B announcement (DO NOT send - not accepted)
934. Create issue B (for repo B) with:
94 - ["a", "30617:{pubkey}:{repo-b-id}"] // References unaccepted repo B
95 - ["q", "{issue-a-id}"] // Quote accepted issue A
965. Send issue B
97
98Expected: Issue B SHOULD be stored (related via quote to accepted issue A,
99 even though its own repo reference is not accepted)
100```
101
102---
103
104#### Test 2.2: `test_accept_comment_via_E_tag`
105**Comment on issue via `E` tag (NIP-22)**
106
107```rust
108Setup:
1091. Create and send repo announcement
1102. Create and send issue (kind 1621)
1113. Create comment (kind 1111) with:
112 - ["E", "{issue-id}"] // Root
113 - ["K", "1621"]
114 - ["P", "{issue-author}"]
115 - ["e", "{issue-id}"] // Parent (same as root for top-level)
116 - ["k", "1621"]
117 - ["p", "{issue-author}"]
1184. Send comment
119
120Expected: Comment SHOULD be stored (related to accepted issue)
121```
122
123---
124
125#### Test 2.3: `test_accept_kind1_via_e_tag`
126**Kind 1 referencing another kind 1 via `e` tag**
127
128```rust
129Setup:
1301. Create and send repo announcement
1312. Create kind 1 note A with ["q", "30617:{pubkey}:{repo-id}"]
1323. Send kind 1 A
1334. Create kind 1 note B with:
134 - ["e", "{kind1-a-id}", "", "reply"]
135 - content: "Great point!"
1365. Send kind 1 B
137
138Expected: Kind 1 B SHOULD be stored (related via e tag to accepted kind 1 A)
139```
140
141---
142
143### Scenario Group 3: Accept Events Tagged by Accepted Events
144
145Events that are referenced BY accepted events should be accepted (forward references).
146
147#### Test 3.1: `test_accept_kind1_referenced_in_issue`
148**Kind 1 referenced in issue via `q` tag**
149
150```rust
151Setup:
1521. Create kind 1 note (NOT sent yet)
1532. Create and send repo announcement
1543. Create issue with:
155 - ["a", "30617:{pubkey}:{repo-id}"]
156 - ["q", "{kind1-id}"] // Reference the not-yet-sent kind 1
1574. Send issue
1585. Send kind 1 note
159
160Expected: Kind 1 SHOULD be stored (referenced by accepted issue)
161```
162
163---
164
165#### Test 3.2: `test_accept_comment_referenced_in_comment`
166**Comment referenced in another comment via `q` tag**
167
168```rust
169Setup:
1701. Create and send repo announcement
1712. Create and send issue
1723. Create comment A (NOT sent yet)
1734. Create comment B with:
174 - ["E", "{issue-id}"] // Root
175 - ["e", "{issue-id}"] // Parent
176 - ["q", "{comment-a-id}"] // Quote comment A
1775. Send comment B
1786. Send comment A
179
180Expected: Comment A SHOULD be stored (referenced by accepted comment B)
181```
182
183---
184
185#### Test 3.3: `test_accept_kind1_referenced_in_kind1`
186**Kind 1 referenced in accepted kind 1 via `e` tag**
187
188```rust
189Setup:
1901. Create and send repo announcement
1912. Create kind 1 A (NOT sent yet)
1923. Create kind 1 B with:
193 - ["q", "30617:{pubkey}:{repo-id}"]
194 - ["e", "{kind1-a-id}", "", "mention"]
1954. Send kind 1 B
1965. Send kind 1 A
197
198Expected: Kind 1 A SHOULD be stored (referenced by accepted kind 1 B)
199```
200
201---
202
203### Scenario Group 4: Reject Unrelated Events
204
205Events with no relationship to accepted repositories should be rejected.
206
207#### Test 4.1: `test_reject_orphan_issue`
208**Issue from unrelated repository**
209
210```rust
211Setup:
2121. Create issue (kind 1621) with:
213 - ["a", "30617:{other-pubkey}:{other-repo-id}"] // Different repo
2142. Send issue
215
216Expected: Issue SHOULD NOT be stored (no accepted repo)
217```
218
219---
220
221#### Test 4.2: `test_reject_orphan_kind1`
222**Kind 1 from unrelated context**
223
224```rust
225Setup:
2261. Create kind 1 note with generic content (no tags)
2272. Send kind 1
228
229Expected: Kind 1 SHOULD NOT be stored (no relationship to any repo)
230```
231
232---
233
234#### Test 4.3: `test_reject_comment_quoting_other_repo`
235**Comment quoting announcement from different repository**
236
237```rust
238Setup:
2391. Create repo A announcement (sent)
2402. Create repo B announcement (NOT sent - different owner)
2413. Create comment with:
242 - ["A", "30617:{other-pubkey}:{repo-b-id}"] // Root
243 - ["q", "30617:{other-pubkey}:{repo-b-id}"] // Quote unaccepted repo
2444. Send comment
245
246Expected: Comment SHOULD NOT be stored (references unaccepted repo)
247```
248
249---
250
251## Helper Functions
252
253Keep helpers minimal and focused on smoke test needs.
254
255**Implementation Note:** Reference [`nostr-sdk`](https://docs.rs/nostr-sdk) (rust-nostr) for event generation patterns. The SDK provides robust helpers for creating events with proper signatures and tags. Use these patterns rather than building everything from scratch.
256
257### `create_test_repo(client, repo_id) -> Event`
258Creates a basic repo announcement with required tags.
259
260```rust
261async fn create_test_repo(client: &AuditClient, repo_id: &str) -> Result<Event> {
262 client.create_repo_announcement(repo_id).await
263}
264```
265
266---
267
268### `create_issue_for_repo(client, repo_event, subject) -> Event`
269Creates issue referencing repo via `a` tag.
270
271```rust
272async fn create_issue_for_repo(
273 client: &AuditClient,
274 repo_event: &Event,
275 subject: &str,
276) -> Result<Event> {
277 let repo_id = extract_d_tag(repo_event)?;
278 let a_tag = Tag::parse(&["a", &format!("30617:{}:{}", repo_event.pubkey, repo_id)])?;
279
280 client.event_builder()
281 .kind(Kind::Custom(1621))
282 .content(format!("Issue: {}", subject))
283 .tag(a_tag)
284 .build()
285 .await
286}
287```
288
289---
290
291### `create_comment_for_event(client, root_event, content) -> Event`
292Creates NIP-22 comment for an event.
293
294```rust
295async fn create_comment_for_event(
296 client: &AuditClient,
297 root_event: &Event,
298 content: &str,
299) -> Result<Event> {
300 client.event_builder()
301 .kind(Kind::Custom(1111))
302 .content(content)
303 .tag(Tag::parse(&["E", &root_event.id.to_string()])?)
304 .tag(Tag::parse(&["K", &root_event.kind.to_string()])?)
305 .tag(Tag::parse(&["P", &root_event.pubkey.to_string()])?)
306 .tag(Tag::parse(&["e", &root_event.id.to_string()])?)
307 .tag(Tag::parse(&["k", &root_event.kind.to_string()])?)
308 .tag(Tag::parse(&["p", &root_event.pubkey.to_string()])?)
309 .build()
310 .await
311}
312```
313
314---
315
316### `send_and_verify_accepted(client, event) -> Result<()>`
317Sends event and verifies it was stored.
318
319```rust
320async fn send_and_verify_accepted(client: &AuditClient, event: Event) -> Result<()> {
321 let event_id = client.send_event(event.clone()).await?;
322
323 // Small delay for propagation
324 tokio::time::sleep(Duration::from_millis(100)).await;
325
326 let filter = Filter::new()
327 .id(event_id)
328 .limit(1);
329
330 let results = client.query(filter).await?;
331
332 if results.is_empty() {
333 return Err("Event was not stored".into());
334 }
335
336 Ok(())
337}
338```
339
340---
341
342### `send_and_verify_rejected(client, event) -> Result<()>`
343Sends event and verifies it was NOT stored.
344
345```rust
346async fn send_and_verify_rejected(client: &AuditClient, event: Event) -> Result<()> {
347 let event_id = event.id;
348
349 // Attempt to send
350 let _ = client.send_event(event).await;
351
352 // Small delay for propagation
353 tokio::time::sleep(Duration::from_millis(100)).await;
354
355 let filter = Filter::new()
356 .id(event_id)
357 .limit(1);
358
359 let results = client.query(filter).await?;
360
361 if !results.is_empty() {
362 return Err("Event was stored but should have been rejected".into());
363 }
364
365 Ok(())
366}
367```
368
369---
370
371### `extract_d_tag(event) -> Result<String>`
372Extracts `d` tag value from event.
373
374```rust
375fn extract_d_tag(event: &Event) -> Result<String> {
376 event.tags
377 .iter()
378 .find(|t| t.kind() == TagKind::d())
379 .and_then(|t| t.content())
380 .ok_or("Missing d tag")?
381 .to_string()
382}
383```
384
385---
386
387## Module Structure
388
389```rust
390//! GRASP-01 Event Relationship Smoke Tests
391//!
392//! Focused smoke tests validating basic event acceptance/rejection
393//! based on tagging relationships with accepted repositories.
394
395use crate::{AuditClient, AuditResult, TestResult};
396use nostr_sdk::prelude::*;
397use std::time::Duration;
398
399pub struct EventAcceptancePolicyTests;
400
401impl EventAcceptancePolicyTests {
402 pub async fn run_all(client: &AuditClient) -> AuditResult {
403 let mut results = AuditResult::new("GRASP-01 Event Acceptance Policy Tests");
404
405 // Group 1: Events tagging repos
406 results.add(Self::test_accept_issue_via_a_tag(client).await);
407 results.add(Self::test_accept_comment_via_A_tag(client).await);
408 results.add(Self::test_accept_kind1_via_q_tag(client).await);
409
410 // Group 2: Events tagging accepted events
411 results.add(Self::test_accept_issue_quoting_issue_via_q(client).await);
412 results.add(Self::test_accept_comment_via_E_tag(client).await);
413 results.add(Self::test_accept_kind1_via_e_tag(client).await);
414
415 // Group 3: Events tagged by accepted events
416 results.add(Self::test_accept_kind1_referenced_in_issue(client).await);
417 results.add(Self::test_accept_comment_referenced_in_comment(client).await);
418 results.add(Self::test_accept_kind1_referenced_in_kind1(client).await);
419
420 // Group 4: Reject unrelated events
421 results.add(Self::test_reject_orphan_issue(client).await);
422 results.add(Self::test_reject_orphan_kind1(client).await);
423 results.add(Self::test_reject_comment_quoting_other_repo(client).await);
424
425 results
426 }
427
428 // Test implementations follow...
429}
430
431// Helper functions follow...
432```
433
434---
435
436## Integration with Test Suite
437
438Add to `grasp-audit/src/specs/grasp01/mod.rs`:
439
440```rust
441pub mod event_acceptance_policy;
442
443pub use event_acceptance_policy::EventAcceptancePolicyTests;
444```
445
446Add to main test runner if desired, or run independently:
447
448```rust
449// In grasp01_nostr_relay.rs or separate test file
450#[tokio::test]
451#[ignore]
452async fn test_event_acceptance_policy_suite() {
453 let client = AuditClient::new_for_relay(&relay_url()).await.unwrap();
454 let results = EventAcceptancePolicyTests::run_all(&client).await;
455
456 // Assert all tests passed
457 assert!(results.all_passed(), "Some tests failed:\n{}", results);
458}
459```
460
461---
462
463## Implementation Notes
464
4651. **Simplicity First:** Keep test logic straightforward - setup, send, verify
4662. **Independent Tests:** Each test should be runnable standalone
4673. **Clear Failures:** Use descriptive error messages for debugging
4684. **Minimal Helpers:** Only create helpers that reduce significant duplication
4695. **Fast Execution:** Smoke tests should run quickly (use minimal delays)
470
471---
472
473## Expected Outcomes
474
475When implemented, this suite should:
476
477- ✅ Run in under 5 seconds total
478- ✅ Clearly show which relationship types work/fail
479- ✅ Provide quick validation during development
480- ✅ Act as regression tests for basic GRASP-01 compliance
481- ✅ Be easy to understand and modify
482
483---
484
485## Next Steps
486
4871. Create `grasp-audit/src/specs/grasp01/event-acceptance-policy.rs`
4882. Implement helper functions (referencing nostr-sdk patterns)
4893. Implement each test function following the specifications above
4904. Add module declaration to `grasp01/mod.rs`
4915. Run tests: `cd grasp-audit && nix develop -c bash test-ngit-relay.sh --mode test`
4926. Verify all tests pass or show expected "Not implemented yet" status
493
494---
495
496## Success Criteria
497
498- [ ] All 12 tests compile without errors
499- [ ] Tests run independently and as a suite
500- [ ] Accept tests verify events ARE stored
501- [ ] Reject tests verify events are NOT stored
502- [ ] Helper functions eliminate code duplication
503- [ ] Test output clearly indicates pass/fail/not-implemented \ No newline at end of file