upleb.uk

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

summaryrefslogtreecommitdiff
path: root/grasp-audit/src/fixtures.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-01 22:13:37 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-01 22:13:37 +0000
commit11870a0f810accf0431d82a74b6fd3adec9d23df (patch)
tree82a61995b1a18b4a618544b159e886d5c2e95015 /grasp-audit/src/fixtures.rs
parent883774c7785c57bded21ba3be63d0fdac8a51dcf (diff)
better fixtures: refs/nostr tests
Diffstat (limited to 'grasp-audit/src/fixtures.rs')
-rw-r--r--grasp-audit/src/fixtures.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/grasp-audit/src/fixtures.rs b/grasp-audit/src/fixtures.rs
index 5e8c50a..d054e36 100644
--- a/grasp-audit/src/fixtures.rs
+++ b/grasp-audit/src/fixtures.rs
@@ -188,6 +188,50 @@ pub enum FixtureKind {
188 /// - Timestamp: 1 second in the past 188 /// - Timestamp: 1 second in the past
189 PREvent, 189 PREvent,
190 190
191 /// PR event generated (built) but NOT sent to relay
192 ///
193 /// This is a "Generated" stage fixture - the event is created but not published.
194 /// Useful for tests that need the PR event ID before the event exists on the relay.
195 ///
196 /// - Requires ValidRepo (uses same repo_id)
197 /// - Signed by `client.pr_author_keys()`
198 /// - Kind 1618 (NIP-34 PR)
199 /// - Includes `c` tag pointing to PR_TEST_COMMIT_HASH
200 /// - NOT sent to relay (use `client.send_event()` to publish when ready)
201 PREventGenerated,
202
203 /// Wrong commit pushed to refs/nostr/<pr-event-id> BEFORE PR event is sent
204 ///
205 /// This is a "DataPushed" stage fixture for testing pre-event ref behavior.
206 /// The server has refs/nostr/<pr-event-id> pointing to DETERMINISTIC_COMMIT_HASH
207 /// (the "wrong" commit), but no PR event exists yet on the relay.
208 ///
209 /// Server state after this fixture:
210 /// - ValidRepo announcement on relay
211 /// - refs/nostr/<pr-event-id> exists on git server with wrong commit
212 /// - PR event is NOT on relay (but returned for tests to publish later)
213 ///
214 /// - Requires PREventGenerated (for the event ID)
215 /// - Clones repo, creates wrong commit, pushes to refs/nostr/<event-id>
216 /// - Returns: the unsent PR event (tests can publish it later)
217 PRWrongCommitPushedBeforeEvent,
218
219 /// PR event sent to relay AFTER wrong commit was pushed to refs/nostr/<pr-event-id>
220 ///
221 /// This is a compound fixture testing post-event behavior.
222 /// The server had refs/nostr/<pr-event-id> pointing to wrong commit,
223 /// then the PR event was published (which may trigger cleanup).
224 ///
225 /// Server state after this fixture:
226 /// - ValidRepo announcement on relay
227 /// - PR event is on relay
228 /// - refs/nostr/<pr-event-id> may have been cleaned up (that's what tests verify)
229 ///
230 /// - Requires PRWrongCommitPushedBeforeEvent
231 /// - Sends the PR event to relay
232 /// - Returns: the sent PR event
233 PREventSentAfterWrongPush,
234
191 /// Owner's state event with git data successfully pushed (full 4-stage fixture) 235 /// Owner's state event with git data successfully pushed (full 4-stage fixture)
192 /// 236 ///
193 /// This fixture represents the complete flow for testing push authorization: 237 /// This fixture represents the complete flow for testing push authorization:
@@ -268,6 +312,9 @@ impl FixtureKind {
268 Self::RecursiveMaintainerState => vec![Self::ValidRepo], 312 Self::RecursiveMaintainerState => vec![Self::ValidRepo],
269 Self::RecursiveMaintainerRepoAndState => vec![Self::ValidRepo], 313 Self::RecursiveMaintainerRepoAndState => vec![Self::ValidRepo],
270 Self::PREvent => vec![Self::ValidRepo], 314 Self::PREvent => vec![Self::ValidRepo],
315 Self::PREventGenerated => vec![Self::ValidRepo],
316 Self::PRWrongCommitPushedBeforeEvent => vec![Self::PREventGenerated],
317 Self::PREventSentAfterWrongPush => vec![Self::PRWrongCommitPushedBeforeEvent],
271 Self::OwnerStateDataPushed => vec![Self::ValidRepo], 318 Self::OwnerStateDataPushed => vec![Self::ValidRepo],
272 319
273 // Fixtures that depend on RepoWithIssue 320 // Fixtures that depend on RepoWithIssue
@@ -296,6 +343,12 @@ impl FixtureKind {
296 Self::RecursiveMaintainerStateDataPushed => true, 343 Self::RecursiveMaintainerStateDataPushed => true,
297 // RecursiveMaintainerRepoAndState sends multiple events internally 344 // RecursiveMaintainerRepoAndState sends multiple events internally
298 Self::RecursiveMaintainerRepoAndState => true, 345 Self::RecursiveMaintainerRepoAndState => true,
346 // PREventGenerated builds but does NOT send the PR event (that's the point)
347 Self::PREventGenerated => true,
348 // PRWrongCommitPushedBeforeEvent pushes git data but doesn't send event
349 Self::PRWrongCommitPushedBeforeEvent => true,
350 // PREventSentAfterWrongPush sends the PR event internally
351 Self::PREventSentAfterWrongPush => true,
299 // All other fixtures return a single event for the caller to send 352 // All other fixtures return a single event for the caller to send
300 _ => false, 353 _ => false,
301 } 354 }
@@ -753,6 +806,57 @@ impl<'a> TestContext<'a> {
753 .map_err(|e| anyhow::anyhow!("Failed to build PR event: {}", e)) 806 .map_err(|e| anyhow::anyhow!("Failed to build PR event: {}", e))
754 } 807 }
755 808
809 FixtureKind::PREventGenerated => {
810 // Same as PREvent but will NOT be sent to relay (caller may send it later)
811 // This fixture is for "Generated" stage only
812 use nostr_sdk::prelude::*;
813
814 // ValidRepo is ensured by ensure_fixture before this is called
815 let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?;
816
817 let repo_id = repo
818 .tags
819 .iter()
820 .find(|t| t.kind() == TagKind::d())
821 .and_then(|t| t.content())
822 .ok_or_else(|| anyhow::anyhow!("Missing repo_id in ValidRepo fixture"))?
823 .to_string();
824
825 // Create PR event 1 second in the past
826 let base_time = Timestamp::now().as_u64();
827 let pr_timestamp = Timestamp::from(base_time - 1);
828
829 // Build NIP-34 PR event (kind 1618)
830 self.client
831 .event_builder(
832 Kind::Custom(1618), // NIP-34 PR kind (has 'c' tag for commit)
833 "Test PR for GRASP validation",
834 )
835 .tag(Tag::custom(
836 TagKind::custom("a"),
837 vec![format!(
838 "30617:{}:{}",
839 self.client.public_key().to_hex(), // Owner pubkey
840 repo_id
841 )],
842 ))
843 .tag(Tag::custom(
844 TagKind::custom("c"),
845 vec![PR_TEST_COMMIT_HASH.to_string()],
846 ))
847 .custom_time(pr_timestamp)
848 .build(self.client.pr_author_keys())
849 .map_err(|e| anyhow::anyhow!("Failed to build PR event: {}", e))
850 }
851
852 FixtureKind::PRWrongCommitPushedBeforeEvent => {
853 self.build_pr_wrong_commit_pushed_before_event().await
854 }
855
856 FixtureKind::PREventSentAfterWrongPush => {
857 self.build_pr_event_sent_after_wrong_push().await
858 }
859
756 FixtureKind::OwnerStateDataPushed => { 860 FixtureKind::OwnerStateDataPushed => {
757 self.build_owner_state_data_pushed().await 861 self.build_owner_state_data_pushed().await
758 } 862 }
@@ -1370,6 +1474,127 @@ impl<'a> TestContext<'a> {
1370 } 1474 }
1371 } 1475 }
1372 1476
1477 /// Build PRWrongCommitPushedBeforeEvent fixture
1478 ///
1479 /// This fixture sets up a scenario where:
1480 /// 1. A repo exists on the relay
1481 /// 2. A PR event is generated (but NOT sent to relay)
1482 /// 3. A wrong commit is pushed to refs/nostr/<pr-event-id>
1483 ///
1484 /// Server state after:
1485 /// - ValidRepo announcement on relay
1486 /// - refs/nostr/<pr-event-id> on git server pointing to DETERMINISTIC_COMMIT_HASH (wrong)
1487 /// - NO PR event on relay
1488 ///
1489 /// Returns: the unsent PR event (tests can publish it later)
1490 async fn build_pr_wrong_commit_pushed_before_event(&self) -> Result<Event> {
1491 use nostr_sdk::prelude::*;
1492
1493 // Get the cached PREventGenerated (the unsent PR event)
1494 let pr_event = self.get_cached_dependency(FixtureKind::PREventGenerated)?;
1495 let pr_event_id = pr_event.id.to_hex();
1496
1497 // Get the ValidRepo to extract repo info
1498 let repo = self.get_cached_dependency(FixtureKind::ValidRepo)?;
1499 let repo_id = self.extract_repo_id(&repo)?;
1500
1501 // Get relay domain for cloning
1502 let relay_domain = self.get_relay_domain().await?;
1503
1504 // Owner npub for clone URL
1505 let npub = repo
1506 .pubkey
1507 .to_bech32()
1508 .map_err(|e| anyhow::anyhow!("Failed to convert pubkey to bech32: {}", e))?;
1509
1510 // Clone the repository (fresh clone - local repos are never cached)
1511 let clone_path = clone_repo(&relay_domain, &npub, &repo_id)
1512 .map_err(|e| anyhow::anyhow!("Failed to clone repo: {}", e))?;
1513
1514 // Cleanup helper
1515 let cleanup = |path: &PathBuf| {
1516 let _ = fs::remove_dir_all(path);
1517 };
1518
1519 // Create a WRONG commit (Owner variant, not PRTestCommit)
1520 // This commit hash will NOT match what's in the PR event's `c` tag
1521 let wrong_commit_hash = match create_deterministic_commit_with_variant(
1522 &clone_path,
1523 CommitVariant::Owner,
1524 ) {
1525 Ok(h) => h,
1526 Err(e) => {
1527 cleanup(&clone_path);
1528 return Err(anyhow::anyhow!("Failed to create wrong commit: {}", e));
1529 }
1530 };
1531
1532 // Verify it's actually different from expected PR commit
1533 if wrong_commit_hash == PR_TEST_COMMIT_HASH {
1534 cleanup(&clone_path);
1535 return Err(anyhow::anyhow!(
1536 "Test setup error: wrong_commit_hash {} equals PR_TEST_COMMIT_HASH",
1537 wrong_commit_hash
1538 ));
1539 }
1540
1541 // Create master branch if needed and push to refs/nostr/<pr-event-id>
1542 let _ = Command::new("git")
1543 .args(["branch", "-M", "master"])
1544 .current_dir(&clone_path)
1545 .output();
1546
1547 let push_output = Command::new("git")
1548 .args([
1549 "push",
1550 "origin",
1551 &format!("master:refs/nostr/{}", pr_event_id),
1552 ])
1553 .current_dir(&clone_path)
1554 .output()
1555 .map_err(|e| {
1556 cleanup(&clone_path);
1557 anyhow::anyhow!("Failed to execute git push: {}", e)
1558 })?;
1559
1560 cleanup(&clone_path);
1561
1562 if !push_output.status.success() {
1563 let stderr = String::from_utf8_lossy(&push_output.stderr);
1564 return Err(anyhow::anyhow!(
1565 "Initial push to refs/nostr/{} failed (expected success before PR event exists): {}",
1566 pr_event_id,
1567 stderr
1568 ));
1569 }
1570
1571 // Return the unsent PR event (tests can publish it later)
1572 Ok(pr_event)
1573 }
1574
1575 /// Build PREventSentAfterWrongPush fixture
1576 ///
1577 /// This fixture builds on PRWrongCommitPushedBeforeEvent by sending the PR event.
1578 /// After this fixture, the relay has:
1579 /// - ValidRepo announcement
1580 /// - PR event
1581 /// - refs/nostr/<pr-event-id> may have been cleaned up (that's what tests verify)
1582 ///
1583 /// Returns: the sent PR event
1584 async fn build_pr_event_sent_after_wrong_push(&self) -> Result<Event> {
1585 // Get the PR event that was cached by PRWrongCommitPushedBeforeEvent
1586 let pr_event = self.get_cached_dependency(FixtureKind::PRWrongCommitPushedBeforeEvent)?;
1587
1588 // Send the PR event to relay
1589 self.client.send_event(pr_event.clone()).await?;
1590
1591 // Wait for relay to process
1592 tokio::time::sleep(std::time::Duration::from_millis(500)).await;
1593
1594 // Return the now-sent PR event
1595 Ok(pr_event)
1596 }
1597
1373 /// Get relay domain (host:port) from the connected relay 1598 /// Get relay domain (host:port) from the connected relay
1374 /// 1599 ///
1375 /// Extracts the domain from the relay URL for git HTTP operations. 1600 /// Extracts the domain from the relay URL for git HTTP operations.