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, ReviewId, ReviewPublicationKey, ReviewRequestTarget, ReviewSubmission,
12 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) check_runs: BTreeMap<(Repository, String), Vec<CheckRun>>,
37 pub(crate) published_checks: Vec<(Repository, String, CheckOutcome)>,
38 pub(crate) dispatches: Vec<(Repository, String, String, DispatchInputs)>,
39 pub(crate) runs: BTreeMap<(Repository, RunId), WorkflowRun>,
40 pub(crate) cancelled_runs: Vec<(Repository, RunId)>,
41 pub(crate) releases: BTreeMap<(Repository, String), Release>,
42 pub(crate) assets: BTreeMap<(Repository, AssetId), Vec<Bytes>>,
43 pub(crate) next_release_id: u64,
44 pub(crate) next_asset_id: u64,
45}
46
47pub(crate) type FakeReviewPublicationKey = (
48 Repository,
49 ChangeRequestNumber,
50 ProviderAppId,
51 ReviewActorId,
52 ReviewPublicationKey,
53);
54
55pub(crate) type FakeAppliedRequirementsKey = (Repository, String, String, String);
56
57#[derive(Clone, Debug)]
58pub(crate) struct FakeReviewPublication {
59 pub(crate) submission: ReviewSubmission,
60 pub(crate) review_id: ReviewId,
61}
62
63impl FakeProvider {
64 #[must_use]
65 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub async fn seed_repository(&self, facts: RepositoryFacts, settings: RepositorySettings) {
70 self.state
71 .write()
72 .await
73 .repositories
74 .insert(facts.repository.clone(), (facts, settings));
75 }
76
77 pub async fn seed_issue(&self, repository: Repository, issue: Issue) {
78 self.state
79 .write()
80 .await
81 .issues
82 .insert((repository, issue.number), issue);
83 }
84
85 pub async fn seed_change_request(&self, repository: Repository, change_request: ChangeRequest) {
92 self.state
93 .write()
94 .await
95 .change_requests
96 .insert((repository, change_request.number), change_request);
97 }
98
99 pub async fn seed_branch_update(
105 &self,
106 repository: Repository,
107 number: ChangeRequestNumber,
108 observation: BranchUpdateObservation,
109 ) {
110 self.state
111 .write()
112 .await
113 .branch_updates
114 .insert((repository, number), observation);
115 }
116
117 pub async fn seed_applied_requirements(&self, observation: AppliedSourceRequirements) {
123 let range = observation.commit_range();
124 let key = (
125 observation.repository().clone(),
126 observation.target_branch().to_owned(),
127 range.base_sha.clone(),
128 range.head_sha.clone(),
129 );
130 let mut state = self.state.write().await;
131 state.applied_requirement_errors.remove(&key);
132 state.applied_requirements.insert(key, observation);
133 }
134
135 pub async fn seed_applied_requirements_error(
138 &self,
139 repository: Repository,
140 target_branch: impl Into<String>,
141 commit_range: CommitRange,
142 error: ProviderError,
143 ) {
144 let key = (
145 repository,
146 target_branch.into(),
147 commit_range.base_sha,
148 commit_range.head_sha,
149 );
150 let mut state = self.state.write().await;
151 state.applied_requirements.remove(&key);
152 state.applied_requirement_errors.insert(key, error);
153 }
154
155 pub async fn seed_review_request_target(
162 &self,
163 repository: Repository,
164 target: ReviewRequestTarget,
165 observed: ReviewTarget,
166 ) {
167 let mut state = self.state.write().await;
168 state
169 .review_target_observations
170 .retain(|(seeded_repository, seeded_target, _)| {
171 seeded_repository != &repository || seeded_target != &target
172 });
173 state
174 .review_target_observations
175 .push((repository, target, observed));
176 }
177
178 pub async fn seed_reviewer_application(
184 &self,
185 repository: Repository,
186 slug: String,
187 application: ReviewerApplication,
188 ) {
189 self.state
190 .write()
191 .await
192 .reviewer_applications
193 .insert((repository, slug), application);
194 }
195
196 pub async fn seed_check_runs(&self, repository: Repository, runs: Vec<CheckRun>) {
202 let mut state = self.state.write().await;
203 let mut replaced = BTreeSet::new();
204 for run in runs {
205 let key = (repository.clone(), run.head_sha.clone());
206 if replaced.insert(key.clone()) {
207 state.check_runs.insert(key.clone(), Vec::new());
208 }
209 state.check_runs.entry(key).or_default().push(run);
210 }
211 }
212
213 pub async fn seed_run(&self, repository: Repository, run: WorkflowRun) {
214 self.state
215 .write()
216 .await
217 .runs
218 .insert((repository, run.id), run);
219 }
220
221 pub async fn seed_release(&self, repository: Repository, release: Release) {
222 self.state
223 .write()
224 .await
225 .releases
226 .insert((repository, release.tag.clone()), release);
227 }
228
229 pub async fn published_checks(&self) -> Vec<(Repository, String, CheckOutcome)> {
230 self.state.read().await.published_checks.clone()
231 }
232
233 pub async fn accepted_branch_updates(&self) -> Vec<(Repository, ChangeRequestNumber, String)> {
235 self.state.read().await.accepted_branch_updates.clone()
236 }
237
238 pub async fn dispatches(&self) -> Vec<(Repository, String, String, DispatchInputs)> {
239 self.state.read().await.dispatches.clone()
240 }
241}
242
243pub(crate) fn missing(entity: impl Into<String>) -> ProviderError {
244 ProviderError::NotFound {
245 entity: entity.into(),
246 }
247}