upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'grasp-audit/README.md')
-rw-r--r--grasp-audit/README.md121
1 files changed, 121 insertions, 0 deletions
diff --git a/grasp-audit/README.md b/grasp-audit/README.md
index 62f1f03..44deec2 100644
--- a/grasp-audit/README.md
+++ b/grasp-audit/README.md
@@ -199,6 +199,126 @@ RELAY_URL="ws://localhost:18081" cargo test --lib test_grasp01_nostr_relay_again
199 199
200</details> 200</details>
201 201
202## Test Design Pattern: Fixture-First
203
204To prevent rate-limiting from production relays during testing, we use a **fixture-first** approach that minimizes relay interactions.
205
206### Quick Start for New Tests
207
2081. Create TestContext at test start
2092. Get prerequisites via `ctx.get_fixture(FixtureKind::...)`
2103. Build test-specific events using fixtures as base
2114. Verify outcomes via `send_and_verify_accepted/rejected`
212
213### Pattern Template
214
215```rust
216pub async fn test_something(client: &AuditClient) -> TestResult {
217 TestResult::new(...)
218 .run(|| async {
219 // 1. Context
220 let ctx = TestContext::new(client);
221
222 // 2. Prerequisites (cached per-TestContext)
223 let repo = ctx.get_fixture(FixtureKind::ValidRepo).await?;
224
225 // 3. Test-specific event
226 let my_event = client.create_issue(&repo, "Title", "Content", vec![])?;
227
228 // 4. Verify
229 send_and_verify_accepted(client, my_event, "description").await?;
230
231 Ok(())
232 })
233 .await
234}
235```
236
237### Three-Layer Architecture
238
239```
240┌─────────────────────────────────────────────────────────────────┐
241│ Layer 3: Test Functions │
242│ Create TestContext, get fixtures, build scenarios, verify │
243├─────────────────────────────────────────────────────────────────┤
244│ Layer 2: FixtureKind + TestContext │
245│ ValidRepo, RepoState, MaintainerState, etc. │
246│ Mode-aware caching within TestContext │
247├─────────────────────────────────────────────────────────────────┤
248│ Layer 1: AuditClient │
249│ event_builder, create_repo_announcement, send_event │
250└─────────────────────────────────────────────────────────────────┘
251```
252
253### Available Fixtures
254
255| FixtureKind | Provides | Use When |
256|-------------|----------|----------|
257| `ValidRepo` | Accepted repo announcement (kind 30617) | Need a repo as prerequisite |
258| `RepoState` | Repo + state event (kind 30618) | Testing owner push authorization |
259| `MaintainerAnnouncement` | Maintainer's repo announcement | Testing maintainer chain setup |
260| `MaintainerState` | Maintainer's state event | Testing maintainer push authorization |
261| `RepoWithIssue` | Repo + accepted issue (kind 1621) | Testing issue-dependent events |
262| `RepoWithComment` | Repo + issue + comment | Testing comment-dependent events |
263
264### Fixture Lifecycle: Generate → Send → Verify
265
266Every fixture follows a 3-step lifecycle:
267
2681. **GENERATE**: Build event via `AuditClient.event_builder()` (in memory only)
2692. **SEND**: `client.send_event(event)` transmits to relay (rate-limited operation)
2703. **VERIFY**: Query relay to confirm acceptance/rejection
271
272Caching happens after SEND succeeds - same fixture request returns cached Event.
273
274### How TestContext Correlates Events
275
276Each TestContext shares a `run_id` with all events:
277
278```rust
279// All events in a TestContext get these tags automatically:
280["t", "grasp-audit-test-event"] // Identifies test events
281["t", "audit-{run_id}"] // Unique ID for this run
282["t", "audit-cleanup-after-{ts}"] // Cleanup timestamp
283```
284
285This enables:
286- Event correlation within a test run
287- Production relay cleanup scripts
288- Test isolation between runs
289
290### When NOT to Use Fixtures
291
292Use direct event building (NOT fixtures) when:
293
294- **Testing event REJECTION** - Build invalid events directly
295- **Testing signature/ID validation** - Need malformed events
296- **One-off connectivity tests** - No prerequisites needed
297
298```rust
299// Example: Testing rejection (build invalid event directly)
300let invalid_event = client.event_builder(Kind::GitRepoAnnouncement, "")
301 .tag(Tag::identifier("test"))
302 // Missing required 'clone' tag - should be rejected
303 .build(client.keys())?;
304
305send_and_verify_rejected(client, invalid_event, "missing clone tag").await?;
306```
307
308### Anti-Patterns to Avoid
309
310❌ **Creating TestContext inside helper functions** - Tests lose cache control
311
312❌ **Monolithic setup functions** - Mix fixture retrieval with git operations
313
314❌ **Direct event creation when fixture exists** - Misses caching opportunity
315
316✅ **Each test creates own TestContext** - Isolation guaranteed
317
318✅ **Use fixtures for prerequisites** - Caching minimizes relay calls
319
320✅ **Build invalid events directly** - Only for rejection tests
321
202## Architecture 322## Architecture
203 323
204``` 324```
@@ -207,6 +327,7 @@ grasp-audit/
207│ ├── lib.rs # Public API 327│ ├── lib.rs # Public API
208│ ├── audit.rs # Audit config and event tagging 328│ ├── audit.rs # Audit config and event tagging
209│ ├── client.rs # Audit client 329│ ├── client.rs # Audit client
330│ ├── fixtures.rs # TestContext and FixtureKind
210│ ├── result.rs # Test result types 331│ ├── result.rs # Test result types
211│ ├── isolation.rs # Test isolation utilities 332│ ├── isolation.rs # Test isolation utilities
212│ └── specs/ 333│ └── specs/