use std::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
};
use bytes::Bytes;
use interprex::{
AppliedSourceRequirements, AssetId, BranchUpdateObservation, ChangeRequest,
ChangeRequestNumber, CheckOutcome, CheckRun, CommitRange, DispatchInputs, Issue, IssueNumber,
Label, ProviderAppId, ProviderError, Release, Repository, RepositoryFacts, RepositorySettings,
ReviewActorId, ReviewId, ReviewPublicationKey, ReviewRequestTarget, ReviewSubmission,
ReviewTarget, ReviewerApplication, RunId, WorkflowRun,
};
use tokio::sync::RwLock;
#[derive(Clone, Debug, Default)]
pub struct FakeProvider {
pub(crate) state: Arc<RwLock<State>>,
}
#[derive(Debug, Default)]
pub(crate) struct State {
pub(crate) repositories: BTreeMap<Repository, (RepositoryFacts, RepositorySettings)>,
pub(crate) secret_names: BTreeMap<Repository, Vec<String>>,
pub(crate) issues: BTreeMap<(Repository, IssueNumber), Issue>,
pub(crate) labels: BTreeMap<Repository, Vec<Label>>,
pub(crate) change_requests: BTreeMap<(Repository, ChangeRequestNumber), ChangeRequest>,
pub(crate) branch_updates: BTreeMap<(Repository, ChangeRequestNumber), BranchUpdateObservation>,
pub(crate) accepted_branch_updates: Vec<(Repository, ChangeRequestNumber, String)>,
pub(crate) applied_requirements:
BTreeMap<FakeAppliedRequirementsKey, AppliedSourceRequirements>,
pub(crate) applied_requirement_errors: BTreeMap<FakeAppliedRequirementsKey, ProviderError>,
pub(crate) review_target_observations: Vec<(Repository, ReviewRequestTarget, ReviewTarget)>,
pub(crate) reviewer_applications: BTreeMap<(Repository, String), ReviewerApplication>,
pub(crate) review_publications: BTreeMap<FakeReviewPublicationKey, FakeReviewPublication>,
pub(crate) check_runs: BTreeMap<(Repository, String), Vec<CheckRun>>,
pub(crate) published_checks: Vec<(Repository, String, CheckOutcome)>,
pub(crate) dispatches: Vec<(Repository, String, String, DispatchInputs)>,
pub(crate) runs: BTreeMap<(Repository, RunId), WorkflowRun>,
pub(crate) cancelled_runs: Vec<(Repository, RunId)>,
pub(crate) releases: BTreeMap<(Repository, String), Release>,
pub(crate) assets: BTreeMap<(Repository, AssetId), Vec<Bytes>>,
pub(crate) next_release_id: u64,
pub(crate) next_asset_id: u64,
}
pub(crate) type FakeReviewPublicationKey = (
Repository,
ChangeRequestNumber,
ProviderAppId,
ReviewActorId,
ReviewPublicationKey,
);
pub(crate) type FakeAppliedRequirementsKey = (Repository, String, String, String);
#[derive(Clone, Debug)]
pub(crate) struct FakeReviewPublication {
pub(crate) submission: ReviewSubmission,
pub(crate) review_id: ReviewId,
}
impl FakeProvider {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub async fn seed_repository(&self, facts: RepositoryFacts, settings: RepositorySettings) {
self.state
.write()
.await
.repositories
.insert(facts.repository.clone(), (facts, settings));
}
pub async fn seed_issue(&self, repository: Repository, issue: Issue) {
self.state
.write()
.await
.issues
.insert((repository, issue.number), issue);
}
pub async fn seed_change_request(&self, repository: Repository, change_request: ChangeRequest) {
self.state
.write()
.await
.change_requests
.insert((repository, change_request.number), change_request);
}
pub async fn seed_branch_update(
&self,
repository: Repository,
number: ChangeRequestNumber,
observation: BranchUpdateObservation,
) {
self.state
.write()
.await
.branch_updates
.insert((repository, number), observation);
}
pub async fn seed_applied_requirements(&self, observation: AppliedSourceRequirements) {
let range = observation.commit_range();
let key = (
observation.repository().clone(),
observation.target_branch().to_owned(),
range.base_sha.clone(),
range.head_sha.clone(),
);
let mut state = self.state.write().await;
state.applied_requirement_errors.remove(&key);
state.applied_requirements.insert(key, observation);
}
pub async fn seed_applied_requirements_error(
&self,
repository: Repository,
target_branch: impl Into<String>,
commit_range: CommitRange,
error: ProviderError,
) {
let key = (
repository,
target_branch.into(),
commit_range.base_sha,
commit_range.head_sha,
);
let mut state = self.state.write().await;
state.applied_requirements.remove(&key);
state.applied_requirement_errors.insert(key, error);
}
pub async fn seed_review_request_target(
&self,
repository: Repository,
target: ReviewRequestTarget,
observed: ReviewTarget,
) {
let mut state = self.state.write().await;
state
.review_target_observations
.retain(|(seeded_repository, seeded_target, _)| {
seeded_repository != &repository || seeded_target != &target
});
state
.review_target_observations
.push((repository, target, observed));
}
pub async fn seed_reviewer_application(
&self,
repository: Repository,
slug: String,
application: ReviewerApplication,
) {
self.state
.write()
.await
.reviewer_applications
.insert((repository, slug), application);
}
pub async fn seed_check_runs(&self, repository: Repository, runs: Vec<CheckRun>) {
let mut state = self.state.write().await;
let mut replaced = BTreeSet::new();
for run in runs {
let key = (repository.clone(), run.head_sha.clone());
if replaced.insert(key.clone()) {
state.check_runs.insert(key.clone(), Vec::new());
}
state.check_runs.entry(key).or_default().push(run);
}
}
pub async fn seed_run(&self, repository: Repository, run: WorkflowRun) {
self.state
.write()
.await
.runs
.insert((repository, run.id), run);
}
pub async fn seed_release(&self, repository: Repository, release: Release) {
self.state
.write()
.await
.releases
.insert((repository, release.tag.clone()), release);
}
pub async fn published_checks(&self) -> Vec<(Repository, String, CheckOutcome)> {
self.state.read().await.published_checks.clone()
}
pub async fn accepted_branch_updates(&self) -> Vec<(Repository, ChangeRequestNumber, String)> {
self.state.read().await.accepted_branch_updates.clone()
}
pub async fn dispatches(&self) -> Vec<(Repository, String, String, DispatchInputs)> {
self.state.read().await.dispatches.clone()
}
}
pub(crate) fn missing(entity: impl Into<String>) -> ProviderError {
ProviderError::NotFound {
entity: entity.into(),
}
}