diff options
Diffstat (limited to 'grasp-audit')
| -rw-r--r-- | grasp-audit/README.md | 121 |
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 | |||
| 204 | To 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 | |||
| 208 | 1. Create TestContext at test start | ||
| 209 | 2. Get prerequisites via `ctx.get_fixture(FixtureKind::...)` | ||
| 210 | 3. Build test-specific events using fixtures as base | ||
| 211 | 4. Verify outcomes via `send_and_verify_accepted/rejected` | ||
| 212 | |||
| 213 | ### Pattern Template | ||
| 214 | |||
| 215 | ```rust | ||
| 216 | pub 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 | |||
| 266 | Every fixture follows a 3-step lifecycle: | ||
| 267 | |||
| 268 | 1. **GENERATE**: Build event via `AuditClient.event_builder()` (in memory only) | ||
| 269 | 2. **SEND**: `client.send_event(event)` transmits to relay (rate-limited operation) | ||
| 270 | 3. **VERIFY**: Query relay to confirm acceptance/rejection | ||
| 271 | |||
| 272 | Caching happens after SEND succeeds - same fixture request returns cached Event. | ||
| 273 | |||
| 274 | ### How TestContext Correlates Events | ||
| 275 | |||
| 276 | Each 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 | |||
| 285 | This 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 | |||
| 292 | Use 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) | ||
| 300 | let 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 | |||
| 305 | send_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/ |