diff options
Diffstat (limited to 'src/purgatory/sync/context.rs')
| -rw-r--r-- | src/purgatory/sync/context.rs | 77 |
1 files changed, 75 insertions, 2 deletions
diff --git a/src/purgatory/sync/context.rs b/src/purgatory/sync/context.rs index e97b708..2922f10 100644 --- a/src/purgatory/sync/context.rs +++ b/src/purgatory/sync/context.rs | |||
| @@ -63,6 +63,18 @@ impl ProcessResult { | |||
| 63 | /// with mocks. | 63 | /// with mocks. |
| 64 | #[async_trait] | 64 | #[async_trait] |
| 65 | pub trait SyncContext: Send + Sync { | 65 | pub trait SyncContext: Send + Sync { |
| 66 | /// Collect clone URLs from PR events in purgatory for a given identifier. | ||
| 67 | /// | ||
| 68 | /// PR events (kind 1618) and PR Update events (kind 1619) can include `clone` tags | ||
| 69 | /// specifying where the PR commits can be fetched from. This method extracts those | ||
| 70 | /// URLs to supplement the clone URLs from repository announcements. | ||
| 71 | /// | ||
| 72 | /// # Arguments | ||
| 73 | /// * `identifier` - The repository identifier | ||
| 74 | /// | ||
| 75 | /// # Returns | ||
| 76 | /// Set of clone URLs from PR events in purgatory for this identifier | ||
| 77 | fn collect_pr_clone_urls(&self, identifier: &str) -> HashSet<String>; | ||
| 66 | /// Get repository data (announcements, clone URLs, etc.) from the database. | 78 | /// Get repository data (announcements, clone URLs, etc.) from the database. |
| 67 | /// | 79 | /// |
| 68 | /// # Arguments | 80 | /// # Arguments |
| @@ -232,6 +244,30 @@ impl RealSyncContext { | |||
| 232 | 244 | ||
| 233 | #[async_trait] | 245 | #[async_trait] |
| 234 | impl SyncContext for RealSyncContext { | 246 | impl SyncContext for RealSyncContext { |
| 247 | fn collect_pr_clone_urls(&self, identifier: &str) -> HashSet<String> { | ||
| 248 | let mut urls = HashSet::new(); | ||
| 249 | |||
| 250 | for entry in self.purgatory.find_prs_for_identifier(identifier) { | ||
| 251 | if let Some(ref event) = entry.event { | ||
| 252 | for tag in event.tags.iter() { | ||
| 253 | let tag_vec = tag.clone().to_vec(); | ||
| 254 | if tag_vec.len() >= 2 && tag_vec[0] == "clone" { | ||
| 255 | // Clone tags can have multiple URLs: ["clone", "url1", "url2", ...] | ||
| 256 | urls.extend(tag_vec[1..].iter().cloned()); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | debug!( | ||
| 263 | identifier = %identifier, | ||
| 264 | pr_clone_urls_count = urls.len(), | ||
| 265 | "Collected clone URLs from PR events in purgatory" | ||
| 266 | ); | ||
| 267 | |||
| 268 | urls | ||
| 269 | } | ||
| 270 | |||
| 235 | async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> { | 271 | async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> { |
| 236 | crate::git::authorization::fetch_repository_data(&self.database, identifier).await | 272 | crate::git::authorization::fetch_repository_data(&self.database, identifier).await |
| 237 | } | 273 | } |
| @@ -450,9 +486,12 @@ pub mod mock { | |||
| 450 | /// Repository data to return from fetch_repository_data | 486 | /// Repository data to return from fetch_repository_data |
| 451 | repo_data: RwLock<Option<RepositoryData>>, | 487 | repo_data: RwLock<Option<RepositoryData>>, |
| 452 | 488 | ||
| 453 | /// Clone URLs available for the repository | 489 | /// Clone URLs available for the repository (from announcements) |
| 454 | clone_urls: Vec<String>, | 490 | clone_urls: Vec<String>, |
| 455 | 491 | ||
| 492 | /// Clone URLs from PR events in purgatory | ||
| 493 | pr_clone_urls: HashSet<String>, | ||
| 494 | |||
| 456 | /// OIDs still needed (decremented when "fetched") | 495 | /// OIDs still needed (decremented when "fetched") |
| 457 | needed_oids: RwLock<HashSet<String>>, | 496 | needed_oids: RwLock<HashSet<String>>, |
| 458 | 497 | ||
| @@ -490,6 +529,7 @@ pub mod mock { | |||
| 490 | Self { | 529 | Self { |
| 491 | repo_data: RwLock::new(None), | 530 | repo_data: RwLock::new(None), |
| 492 | clone_urls: Vec::new(), | 531 | clone_urls: Vec::new(), |
| 532 | pr_clone_urls: HashSet::new(), | ||
| 493 | needed_oids: RwLock::new(HashSet::new()), | 533 | needed_oids: RwLock::new(HashSet::new()), |
| 494 | url_provides_oids: HashMap::new(), | 534 | url_provides_oids: HashMap::new(), |
| 495 | fetch_log: RwLock::new(Vec::new()), | 535 | fetch_log: RwLock::new(Vec::new()), |
| @@ -501,12 +541,18 @@ pub mod mock { | |||
| 501 | } | 541 | } |
| 502 | } | 542 | } |
| 503 | 543 | ||
| 504 | /// Configure clone URLs for the repository. | 544 | /// Configure clone URLs for the repository (from announcements). |
| 505 | pub fn with_urls(mut self, urls: &[&str]) -> Self { | 545 | pub fn with_urls(mut self, urls: &[&str]) -> Self { |
| 506 | self.clone_urls = urls.iter().map(|s| s.to_string()).collect(); | 546 | self.clone_urls = urls.iter().map(|s| s.to_string()).collect(); |
| 507 | self | 547 | self |
| 508 | } | 548 | } |
| 509 | 549 | ||
| 550 | /// Configure clone URLs from PR events in purgatory. | ||
| 551 | pub fn with_pr_clone_urls(mut self, urls: &[&str]) -> Self { | ||
| 552 | self.pr_clone_urls = urls.iter().map(|s| s.to_string()).collect(); | ||
| 553 | self | ||
| 554 | } | ||
| 555 | |||
| 510 | /// Configure OIDs that are still needed. | 556 | /// Configure OIDs that are still needed. |
| 511 | pub fn with_needed_oids(self, oids: &[&str]) -> Self { | 557 | pub fn with_needed_oids(self, oids: &[&str]) -> Self { |
| 512 | *self.needed_oids.write().unwrap() = oids.iter().map(|s| s.to_string()).collect(); | 558 | *self.needed_oids.write().unwrap() = oids.iter().map(|s| s.to_string()).collect(); |
| @@ -580,6 +626,10 @@ pub mod mock { | |||
| 580 | 626 | ||
| 581 | #[async_trait] | 627 | #[async_trait] |
| 582 | impl SyncContext for MockSyncContext { | 628 | impl SyncContext for MockSyncContext { |
| 629 | fn collect_pr_clone_urls(&self, _identifier: &str) -> HashSet<String> { | ||
| 630 | self.pr_clone_urls.clone() | ||
| 631 | } | ||
| 632 | |||
| 583 | async fn fetch_repository_data(&self, _identifier: &str) -> Result<RepositoryData> { | 633 | async fn fetch_repository_data(&self, _identifier: &str) -> Result<RepositoryData> { |
| 584 | // Return stored repo_data or create a minimal one with clone URLs | 634 | // Return stored repo_data or create a minimal one with clone URLs |
| 585 | if let Some(data) = self.repo_data.read().unwrap().as_ref() { | 635 | if let Some(data) = self.repo_data.read().unwrap().as_ref() { |
| @@ -791,5 +841,28 @@ pub mod mock { | |||
| 791 | mock.set_pending_events(false); | 841 | mock.set_pending_events(false); |
| 792 | assert!(!mock.has_pending_events("test-repo")); | 842 | assert!(!mock.has_pending_events("test-repo")); |
| 793 | } | 843 | } |
| 844 | |||
| 845 | #[test] | ||
| 846 | fn mock_collect_pr_clone_urls_returns_configured_urls() { | ||
| 847 | let mock = MockSyncContext::new().with_pr_clone_urls(&[ | ||
| 848 | "https://fork-server.com/repo.git", | ||
| 849 | "https://another-fork.com/repo.git", | ||
| 850 | ]); | ||
| 851 | |||
| 852 | let urls = mock.collect_pr_clone_urls("any-identifier"); | ||
| 853 | |||
| 854 | assert_eq!(urls.len(), 2); | ||
| 855 | assert!(urls.contains("https://fork-server.com/repo.git")); | ||
| 856 | assert!(urls.contains("https://another-fork.com/repo.git")); | ||
| 857 | } | ||
| 858 | |||
| 859 | #[test] | ||
| 860 | fn mock_collect_pr_clone_urls_empty_by_default() { | ||
| 861 | let mock = MockSyncContext::new(); | ||
| 862 | |||
| 863 | let urls = mock.collect_pr_clone_urls("any-identifier"); | ||
| 864 | |||
| 865 | assert!(urls.is_empty()); | ||
| 866 | } | ||
| 794 | } | 867 | } |
| 795 | } | 868 | } |