1use std::{
2 collections::{BTreeMap, BTreeSet},
3 sync::Arc,
4};
5
6use bytes::Bytes;
7use interprex::{
8 AssetId, ChangeRequest, ChangeRequestNumber, CheckOutcome, CheckRun, DispatchInputs, Issue,
9 IssueNumber, Label, ProviderAppId, ProviderError, Release, Repository, RepositoryFacts,
10 RepositorySettings, ReviewActorId, ReviewId, ReviewPublicationKey, ReviewRequestTarget,
11 ReviewSubmission, ReviewTarget, ReviewerApplication, Ruleset, RunId, WorkflowRun,
12};
13use tokio::sync::RwLock;
14
15#[derive(Clone, Debug, Default)]
16pub struct FakeProvider {
17 pub(crate) state: Arc<RwLock<State>>,
18}
19
20#[derive(Debug, Default)]
21pub(crate) struct State {
22 pub(crate) repositories: BTreeMap<Repository, (RepositoryFacts, RepositorySettings)>,
23 pub(crate) rulesets: BTreeMap<Repository, Vec<Ruleset>>,
24 pub(crate) secret_names: BTreeMap<Repository, Vec<String>>,
25 pub(crate) issues: BTreeMap<(Repository, IssueNumber), Issue>,
26 pub(crate) labels: BTreeMap<Repository, Vec<Label>>,
27 pub(crate) change_requests: BTreeMap<(Repository, ChangeRequestNumber), ChangeRequest>,
28 pub(crate) review_target_observations: Vec<(Repository, ReviewRequestTarget, ReviewTarget)>,
29 pub(crate) reviewer_applications: BTreeMap<(Repository, String), ReviewerApplication>,
30 pub(crate) review_publications: BTreeMap<FakeReviewPublicationKey, FakeReviewPublication>,
31 pub(crate) check_runs: BTreeMap<(Repository, String), Vec<CheckRun>>,
32 pub(crate) published_checks: Vec<(Repository, String, CheckOutcome)>,
33 pub(crate) dispatches: Vec<(Repository, String, String, DispatchInputs)>,
34 pub(crate) runs: BTreeMap<(Repository, RunId), WorkflowRun>,
35 pub(crate) cancelled_runs: Vec<(Repository, RunId)>,
36 pub(crate) releases: BTreeMap<(Repository, String), Release>,
37 pub(crate) assets: BTreeMap<(Repository, AssetId), Vec<Bytes>>,
38 pub(crate) next_release_id: u64,
39 pub(crate) next_asset_id: u64,
40}
41
42pub(crate) type FakeReviewPublicationKey = (
43 Repository,
44 ChangeRequestNumber,
45 ProviderAppId,
46 ReviewActorId,
47 ReviewPublicationKey,
48);
49
50#[derive(Clone, Debug)]
51pub(crate) struct FakeReviewPublication {
52 pub(crate) submission: ReviewSubmission,
53 pub(crate) review_id: ReviewId,
54}
55
56impl FakeProvider {
57 #[must_use]
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub async fn seed_repository(&self, facts: RepositoryFacts, settings: RepositorySettings) {
63 self.state
64 .write()
65 .await
66 .repositories
67 .insert(facts.repository.clone(), (facts, settings));
68 }
69
70 pub async fn seed_issue(&self, repository: Repository, issue: Issue) {
71 self.state
72 .write()
73 .await
74 .issues
75 .insert((repository, issue.number), issue);
76 }
77
78 pub async fn seed_change_request(&self, repository: Repository, change_request: ChangeRequest) {
85 self.state
86 .write()
87 .await
88 .change_requests
89 .insert((repository, change_request.number), change_request);
90 }
91
92 pub async fn seed_review_request_target(
99 &self,
100 repository: Repository,
101 target: ReviewRequestTarget,
102 observed: ReviewTarget,
103 ) {
104 let mut state = self.state.write().await;
105 state
106 .review_target_observations
107 .retain(|(seeded_repository, seeded_target, _)| {
108 seeded_repository != &repository || seeded_target != &target
109 });
110 state
111 .review_target_observations
112 .push((repository, target, observed));
113 }
114
115 pub async fn seed_reviewer_application(
121 &self,
122 repository: Repository,
123 slug: String,
124 application: ReviewerApplication,
125 ) {
126 self.state
127 .write()
128 .await
129 .reviewer_applications
130 .insert((repository, slug), application);
131 }
132
133 pub async fn seed_check_runs(&self, repository: Repository, runs: Vec<CheckRun>) {
139 let mut state = self.state.write().await;
140 let mut replaced = BTreeSet::new();
141 for run in runs {
142 let key = (repository.clone(), run.head_sha.clone());
143 if replaced.insert(key.clone()) {
144 state.check_runs.insert(key.clone(), Vec::new());
145 }
146 state.check_runs.entry(key).or_default().push(run);
147 }
148 }
149
150 pub async fn seed_run(&self, repository: Repository, run: WorkflowRun) {
151 self.state
152 .write()
153 .await
154 .runs
155 .insert((repository, run.id), run);
156 }
157
158 pub async fn seed_release(&self, repository: Repository, release: Release) {
159 self.state
160 .write()
161 .await
162 .releases
163 .insert((repository, release.tag.clone()), release);
164 }
165
166 pub async fn published_checks(&self) -> Vec<(Repository, String, CheckOutcome)> {
167 self.state.read().await.published_checks.clone()
168 }
169
170 pub async fn dispatches(&self) -> Vec<(Repository, String, String, DispatchInputs)> {
171 self.state.read().await.dispatches.clone()
172 }
173}
174
175pub(crate) fn missing(entity: impl Into<String>) -> ProviderError {
176 ProviderError::NotFound {
177 entity: entity.into(),
178 }
179}