diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 22:34:17 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2025-12-01 22:34:17 +0000 |
| commit | 29ac0975a6a2f9e1cc585bd56a28b93205d0a2ac (patch) | |
| tree | 1ba0124839a3bff36f56780d67fdead2ecdd1f01 /grasp-audit/src | |
| parent | 504eaf4f5aba93a3e935bbee76042dd35cada666 (diff) | |
test: test_head_set_after_git_push_with_required_oids
Diffstat (limited to 'grasp-audit/src')
| -rw-r--r-- | grasp-audit/src/specs/grasp01/push_authorization.rs | 236 |
1 files changed, 204 insertions, 32 deletions
diff --git a/grasp-audit/src/specs/grasp01/push_authorization.rs b/grasp-audit/src/specs/grasp01/push_authorization.rs index 9e8597b..00b8ae9 100644 --- a/grasp-audit/src/specs/grasp01/push_authorization.rs +++ b/grasp-audit/src/specs/grasp01/push_authorization.rs | |||
| @@ -1031,7 +1031,10 @@ impl PushAuthorizationTests { | |||
| 1031 | // 2. Build PR event (but don't send it) | 1031 | // 2. Build PR event (but don't send it) |
| 1032 | // 3. Clone repo, create wrong commit, push to refs/nostr/<event-id> | 1032 | // 3. Clone repo, create wrong commit, push to refs/nostr/<event-id> |
| 1033 | // If the push fails, the fixture will return an error | 1033 | // If the push fails, the fixture will return an error |
| 1034 | match ctx.get_fixture(FixtureKind::PRWrongCommitPushedBeforeEvent).await { | 1034 | match ctx |
| 1035 | .get_fixture(FixtureKind::PRWrongCommitPushedBeforeEvent) | ||
| 1036 | .await | ||
| 1037 | { | ||
| 1035 | Ok(_pr_event) => TestResult::new(test_name, "GRASP-01", desc).pass(), | 1038 | Ok(_pr_event) => TestResult::new(test_name, "GRASP-01", desc).pass(), |
| 1036 | Err(e) => TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)), | 1039 | Err(e) => TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)), |
| 1037 | } | 1040 | } |
| @@ -1053,7 +1056,10 @@ impl PushAuthorizationTests { | |||
| 1053 | let ctx = TestContext::new(client); | 1056 | let ctx = TestContext::new(client); |
| 1054 | 1057 | ||
| 1055 | // Get fixture: wrong commit was pushed, then PR event was sent | 1058 | // Get fixture: wrong commit was pushed, then PR event was sent |
| 1056 | let pr_event = match ctx.get_fixture(FixtureKind::PREventSentAfterWrongPush).await { | 1059 | let pr_event = match ctx |
| 1060 | .get_fixture(FixtureKind::PREventSentAfterWrongPush) | ||
| 1061 | .await | ||
| 1062 | { | ||
| 1057 | Ok(e) => e, | 1063 | Ok(e) => e, |
| 1058 | Err(e) => { | 1064 | Err(e) => { |
| 1059 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); | 1065 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); |
| @@ -1134,7 +1140,10 @@ impl PushAuthorizationTests { | |||
| 1134 | let ctx = TestContext::new(client); | 1140 | let ctx = TestContext::new(client); |
| 1135 | 1141 | ||
| 1136 | // Get fixture: PR event exists on relay (wrong commit was previously pushed but may have been cleaned up) | 1142 | // Get fixture: PR event exists on relay (wrong commit was previously pushed but may have been cleaned up) |
| 1137 | let pr_event = match ctx.get_fixture(FixtureKind::PREventSentAfterWrongPush).await { | 1143 | let pr_event = match ctx |
| 1144 | .get_fixture(FixtureKind::PREventSentAfterWrongPush) | ||
| 1145 | .await | ||
| 1146 | { | ||
| 1138 | Ok(e) => e, | 1147 | Ok(e) => e, |
| 1139 | Err(e) => { | 1148 | Err(e) => { |
| 1140 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); | 1149 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); |
| @@ -1218,7 +1227,10 @@ impl PushAuthorizationTests { | |||
| 1218 | let ctx = TestContext::new(client); | 1227 | let ctx = TestContext::new(client); |
| 1219 | 1228 | ||
| 1220 | // Get fixture: PR event exists on relay | 1229 | // Get fixture: PR event exists on relay |
| 1221 | let pr_event = match ctx.get_fixture(FixtureKind::PREventSentAfterWrongPush).await { | 1230 | let pr_event = match ctx |
| 1231 | .get_fixture(FixtureKind::PREventSentAfterWrongPush) | ||
| 1232 | .await | ||
| 1233 | { | ||
| 1222 | Ok(e) => e, | 1234 | Ok(e) => e, |
| 1223 | Err(e) => { | 1235 | Err(e) => { |
| 1224 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); | 1236 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!("{}", e)); |
| @@ -1315,11 +1327,14 @@ impl PushAuthorizationTests { | |||
| 1315 | // ============================================================ | 1327 | // ============================================================ |
| 1316 | let ctx = TestContext::new(client); | 1328 | let ctx = TestContext::new(client); |
| 1317 | 1329 | ||
| 1318 | let _develop_state_event = match ctx.get_fixture(FixtureKind::HeadSetToDevelopBranch).await { | 1330 | let _develop_state_event = match ctx.get_fixture(FixtureKind::HeadSetToDevelopBranch).await |
| 1331 | { | ||
| 1319 | Ok(e) => e, | 1332 | Ok(e) => e, |
| 1320 | Err(e) => { | 1333 | Err(e) => { |
| 1321 | return TestResult::new(test_name, "GRASP-01", desc) | 1334 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!( |
| 1322 | .fail(format!("Failed to create HeadSetToDevelopBranch fixture: {}", e)); | 1335 | "Failed to create HeadSetToDevelopBranch fixture: {}", |
| 1336 | e | ||
| 1337 | )); | ||
| 1323 | } | 1338 | } |
| 1324 | }; | 1339 | }; |
| 1325 | 1340 | ||
| @@ -1381,36 +1396,193 @@ impl PushAuthorizationTests { | |||
| 1381 | } | 1396 | } |
| 1382 | 1397 | ||
| 1383 | /// Test that HEAD is set after git push with oids | 1398 | /// Test that HEAD is set after git push with oids |
| 1399 | /// | ||
| 1400 | /// GRASP-01: "MUST set repository HEAD per repository state announcement | ||
| 1401 | /// as soon as the git data related to that branch has been received." | ||
| 1402 | /// | ||
| 1403 | /// This test verifies the HEAD-setting behavior when: | ||
| 1404 | /// 1. A new state event is published with HEAD="refs/heads/develop1" pointing to a new commit | ||
| 1405 | /// 2. The git data (the new commit) has NOT yet been pushed | ||
| 1406 | /// 3. The relay receives the git push with the required oids | ||
| 1407 | /// 4. Only AFTER the push completes should HEAD be updated to "develop1" | ||
| 1408 | /// | ||
| 1409 | /// This differs from test_head_set_after_state_event_with_existing_commit in that | ||
| 1410 | /// the git data doesn't exist yet when the state event is published. | ||
| 1411 | /// | ||
| 1412 | /// ## Fixture-First Pattern | ||
| 1413 | /// | ||
| 1414 | /// Uses HeadSetToDevelopBranch fixture as base, then: | ||
| 1415 | /// 1. **Depends on**: HeadSetToDevelopBranch (HEAD already set to develop) | ||
| 1416 | /// 2. **Clone**: Clone repo to create new local branch develop1 | ||
| 1417 | /// 3. **Create unique commit**: New commit on develop1 that doesn't exist on relay | ||
| 1418 | /// 4. **Build state event**: HEAD=refs/heads/develop1 pointing to new commit | ||
| 1419 | /// 5. **Send state event**: Before git push (git data not yet on relay) | ||
| 1420 | /// 6. **Git push**: Push develop1 branch - sends required oids | ||
| 1421 | /// 7. **Verify**: HEAD should now point to refs/heads/develop1 | ||
| 1384 | pub async fn test_head_set_after_git_push_with_required_oids( | 1422 | pub async fn test_head_set_after_git_push_with_required_oids( |
| 1385 | _client: &AuditClient, | 1423 | client: &AuditClient, |
| 1386 | _relay_domain: &str, | 1424 | relay_domain: &str, |
| 1387 | ) -> TestResult { | 1425 | ) -> TestResult { |
| 1388 | let test_name = "test_head_set_after_git_push_with_required_oids"; | 1426 | let test_name = "test_head_set_after_git_push_with_required_oids"; |
| 1389 | let desc = "HEAD is set to match state event when git push sends required oids to formulate branch"; | 1427 | let desc = "HEAD is set to match state event when git push sends required oids to formulate branch"; |
| 1390 | 1428 | ||
| 1391 | // DO the above as prep. then create a unique commit, create state event with HEAD=develop1 branch at unqiue commit | 1429 | // ============================================================ |
| 1392 | // git push the new develop1 branch. then check HEAD with this: | 1430 | // Step 1: Get HeadSetToDevelopBranch fixture as baseline |
| 1393 | // let default_branch = | 1431 | // This establishes: repo, maintainer chain, git data, HEAD=develop |
| 1394 | // match get_default_branch_from_info_refs(relay_domain, &npub, &repo_id).await { | 1432 | // ============================================================ |
| 1395 | // Ok(branch) => branch, | 1433 | let ctx = TestContext::new(client); |
| 1396 | // Err(e) => { | 1434 | |
| 1397 | // return TestResult::new(test_name, "GRASP-01", desc) | 1435 | let _develop_state = match ctx.get_fixture(FixtureKind::HeadSetToDevelopBranch).await { |
| 1398 | // .fail(format!("Failed to get default branch: {}", e)); | 1436 | Ok(e) => e, |
| 1399 | // } | 1437 | Err(e) => { |
| 1400 | // }; | 1438 | return TestResult::new(test_name, "GRASP-01", desc).fail(format!( |
| 1401 | 1439 | "Failed to create HeadSetToDevelopBranch fixture: {}", | |
| 1402 | // // Verify HEAD points to refs/heads/develop1 | 1440 | e |
| 1403 | // if default_branch == "refs/heads/develop1" { | 1441 | )); |
| 1404 | // TestResult::new(test_name, "GRASP-01", desc).pass() | 1442 | } |
| 1405 | // } else { | 1443 | }; |
| 1406 | // TestResult::new(test_name, "GRASP-01", desc).fail(format!( | 1444 | |
| 1407 | // "Expected HEAD to point to 'refs/heads/develop' but got '{}'. \ | 1445 | // ============================================================ |
| 1408 | // GRASP-01 requires: 'MUST set repository HEAD per repository state announcement \ | 1446 | // Step 2: Extract repo_id and owner npub from ValidRepo (cached by fixture) |
| 1409 | // as soon as the git data related to that branch has been received.'", | 1447 | // ============================================================ |
| 1410 | // default_branch | 1448 | let valid_repo = match ctx.get_fixture(FixtureKind::ValidRepo).await { |
| 1411 | // )) | 1449 | Ok(e) => e, |
| 1412 | // } | 1450 | Err(e) => { |
| 1413 | TestResult::new(test_name, "GRASP-01", desc).fail("test not implemented") | 1451 | return TestResult::new(test_name, "GRASP-01", desc) |
| 1452 | .fail(format!("Failed to get ValidRepo fixture: {}", e)); | ||
| 1453 | } | ||
| 1454 | }; | ||
| 1455 | |||
| 1456 | let repo_id = match valid_repo | ||
| 1457 | .tags | ||
| 1458 | .iter() | ||
| 1459 | .find(|t| t.kind() == TagKind::d()) | ||
| 1460 | .and_then(|t| t.content()) | ||
| 1461 | { | ||
| 1462 | Some(id) => id.to_string(), | ||
| 1463 | None => { | ||
| 1464 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1465 | .fail("Missing repo_id in ValidRepo"); | ||
| 1466 | } | ||
| 1467 | }; | ||
| 1468 | |||
| 1469 | let npub = match valid_repo.pubkey.to_bech32() { | ||
| 1470 | Ok(n) => n, | ||
| 1471 | Err(e) => { | ||
| 1472 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1473 | .fail(format!("Failed to convert pubkey to bech32: {}", e)); | ||
| 1474 | } | ||
| 1475 | }; | ||
| 1476 | |||
| 1477 | // ============================================================ | ||
| 1478 | // Step 3: Clone the repo to create a new local branch | ||
| 1479 | // ============================================================ | ||
| 1480 | let clone_path = match clone_repo(relay_domain, &npub, &repo_id) { | ||
| 1481 | Ok(path) => path, | ||
| 1482 | Err(e) => { | ||
| 1483 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1484 | .fail(format!("Failed to clone repo: {}", e)); | ||
| 1485 | } | ||
| 1486 | }; | ||
| 1487 | |||
| 1488 | // ============================================================ | ||
| 1489 | // Step 4: Create and checkout develop1 branch, then create unique commit | ||
| 1490 | // ============================================================ | ||
| 1491 | let output = Command::new("git") | ||
| 1492 | .args(["checkout", "-b", "develop1"]) | ||
| 1493 | .current_dir(&clone_path) | ||
| 1494 | .output(); | ||
| 1495 | |||
| 1496 | if let Err(e) = output { | ||
| 1497 | let _ = fs::remove_dir_all(&clone_path); | ||
| 1498 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1499 | .fail(format!("Failed to create develop1 branch: {}", e)); | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | // Create a unique commit on develop1 | ||
| 1503 | let commit_hash = match create_commit(&clone_path, "Unique develop1 commit") { | ||
| 1504 | Ok(hash) => hash, | ||
| 1505 | Err(e) => { | ||
| 1506 | let _ = fs::remove_dir_all(&clone_path); | ||
| 1507 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1508 | .fail(format!("Failed to create commit: {}", e)); | ||
| 1509 | } | ||
| 1510 | }; | ||
| 1511 | |||
| 1512 | // ============================================================ | ||
| 1513 | // Step 5: Build and send state event with HEAD=refs/heads/develop1 | ||
| 1514 | // This references a commit that doesn't yet exist on the relay | ||
| 1515 | // ============================================================ | ||
| 1516 | let state_event = match client | ||
| 1517 | .event_builder(Kind::Custom(30618), "") | ||
| 1518 | .tag(Tag::identifier(&repo_id)) | ||
| 1519 | .tag(Tag::custom( | ||
| 1520 | TagKind::custom("HEAD"), | ||
| 1521 | vec!["refs/heads/develop1".to_string()], | ||
| 1522 | )) | ||
| 1523 | .tag(Tag::custom( | ||
| 1524 | TagKind::custom("refs/heads/develop1"), | ||
| 1525 | vec![commit_hash.clone()], | ||
| 1526 | )) | ||
| 1527 | .build(client.keys()) | ||
| 1528 | { | ||
| 1529 | Ok(e) => e, | ||
| 1530 | Err(e) => { | ||
| 1531 | let _ = fs::remove_dir_all(&clone_path); | ||
| 1532 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1533 | .fail(format!("Failed to build state event: {}", e)); | ||
| 1534 | } | ||
| 1535 | }; | ||
| 1536 | |||
| 1537 | // Send the state event (commit doesn't exist on relay yet) | ||
| 1538 | if let Err(e) = client.send_event(state_event).await { | ||
| 1539 | let _ = fs::remove_dir_all(&clone_path); | ||
| 1540 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1541 | .fail(format!("Failed to send state event: {}", e)); | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | // ============================================================ | ||
| 1545 | // Step 6: Push the develop1 branch - this sends the required oids | ||
| 1546 | // ============================================================ | ||
| 1547 | let push_result = try_push_to_ref(&clone_path, "refs/heads/develop1"); | ||
| 1548 | let _ = fs::remove_dir_all(&clone_path); // Cleanup clone | ||
| 1549 | |||
| 1550 | match push_result { | ||
| 1551 | Ok(true) => { /* Push succeeded, continue to verify */ } | ||
| 1552 | Ok(false) => { | ||
| 1553 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1554 | .fail("Push to refs/heads/develop1 was rejected"); | ||
| 1555 | } | ||
| 1556 | Err(e) => { | ||
| 1557 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1558 | .fail(format!("Failed to push develop1 branch: {}", e)); | ||
| 1559 | } | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | // ============================================================ | ||
| 1563 | // Step 7: VERIFY - Query info/refs to check the default branch | ||
| 1564 | // HEAD should now point to refs/heads/develop1 as git data is available | ||
| 1565 | // ============================================================ | ||
| 1566 | let default_branch = | ||
| 1567 | match get_default_branch_from_info_refs(relay_domain, &npub, &repo_id).await { | ||
| 1568 | Ok(branch) => branch, | ||
| 1569 | Err(e) => { | ||
| 1570 | return TestResult::new(test_name, "GRASP-01", desc) | ||
| 1571 | .fail(format!("Failed to get default branch: {}", e)); | ||
| 1572 | } | ||
| 1573 | }; | ||
| 1574 | |||
| 1575 | // Verify HEAD points to refs/heads/develop1 | ||
| 1576 | if default_branch == "refs/heads/develop1" { | ||
| 1577 | TestResult::new(test_name, "GRASP-01", desc).pass() | ||
| 1578 | } else { | ||
| 1579 | TestResult::new(test_name, "GRASP-01", desc).fail(format!( | ||
| 1580 | "Expected HEAD to point to 'refs/heads/develop1' but got '{}'. \ | ||
| 1581 | GRASP-01 requires: 'MUST set repository HEAD per repository state announcement \ | ||
| 1582 | as soon as the git data related to that branch has been received.'", | ||
| 1583 | default_branch | ||
| 1584 | )) | ||
| 1585 | } | ||
| 1414 | } | 1586 | } |
| 1415 | } | 1587 | } |
| 1416 | 1588 | ||