1use std::{
2 collections::{BTreeMap, BTreeSet},
3 sync::Arc,
4};
5
6use bytes::Bytes;
7use interprex::{
8 AppliedSourceRequirements, AssetId, BranchUpdateObservation, ChangeRequest,
9 ChangeRequestNumber, CheckOutcome, CheckRun, CommitRange, DispatchInputs, Issue, IssueNumber,
10 Label, ProviderAppId, ProviderError, Release, Repository, RepositoryFacts, RepositorySettings,
11 ReviewActorId, ReviewDismissalMessage, ReviewId, ReviewPublicationKey, ReviewRequestTarget,
12 ReviewSubmission, ReviewTarget, ReviewerApplication, RunId, WorkflowRun,
13};
14use tokio::sync::RwLock;
15
16#[derive(Clone, Debug, Default)]
17pub struct FakeProvider {
18 pub(crate) state: Arc<RwLock<State>>,
19}
20
21#[derive(Debug, Default)]
22pub(crate) struct State {
23 pub(crate) repositories: BTreeMap<Repository, (RepositoryFacts, RepositorySettings)>,
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) branch_updates: BTreeMap<(Repository, ChangeRequestNumber), BranchUpdateObservation>,
29 pub(crate) accepted_branch_updates: Vec<(Repository, ChangeRequestNumber, String)>,
30 pub(crate) applied_requirements:
31 BTreeMap<FakeAppliedRequirementsKey, AppliedSourceRequirements>,
32 pub(crate) applied_requirement_errors: BTreeMap<FakeAppliedRequirementsKey, ProviderError>,
33 pub(crate) review_target_observations: Vec<(Repository, ReviewRequestTarget, ReviewTarget)>,
34 pub(crate) reviewer_applications: BTreeMap<(Repository, String), ReviewerApplication>,
35 pub(crate) review_publications: BTreeMap<FakeReviewPublicationKey, FakeReviewPublication>,
36 pub(crate) review_dismissals: Vec<FakeReviewDismissal>,
37 pub(crate) check_runs: BTreeMap<(Repository, String), Vec<CheckRun>>,
38 pub(crate) published_checks: Vec<(Repository, String, CheckOutcome)>,
39 pub(crate) dispatches: Vec<(Repository, String, String, DispatchInputs)>,
40 pub(crate) runs: BTreeMap<(Repository, RunId), WorkflowRun>,
41 pub(crate) cancelled_runs: Vec<(Repository, RunId)>,
42 pub(crate) releases: BTreeMap<(Repository, String), Release>,
43 pub(crate) assets: BTreeMap<(Repository, AssetId), Vec<Bytes>>,
44 pub(crate) next_release_id: u64,
45 pub(crate) next_asset_id: u64,
46}
47
48pub(crate) type FakeReviewPublicationKey = (
49 Repository,
50 ChangeRequestNumber,
51 ProviderAppId,
52 ReviewActorId,
53 ReviewPublicationKey,
54);
55
56pub(crate) type FakeAppliedRequirementsKey = (Repository, String, String, String);
57
58#[derive(Clone, Debug, Eq, PartialEq)]
60pub struct FakeReviewDismissal {
61 pub repository: Repository,
62 pub number: ChangeRequestNumber,
63 pub review_id: ReviewId,
64 pub message: ReviewDismissalMessage,
65}
66
67#[derive(Clone, Debug)]
68pub(crate) struct FakeReviewPublication {
69 pub(crate) submission: ReviewSubmission,
70 pub(crate) review_id: ReviewId,
71}
72
73impl FakeProvider {
74 #[must_use]
75 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub async fn seed_repository(&self, facts: RepositoryFacts, settings: RepositorySettings) {
80 self.state
81 .write()
82 .await
83 .repositories
84 .insert(facts.repository.clone(), (facts, settings));
85 }
86
87 pub async fn seed_issue(&self, repository: Repository, issue: Issue) {
88 self.state
89 .write()
90 .await
91 .issues
92 .insert((repository, issue.number), issue);
93 }
94
95 pub async fn seed_change_request(&self, repository: Repository, change_request: ChangeRequest) {
102 self.state
103 .write()
104 .await
105 .change_requests
106 .insert((repository, change_request.number), change_request);
107 }
108
109 pub async fn seed_branch_update(
115 &self,
116 repository: Repository,
117 number: ChangeRequestNumber,
118 observation: BranchUpdateObservation,
119 ) {
120 self.state
121 .write()
122 .await
123 .branch_updates
124 .insert((repository, number), observation);
125 }
126
127 pub async fn seed_applied_requirements(&self, observation: AppliedSourceRequirements) {
133 let range = observation.commit_range();
134 let key = (
135 observation.repository().clone(),
136 observation.target_branch().to_owned(),
137 range.base_sha.clone(),
138 range.head_sha.clone(),
139 );
140 let mut state = self.state.write().await;
141 state.applied_requirement_errors.remove(&key);
142 state.applied_requirements.insert(key, observation);
143 }
144
145 pub async fn seed_applied_requirements_error(
148 &self,
149 repository: Repository,
150 target_branch: impl Into<String>,
151 commit_range: CommitRange,
152 error: ProviderError,
153 ) {
154 let key = (
155 repository,
156 target_branch.into(),
157 commit_range.base_sha,
158 commit_range.head_sha,
159 );
160 let mut state = self.state.write().await;
161 state.applied_requirements.remove(&key);
162 state.applied_requirement_errors.insert(key, error);
163 }
164
165 pub async fn seed_review_request_target(
172 &self,
173 repository: Repository,
174 target: ReviewRequestTarget,
175 observed: ReviewTarget,
176 ) {
177 let mut state = self.state.write().await;
178 state
179 .review_target_observations
180 .retain(|(seeded_repository, seeded_target, _)| {
181 seeded_repository != &repository || seeded_target != &target
182 });
183 state
184 .review_target_observations
185 .push((repository, target, observed));
186 }
187
188 pub async fn seed_reviewer_application(
194 &self,
195 repository: Repository,
196 slug: String,
197 application: ReviewerApplication,
198 ) {
199 self.state
200 .write()
201 .await
202 .reviewer_applications
203 .insert((repository, slug), application);
204 }
205
206 pub async fn seed_check_runs(&self, repository: Repository, runs: Vec<CheckRun>) {
212 let mut state = self.state.write().await;
213 let mut replaced = BTreeSet::new();
214 for run in runs {
215 let key = (repository.clone(), run.head_sha.clone());
216 if replaced.insert(key.clone()) {
217 state.check_runs.insert(key.clone(), Vec::new());
218 }
219 state.check_runs.entry(key).or_default().push(run);
220 }
221 }
222
223 pub async fn seed_run(&self, repository: Repository, run: WorkflowRun) {
224 self.state
225 .write()
226 .await
227 .runs
228 .insert((repository, run.id), run);
229 }
230
231 pub async fn seed_release(&self, repository: Repository, release: Release) {
232 self.state
233 .write()
234 .await
235 .releases
236 .insert((repository, release.tag.clone()), release);
237 }
238
239 pub async fn published_checks(&self) -> Vec<(Repository, String, CheckOutcome)> {
240 self.state.read().await.published_checks.clone()
241 }
242
243 pub async fn review_dismissals(&self) -> Vec<FakeReviewDismissal> {
248 self.state.read().await.review_dismissals.clone()
249 }
250
251 pub async fn accepted_branch_updates(&self) -> Vec<(Repository, ChangeRequestNumber, String)> {
253 self.state.read().await.accepted_branch_updates.clone()
254 }
255
256 pub async fn dispatches(&self) -> Vec<(Repository, String, String, DispatchInputs)> {
257 self.state.read().await.dispatches.clone()
258 }
259}
260
261pub(crate) fn missing(entity: impl Into<String>) -> ProviderError {
262 ProviderError::NotFound {
263 entity: entity.into(),
264 }
265}