Compare commits
3 commits
63ff626eab
...
6d9eb0ab86
Author | SHA1 | Date | |
---|---|---|---|
6d9eb0ab86 | |||
f460cd4b49 | |||
e585b07f6b |
32 changed files with 798 additions and 247 deletions
|
@ -3,7 +3,7 @@ use crate as config;
|
|||
|
||||
use derive_more::Constructor;
|
||||
|
||||
#[derive(Debug, Constructor)]
|
||||
#[derive(Debug, Constructor, derive_with::With)]
|
||||
pub struct Push {
|
||||
branch: config::BranchName,
|
||||
sha: String,
|
||||
|
@ -30,7 +30,6 @@ impl Push {
|
|||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Branch {
|
||||
Main,
|
||||
|
|
|
@ -27,18 +27,6 @@ impl Forge {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for Forge {
|
||||
type Target = dyn git::ForgeLike;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Mock => unreachable!(),
|
||||
#[cfg(feature = "forgejo")]
|
||||
Self::ForgeJo(env) => env,
|
||||
#[cfg(feature = "github")]
|
||||
Self::Github(env) => env,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
|
|
@ -34,20 +34,10 @@ pub enum Repository {
|
|||
Test(TestRepository),
|
||||
}
|
||||
|
||||
#[deprecated(note = "use git::repository::real()")]
|
||||
pub const fn new() -> Repository {
|
||||
Repository::Real
|
||||
}
|
||||
|
||||
pub const fn test(fs: kxio::fs::FileSystem) -> TestRepository {
|
||||
TestRepository::new(false, fs, vec![], vec![])
|
||||
TestRepository::new(fs, vec![], vec![])
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// pub const fn test_bare(fs: kxio::fs::FileSystem) -> TestRepository {
|
||||
// TestRepository::new(true, fs, vec![], vec![])
|
||||
// }
|
||||
|
||||
/// Opens a repository, cloning if necessary
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
|
||||
|
@ -86,6 +76,8 @@ pub fn mock() -> Box<MockRepositoryFactory> {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
struct RealRepositoryFactory;
|
||||
|
||||
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
|
||||
impl RepositoryFactory for RealRepositoryFactory {
|
||||
fn open(&self, gitdir: &GitDir) -> Result<Box<dyn OpenRepositoryLike>> {
|
||||
let gix_repo = gix::ThreadSafeRepository::open(gitdir.to_path_buf())?.to_thread_local();
|
||||
|
@ -115,16 +107,16 @@ pub trait RepositoryLike {
|
|||
fn open(&self, gitdir: &GitDir) -> Result<OpenRepository>;
|
||||
fn git_clone(&self, repo_details: &RepoDetails) -> Result<OpenRepository>;
|
||||
}
|
||||
impl std::ops::Deref for Repository {
|
||||
type Target = dyn RepositoryLike;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Real => &real::RealRepository,
|
||||
Self::Test(test_repository) => test_repository,
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl std::ops::Deref for Repository {
|
||||
// type Target = dyn RepositoryLike;
|
||||
//
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// match self {
|
||||
// Self::Real => &real::RealRepository,
|
||||
// Self::Test(test_repository) => test_repository,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
//
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod oreal;
|
||||
|
||||
pub mod otest;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -39,6 +37,7 @@ pub enum OpenRepository {
|
|||
Test(TestOpenRepository),
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn real(gix_repo: gix::Repository) -> OpenRepository {
|
||||
OpenRepository::Real(oreal::RealOpenRepository::new(Arc::new(Mutex::new(
|
||||
gix_repo,
|
||||
|
@ -55,16 +54,6 @@ pub fn test(
|
|||
OpenRepository::Test(TestOpenRepository::new(gitdir, fs, on_fetch, on_push))
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))] // don't test mocks
|
||||
pub fn test_bare(
|
||||
gitdir: &config::GitDir,
|
||||
fs: kxio::fs::FileSystem,
|
||||
on_fetch: Vec<otest::OnFetch>,
|
||||
on_push: Vec<otest::OnPush>,
|
||||
) -> OpenRepository {
|
||||
OpenRepository::Test(TestOpenRepository::new_bare(gitdir, fs, on_fetch, on_push))
|
||||
}
|
||||
|
||||
#[mockall::automock]
|
||||
pub trait OpenRepositoryLike: std::fmt::Debug + Sync {
|
||||
fn duplicate(&self) -> Box<dyn OpenRepositoryLike>;
|
||||
|
|
|
@ -156,24 +156,6 @@ impl TestOpenRepository {
|
|||
real: git::repository::RealOpenRepository::new(Arc::new(Mutex::new(gix))),
|
||||
}
|
||||
}
|
||||
pub fn new_bare(
|
||||
gitdir: &config::GitDir,
|
||||
fs: kxio::fs::FileSystem,
|
||||
on_fetch: Vec<OnFetch>,
|
||||
on_push: Vec<OnPush>,
|
||||
) -> Self {
|
||||
let pathbuf = fs.base().join(gitdir.to_path_buf());
|
||||
#[allow(clippy::expect_used)]
|
||||
let gix = gix::init_bare(pathbuf).expect("git init bare");
|
||||
Self::write_origin(gitdir, &fs);
|
||||
Self {
|
||||
on_fetch,
|
||||
fetch_counter: Arc::new(RwLock::new(0)),
|
||||
on_push,
|
||||
push_counter: Arc::new(RwLock::new(0)),
|
||||
real: git::repository::RealOpenRepository::new(Arc::new(Mutex::new(gix))),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_origin(gitdir: &config::GitDir, fs: &kxio::fs::FileSystem) {
|
||||
let config_file = fs.base().join(gitdir.to_path_buf()).join(".git/config");
|
||||
|
|
|
@ -287,27 +287,6 @@ mod forge_config {
|
|||
}
|
||||
}
|
||||
|
||||
// mod remote_branches {
|
||||
// use super::*;
|
||||
// #[test]
|
||||
// // assumes running in the git-next repo which should have main, next and dev as remote branches
|
||||
// fn should_return_remote_branches() -> TestResult {
|
||||
// let_assert!(Ok(fs) = kxio::fs::temp());
|
||||
// let gitdir: config::GitDir = fs.base().to_path_buf().into();
|
||||
// let test_repository = git::repository::test(fs.clone());
|
||||
// let_assert!(Ok(open_repository) = test_repository.open(&gitdir));
|
||||
// let repo_config = &given::a_repo_config();
|
||||
// let branches = repo_config.branches();
|
||||
// then::create_a_commit_on_branch(&fs, &gitdir, &branches.main())?;
|
||||
// then::create_a_commit_on_branch(&fs, &gitdir, &branches.next())?;
|
||||
// then::create_a_commit_on_branch(&fs, &gitdir, &branches.dev())?;
|
||||
// let_assert!(Ok(remote_branches) = open_repository.remote_branches());
|
||||
// assert!(remote_branches.contains(&branches.main()));
|
||||
// assert!(remote_branches.contains(&branches.next()));
|
||||
// assert!(remote_branches.contains(&branches.dev()));
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
mod find_default_remote {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//
|
||||
#![cfg(not(tarpaulin_include))]
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use crate as git;
|
||||
|
@ -13,7 +14,6 @@ impl git::repository::RepositoryLike for RealRepository {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[cfg(not(tarpaulin_include))] // requires external server
|
||||
fn git_clone(
|
||||
&self,
|
||||
repo_details: &git::RepoDetails,
|
||||
|
|
|
@ -7,7 +7,6 @@ use git_next_config as config;
|
|||
|
||||
#[derive(Clone, Debug, Constructor)]
|
||||
pub struct TestRepository {
|
||||
is_bare: bool,
|
||||
fs: kxio::fs::FileSystem,
|
||||
on_fetch: Vec<git::repository::open::otest::OnFetch>,
|
||||
on_push: Vec<git::repository::open::otest::OnPush>,
|
||||
|
@ -20,28 +19,15 @@ impl TestRepository {
|
|||
pub fn on_push(&mut self, on_push: git::repository::OnPush) {
|
||||
self.on_push.push(on_push);
|
||||
}
|
||||
|
||||
pub const fn fs(&self) -> &kxio::fs::FileSystem {
|
||||
&self.fs
|
||||
}
|
||||
}
|
||||
impl RepositoryLike for TestRepository {
|
||||
fn open(&self, gitdir: &config::GitDir) -> super::Result<crate::OpenRepository> {
|
||||
if self.is_bare {
|
||||
Ok(git::repository::open::test_bare(
|
||||
gitdir,
|
||||
self.fs.clone(),
|
||||
self.on_fetch.clone(),
|
||||
self.on_push.clone(),
|
||||
))
|
||||
} else {
|
||||
Ok(git::repository::open::test(
|
||||
gitdir,
|
||||
self.fs.clone(),
|
||||
self.on_fetch.clone(),
|
||||
self.on_push.clone(),
|
||||
))
|
||||
}
|
||||
Ok(git::repository::open::test(
|
||||
gitdir,
|
||||
self.fs.clone(),
|
||||
self.on_fetch.clone(),
|
||||
self.on_push.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn git_clone(
|
||||
|
|
|
@ -51,7 +51,16 @@ pub fn validate_positions(
|
|||
});
|
||||
}
|
||||
// verify that next is on main or at most one commit on top of main, else reset it back to main
|
||||
if is_not_based_on(&commit_histories.next[0..=1], &main) {
|
||||
if is_not_based_on(
|
||||
commit_histories
|
||||
.next
|
||||
.iter()
|
||||
.take(2)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
&main,
|
||||
) {
|
||||
tracing::info!("Main not on same commit as next, or it's parent - resetting next to main",);
|
||||
return reset_next_to_main(open_repository, repo_details, &main, &next, &next_branch);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ ulid = { workspace = true }
|
|||
|
||||
# boilerplate
|
||||
derive_more = { workspace = true }
|
||||
derive-with = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
# Actors
|
||||
|
|
|
@ -14,10 +14,12 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
#[tracing::instrument(name = "RepoActor::WebhookMessage", skip_all, fields(token = %self.message_token, repo = %self.repo_details))]
|
||||
fn handle(&mut self, msg: WebhookNotification, ctx: &mut Self::Context) -> Self::Result {
|
||||
let Some(expected_authorization) = &self.webhook_auth else {
|
||||
actor::logger(&self.log, "server has no auth token");
|
||||
warn!("Don't know what authorization to expect");
|
||||
return;
|
||||
};
|
||||
let Some(config) = &self.repo_details.repo_config else {
|
||||
actor::logger(&self.log, "server has no repo config");
|
||||
warn!("No repo config");
|
||||
return;
|
||||
};
|
||||
|
@ -25,6 +27,7 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
.forge
|
||||
.is_message_authorised(&msg, expected_authorization)
|
||||
{
|
||||
actor::logger(&self.log, "message authorisation is invalid");
|
||||
warn!(
|
||||
"Invalid authorization - expected {}",
|
||||
expected_authorization
|
||||
|
@ -34,11 +37,13 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
let body = msg.body();
|
||||
match self.forge.parse_webhook_body(body) {
|
||||
Err(err) => {
|
||||
actor::logger(&self.log, "message parse error - not a push");
|
||||
warn!(?err, "Not a 'push'");
|
||||
return;
|
||||
}
|
||||
Ok(push) => match push.branch(config.branches()) {
|
||||
None => {
|
||||
actor::logger(&self.log, "unknown branch");
|
||||
warn!(
|
||||
?push,
|
||||
"Unrecognised branch, we should be filtering to only the ones we want"
|
||||
|
@ -46,8 +51,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
return;
|
||||
}
|
||||
Some(config::webhook::push::Branch::Main) => {
|
||||
actor::logger(&self.log, "message is for main branch");
|
||||
let commit = git::Commit::from(push);
|
||||
if self.last_main_commit.as_ref() == Some(&commit) {
|
||||
actor::logger(&self.log, "not a new commit on main");
|
||||
info!(
|
||||
branch = %config.branches().main(),
|
||||
%commit,
|
||||
|
@ -58,8 +65,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
self.last_main_commit.replace(commit);
|
||||
}
|
||||
Some(config::webhook::push::Branch::Next) => {
|
||||
actor::logger(&self.log, "message is for next branch");
|
||||
let commit = git::Commit::from(push);
|
||||
if self.last_next_commit.as_ref() == Some(&commit) {
|
||||
actor::logger(&self.log, "not a new commit on next");
|
||||
info!(
|
||||
branch = %config.branches().next(),
|
||||
%commit,
|
||||
|
@ -70,8 +79,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
self.last_next_commit.replace(commit);
|
||||
}
|
||||
Some(config::webhook::push::Branch::Dev) => {
|
||||
actor::logger(&self.log, "message is for dev branch");
|
||||
let commit = git::Commit::from(push);
|
||||
if self.last_dev_commit.as_ref() == Some(&commit) {
|
||||
actor::logger(&self.log, "not a new commit on dev");
|
||||
info!(
|
||||
branch = %config.branches().dev(),
|
||||
%commit,
|
||||
|
@ -88,7 +99,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
|||
token = %message_token,
|
||||
"New commit"
|
||||
);
|
||||
ctx.address()
|
||||
.do_send(actor::messages::ValidateRepo::new(message_token));
|
||||
actor::do_send(
|
||||
ctx.address(),
|
||||
actor::messages::ValidateRepo::new(message_token),
|
||||
&self.log,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,22 @@ mod tests;
|
|||
|
||||
use actix::prelude::*;
|
||||
|
||||
use derive_more::Deref;
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
|
||||
use kxio::network::Network;
|
||||
use tracing::{info, warn, Instrument};
|
||||
|
||||
pub type RepoActorLog = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct RepoActorLog(std::sync::Arc<std::sync::Mutex<Vec<String>>>);
|
||||
impl Deref for RepoActorLog {
|
||||
type Target = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An actor that represents a Git Repository.
|
||||
///
|
||||
|
@ -48,7 +57,7 @@ pub type RepoActorLog = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
|
|||
/// WebhookMessage --> ValidateRepo
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, derive_more::Display)]
|
||||
#[derive(Debug, derive_more::Display, derive_with::With)]
|
||||
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
|
||||
pub struct RepoActor {
|
||||
sleep_duration: std::time::Duration,
|
||||
|
@ -97,27 +106,6 @@ impl RepoActor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl RepoActor {
|
||||
pub fn with_log(mut self, log: RepoActorLog) -> Self {
|
||||
self.log = Some(log);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_open_repository(
|
||||
mut self,
|
||||
open_repository: Box<dyn git::repository::OpenRepositoryLike>,
|
||||
) -> Self {
|
||||
self.open_repository.replace(open_repository);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_message_token(mut self, message_token: messages::MessageToken) -> Self {
|
||||
self.message_token = message_token;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Actor for RepoActor {
|
||||
type Context = Context<Self>;
|
||||
#[tracing::instrument(name = "RepoActor::stopping", skip_all, fields(repo = %self.repo_details))]
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
//
|
||||
#![allow(unused_imports)] // TODO remove this
|
||||
|
||||
use crate as actor;
|
||||
use actix::prelude::*;
|
||||
use actor::{
|
||||
messages::{CloneRepo, MessageToken, WebhookRegistered},
|
||||
RepoActor, RepoActorLog,
|
||||
};
|
||||
use assert2::let_assert;
|
||||
use config::{BranchName, RegisteredWebhook, RepoBranches, RepoConfig};
|
||||
use git::{
|
||||
repository::{
|
||||
open::MockOpenRepositoryLike, Direction, MockRepositoryFactory, RepositoryFactory,
|
||||
},
|
||||
validation::remotes,
|
||||
GitRemote, RepoDetails,
|
||||
};
|
||||
use git_next_config as config;
|
||||
use git_next_forge as forge;
|
||||
use git_next_git as git;
|
||||
use mockall::predicate::eq;
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{atomic::AtomicBool, Arc, Mutex},
|
||||
};
|
||||
|
||||
type TestResult = Result<(), Box<dyn std::error::Error>>;
|
||||
|
||||
mod branch;
|
||||
mod expect;
|
||||
pub mod given;
|
||||
mod handlers;
|
||||
mod load;
|
||||
mod when;
|
|
@ -1,6 +1,4 @@
|
|||
//
|
||||
use git::repository::{OnFetch, OnPush, OpenRepositoryLike};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
use super::*;
|
||||
|
||||
mod when_at_dev {
|
||||
// next and dev branches are the same
|
||||
use super::*;
|
||||
|
@ -27,12 +28,14 @@ mod when_at_dev {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod can_advance {
|
||||
// dev has at least one commit ahead of next
|
||||
use super::*;
|
||||
mod to_wip_commit {
|
||||
// commit on dev is either invalid message or a WIP
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_not_push() -> TestResult {
|
||||
let next = given::a_commit();
|
||||
|
@ -58,9 +61,11 @@ mod can_advance {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod to_invalid_commit {
|
||||
// commit on dev is either invalid message or a WIP
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_not_push_and_error() -> TestResult {
|
||||
let next = given::a_commit();
|
||||
|
@ -90,13 +95,12 @@ mod can_advance {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod to_valid_commit {
|
||||
// commit on dev is valid conventional commit message
|
||||
use super::*;
|
||||
|
||||
mod push_is_err {
|
||||
use git::repository::{OnFetch, OnPush};
|
||||
|
||||
// the git push command fails
|
||||
use super::*;
|
||||
|
||||
|
@ -129,12 +133,11 @@ mod can_advance {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod push_is_ok {
|
||||
// the git push command succeeds
|
||||
use git_next_git::repository::{OnFetch, OnPush};
|
||||
|
||||
// the git push command fails
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_ok() -> TestResult {
|
||||
let next = given::a_commit();
|
||||
|
|
|
@ -1,22 +1,5 @@
|
|||
//
|
||||
use super::*;
|
||||
use config::{
|
||||
server::{Webhook, WebhookUrl},
|
||||
BranchName, ForgeAlias, ForgeConfig, ForgeType, GitDir, RepoAlias, RepoBranches, RepoConfig,
|
||||
ServerRepoConfig, WebhookAuth, WebhookId,
|
||||
};
|
||||
use git::{
|
||||
repository::{open::MockOpenRepositoryLike, Direction},
|
||||
Generation, GitRemote, MockForgeLike, RepoDetails,
|
||||
};
|
||||
use git_next_git::repository::RepositoryLike as _;
|
||||
use mockall::predicate::eq;
|
||||
use rand::RngCore;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
pub fn has_all_valid_remote_defaults(
|
||||
open_repository: &mut MockOpenRepositoryLike,
|
||||
|
@ -252,7 +235,7 @@ pub fn a_repo_actor(
|
|||
let webhook_url = given::a_webhook_url(forge_alias, repo_alias);
|
||||
let webhook = given::a_webhook(&webhook_url);
|
||||
let generation = Generation::default();
|
||||
let log = Arc::new(Mutex::new(vec![]));
|
||||
let log = RepoActorLog::default();
|
||||
let actors_log = log.clone();
|
||||
(
|
||||
actor::RepoActor::new(
|
||||
|
@ -276,3 +259,11 @@ pub fn a_hostname() -> config::Hostname {
|
|||
pub fn a_registered_webhook() -> RegisteredWebhook {
|
||||
RegisteredWebhook::new(given::a_webhook_id(), given::a_webhook_auth())
|
||||
}
|
||||
|
||||
pub fn a_push() -> config::webhook::Push {
|
||||
config::webhook::Push::new(
|
||||
given::a_branch_name("push"),
|
||||
given::a_name(),
|
||||
given::a_name(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -27,8 +27,11 @@ async fn when_repo_config_should_fetch_then_push_then_revalidate() -> TestResult
|
|||
.return_once(|_, _, _, _| Ok(()));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -69,8 +72,11 @@ async fn when_server_config_should_fetch_then_push_then_revalidate() -> TestResu
|
|||
.return_once(|_, _, _, _| Ok(()));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
|
|
@ -26,8 +26,11 @@ async fn should_fetch_then_push_then_revalidate() -> TestResult {
|
|||
.return_once(|_, _, _, _| Ok(()));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::AdvanceNext::new((
|
||||
next_commit.clone(),
|
||||
dev_commit_log,
|
||||
|
|
|
@ -15,8 +15,11 @@ async fn should_passthrough_to_receive_ci_status() -> TestResult {
|
|||
);
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
Box::new(forge),
|
||||
);
|
||||
addr.send(actor::messages::CheckCIStatus::new(next_commit.clone()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
|
|
@ -34,8 +34,11 @@ async fn when_read_file_ok_should_send_config_loaded() -> TestResult {
|
|||
.return_once(|| Box::new(load_config_open_repo));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::LoadConfigFromRepo::new())
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -66,8 +69,11 @@ async fn when_read_file_err_should_notify_user() -> TestResult {
|
|||
.return_once(|| Box::new(load_config_open_repo));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::LoadConfigFromRepo::new())
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
use super::*;
|
||||
use actix::prelude::*;
|
||||
|
||||
#[actix::test]
|
||||
async fn should_store_repo_config_in_actor() -> TestResult {
|
||||
|
@ -11,8 +10,11 @@ async fn should_store_repo_config_in_actor() -> TestResult {
|
|||
let new_repo_config = given::a_repo_config();
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||
new_repo_config.clone(),
|
||||
))
|
||||
|
@ -36,8 +38,11 @@ async fn should_validate_repo() -> TestResult {
|
|||
let new_repo_config = given::a_repo_config();
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||
new_repo_config.clone(),
|
||||
))
|
||||
|
@ -63,8 +68,11 @@ async fn should_register_webhook() -> TestResult {
|
|||
let new_repo_config = given::a_repo_config();
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||
new_repo_config.clone(),
|
||||
))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
use super::*;
|
||||
use actix::prelude::*;
|
||||
|
||||
mod advance_main;
|
||||
mod advance_next;
|
||||
|
@ -11,3 +10,5 @@ mod loaded_config;
|
|||
mod receive_ci_status;
|
||||
mod register_webhook;
|
||||
mod validate_repo;
|
||||
mod webhook_notification;
|
||||
mod webhook_registered;
|
|
@ -9,8 +9,11 @@ async fn when_pass_should_advance_main_to_next() -> TestResult {
|
|||
let next_commit = given::a_named_commit("next");
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||
next_commit.clone(),
|
||||
git::forge::commit::Status::Pass,
|
||||
|
@ -36,8 +39,11 @@ async fn when_pending_should_recheck_ci_status() -> TestResult {
|
|||
let next_commit = given::a_named_commit("next");
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||
next_commit.clone(),
|
||||
git::forge::commit::Status::Pending,
|
||||
|
@ -64,8 +70,11 @@ async fn when_fail_should_notify_user() -> TestResult {
|
|||
let next_commit = given::a_named_commit("next");
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||
next_commit.clone(),
|
||||
git::forge::commit::Status::Fail,
|
||||
|
|
|
@ -17,8 +17,11 @@ async fn when_registered_ok_should_send_webhook_registered() -> TestResult {
|
|||
forge.expect_duplicate().return_once(|| Box::new(my_forge));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
Box::new(forge),
|
||||
);
|
||||
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
||||
System::current().stop();
|
||||
|
||||
|
@ -50,8 +53,11 @@ async fn when_registered_error_should_send_notify_user() -> TestResult {
|
|||
forge.expect_duplicate().return_once(|| Box::new(my_forge));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
Box::new(forge),
|
||||
);
|
||||
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
||||
System::current().stop();
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//
|
||||
use crate::messages::{MessageToken, ValidateRepo};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test_log::test(actix::test)]
|
||||
|
@ -35,8 +33,11 @@ async fn repo_with_next_not_an_ancestor_of_dev_should_be_reset() -> TestResult {
|
|||
expect::push_ok(&mut open_repository);
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -83,8 +84,11 @@ async fn repo_with_next_not_on_or_near_main_should_be_reset() -> TestResult {
|
|||
expect::push_ok(&mut open_repository);
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -131,8 +135,11 @@ async fn repo_with_next_not_based_on_main_should_be_reset() -> TestResult {
|
|||
expect::push_ok(&mut open_repository);
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -178,8 +185,11 @@ async fn repo_with_next_ahead_of_main_should_check_ci_status() -> TestResult {
|
|||
.return_once(|_, _| Ok(dev_branch_log));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -225,8 +235,11 @@ async fn repo_with_dev_and_next_on_main_should_do_nothing() -> TestResult {
|
|||
.return_once(move |_, _| Ok(dev_commit_log));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
@ -272,8 +285,11 @@ async fn repo_with_dev_ahead_of_next_should_advance_next() -> TestResult {
|
|||
.return_once(move |_, _| Ok(dev_commit_log));
|
||||
|
||||
//when
|
||||
let (addr, log) =
|
||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
||||
let (addr, log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
|
478
crates/repo-actor/src/tests/handlers/webhook_notification.rs
Normal file
478
crates/repo-actor/src/tests/handlers/webhook_notification.rs
Normal file
|
@ -0,0 +1,478 @@
|
|||
//
|
||||
use super::*;
|
||||
|
||||
#[actix::test]
|
||||
async fn when_no_expected_auth_token_drop_notification() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_details = given::repo_details(&fs);
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
given::a_forge(),
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor.with_webhook_auth(None);
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("server has no auth token")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_no_repo_config_drop_notification() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(None);
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
given::a_forge(),
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor.with_webhook_auth(Some(given::a_webhook_auth()));
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("server has no repo config")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_auth_is_invalid_drop_notification() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_details = given::repo_details(&fs);
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| false); // is not valid
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor.with_webhook_auth(Some(given::a_webhook_auth()));
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("message authorisation is invalid")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_not_a_push_drop_notification() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_details = given::repo_details(&fs);
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge
|
||||
.expect_parse_webhook_body()
|
||||
.return_once(|_| Err(git::forge::webhook::Error::NetworkResponseEmpty));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor.with_webhook_auth(Some(given::a_webhook_auth()));
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("message parse error - not a push")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_on_unknown_branch_drop_notification() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(given::a_branch_name("unknown"))
|
||||
.with_sha(commit.sha().to_string())
|
||||
.with_message(commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_main_commit(commit);
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("unknown branch")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_already_seen_commit_to_main() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().main())
|
||||
.with_sha(commit.sha().to_string())
|
||||
.with_message(commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_main_commit(commit);
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("not a new commit on main")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_already_seen_commit_to_next() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().next())
|
||||
.with_sha(commit.sha().to_string())
|
||||
.with_message(commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_next_commit(commit);
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("not a new commit on next")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_already_seen_commit_to_dev() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().dev())
|
||||
.with_sha(commit.sha().to_string())
|
||||
.with_message(commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_dev_commit(commit);
|
||||
|
||||
//when
|
||||
actor
|
||||
.start()
|
||||
.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
log.no_message_contains("send")?;
|
||||
log.require_message_containing("not a new commit on dev")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_new_commit_to_main_should_stash_and_validate_repo() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let push_commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().main())
|
||||
.with_sha(push_commit.sha().to_string())
|
||||
.with_message(push_commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_main_commit(given::a_commit());
|
||||
|
||||
//when
|
||||
let addr = actor.start();
|
||||
addr.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
let view = addr.send(ExamineActor).await?;
|
||||
assert_eq!(view.last_main_commit, Some(push_commit));
|
||||
log.require_message_containing("send: ValidateRepo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_new_commit_to_next_should_stash_and_validate_repo() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let push_commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().next())
|
||||
.with_sha(push_commit.sha().to_string())
|
||||
.with_message(push_commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_next_commit(given::a_commit());
|
||||
|
||||
//when
|
||||
let addr = actor.start();
|
||||
addr.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
let view = addr.send(ExamineActor).await?;
|
||||
assert_eq!(view.last_next_commit, Some(push_commit));
|
||||
log.require_message_containing("send: ValidateRepo")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn when_message_is_push_new_commit_to_dev_should_stash_and_validate_repo() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let repo_config = given::a_repo_config();
|
||||
let repo_details = given::repo_details(&fs).with_repo_config(Some(repo_config.clone()));
|
||||
let forge_alias = given::a_forge_alias();
|
||||
let repo_alias = given::a_repo_alias();
|
||||
let headers = BTreeMap::new();
|
||||
let body = Body::new("".to_string());
|
||||
let forge_notification = ForgeNotification::new(forge_alias, repo_alias, headers, body);
|
||||
let repository_factory = MockRepositoryFactory::new();
|
||||
let push_commit = given::a_commit();
|
||||
let push = given::a_push()
|
||||
.with_branch(repo_config.branches().dev())
|
||||
.with_sha(push_commit.sha().to_string())
|
||||
.with_message(push_commit.message().to_string());
|
||||
let mut forge = given::a_forge();
|
||||
forge
|
||||
.expect_is_message_authorised()
|
||||
.return_once(|_, _| true); // is valid
|
||||
forge.expect_parse_webhook_body().return_once(|_| Ok(push));
|
||||
let (actor, log) = given::a_repo_actor(
|
||||
repo_details,
|
||||
Box::new(repository_factory),
|
||||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor
|
||||
.with_webhook_auth(Some(given::a_webhook_auth()))
|
||||
.with_last_dev_commit(given::a_commit());
|
||||
|
||||
//when
|
||||
let addr = actor.start();
|
||||
addr.send(actor::messages::WebhookNotification::new(
|
||||
forge_notification,
|
||||
))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
let view = addr.send(ExamineActor).await?;
|
||||
assert_eq!(view.last_dev_commit, Some(push_commit));
|
||||
log.require_message_containing("send: ValidateRepo")?;
|
||||
Ok(())
|
||||
}
|
30
crates/repo-actor/src/tests/handlers/webhook_registered.rs
Normal file
30
crates/repo-actor/src/tests/handlers/webhook_registered.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
use super::*;
|
||||
|
||||
#[actix::test]
|
||||
async fn should_store_webhook_details() -> TestResult {
|
||||
//given
|
||||
let fs = given::a_filesystem();
|
||||
let (open_repository, repo_details) = given::an_open_repository(&fs);
|
||||
let webhook_id = given::a_webhook_id();
|
||||
let webhook_auth = given::a_webhook_auth();
|
||||
|
||||
//when
|
||||
let (addr, _log) = when::start_actor_with_open_repository(
|
||||
Box::new(open_repository),
|
||||
repo_details,
|
||||
given::a_forge(),
|
||||
);
|
||||
addr.send(actor::messages::WebhookRegistered::new((
|
||||
webhook_id.clone(),
|
||||
webhook_auth.clone(),
|
||||
)))
|
||||
.await?;
|
||||
System::current().stop();
|
||||
|
||||
//then
|
||||
let view = addr.send(ExamineActor).await?;
|
||||
assert_eq!(view.webhook_id, Some(webhook_id));
|
||||
assert_eq!(view.webhook_auth, Some(webhook_auth));
|
||||
Ok(())
|
||||
}
|
|
@ -1,8 +1,4 @@
|
|||
//
|
||||
use std::path::PathBuf;
|
||||
|
||||
use git_next_git::common::repo_details;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
|
|
109
crates/repo-actor/src/tests/mod.rs
Normal file
109
crates/repo-actor/src/tests/mod.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
use crate::{self as actor, message};
|
||||
use actix::prelude::*;
|
||||
use actor::{
|
||||
messages::{CloneRepo, MessageToken},
|
||||
RepoActor, RepoActorLog,
|
||||
};
|
||||
use assert2::let_assert;
|
||||
use config::{
|
||||
server::{Webhook, WebhookUrl},
|
||||
webhook::forge_notification::Body,
|
||||
BranchName, ForgeAlias, ForgeConfig, ForgeNotification, ForgeType, GitDir, RegisteredWebhook,
|
||||
RepoAlias, RepoBranches, RepoConfig, ServerRepoConfig, WebhookAuth, WebhookId,
|
||||
};
|
||||
use git::{
|
||||
repository::{open::MockOpenRepositoryLike, Direction, MockRepositoryFactory},
|
||||
Generation, GitRemote, MockForgeLike, RepoDetails,
|
||||
};
|
||||
use git_next_config as config;
|
||||
use git_next_git as git;
|
||||
use mockall::predicate::eq;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
type TestResult = Result<(), Box<dyn std::error::Error>>;
|
||||
|
||||
mod branch;
|
||||
mod expect;
|
||||
pub mod given;
|
||||
mod handlers;
|
||||
mod load;
|
||||
mod when;
|
||||
|
||||
impl RepoActorLog {
|
||||
pub fn no_message_contains(&self, needle: impl AsRef<str> + std::fmt::Display) -> TestResult {
|
||||
if self.find_in_messages(needle.as_ref())? {
|
||||
tracing::error!(?self, "");
|
||||
panic!("found unexpected message: {needle}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn require_message_containing(
|
||||
&self,
|
||||
needle: impl AsRef<str> + std::fmt::Display,
|
||||
) -> TestResult {
|
||||
if !self.find_in_messages(needle.as_ref())? {
|
||||
tracing::error!(?self, "");
|
||||
panic!("expected message not found: {needle}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_in_messages(
|
||||
&self,
|
||||
needle: impl AsRef<str>,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
let found = self
|
||||
.lock()
|
||||
.map_err(|e| e.to_string())?
|
||||
.iter()
|
||||
.any(|message| message.contains(needle.as_ref()));
|
||||
Ok(found)
|
||||
}
|
||||
}
|
||||
|
||||
message!(ExamineActor => RepoActorView);
|
||||
impl Handler<ExamineActor> for RepoActor {
|
||||
type Result = RepoActorView;
|
||||
|
||||
fn handle(&mut self, _msg: ExamineActor, _ctx: &mut Self::Context) -> Self::Result {
|
||||
let repo_actor: &Self = self;
|
||||
Self::Result::from(repo_actor)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, MessageResponse)]
|
||||
pub struct RepoActorView {
|
||||
pub sleep_duration: std::time::Duration,
|
||||
pub generation: git::Generation,
|
||||
pub message_token: MessageToken,
|
||||
pub repo_details: git::RepoDetails,
|
||||
pub webhook: config::server::Webhook,
|
||||
pub webhook_id: Option<config::WebhookId>, // INFO: if [None] then no webhook is configured
|
||||
pub webhook_auth: Option<config::WebhookAuth>, // INFO: if [None] then no webhook is configured
|
||||
pub last_main_commit: Option<git::Commit>,
|
||||
pub last_next_commit: Option<git::Commit>,
|
||||
pub last_dev_commit: Option<git::Commit>,
|
||||
pub log: Option<RepoActorLog>,
|
||||
}
|
||||
impl From<&RepoActor> for RepoActorView {
|
||||
fn from(repo_actor: &RepoActor) -> Self {
|
||||
Self {
|
||||
sleep_duration: repo_actor.sleep_duration,
|
||||
generation: repo_actor.generation,
|
||||
message_token: repo_actor.message_token,
|
||||
repo_details: repo_actor.repo_details.clone(),
|
||||
webhook: repo_actor.webhook.clone(),
|
||||
webhook_id: repo_actor.webhook_id.clone(),
|
||||
webhook_auth: repo_actor.webhook_auth.clone(),
|
||||
last_main_commit: repo_actor.last_main_commit.clone(),
|
||||
last_next_commit: repo_actor.last_next_commit.clone(),
|
||||
last_dev_commit: repo_actor.last_dev_commit.clone(),
|
||||
log: repo_actor.log.clone(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ pub fn start_actor(
|
|||
}
|
||||
|
||||
pub fn start_actor_with_open_repository(
|
||||
open_repository: MockOpenRepositoryLike,
|
||||
open_repository: Box<dyn OpenRepositoryLike>,
|
||||
repo_details: git::RepoDetails,
|
||||
forge: Box<dyn git::ForgeLike>,
|
||||
) -> (actix::Addr<RepoActor>, RepoActorLog) {
|
||||
|
@ -28,7 +28,7 @@ pub fn start_actor_with_open_repository(
|
|||
forge,
|
||||
given::a_network().into(),
|
||||
);
|
||||
let actor = actor.with_open_repository(Box::new(open_repository));
|
||||
let actor = actor.with_open_repository(Some(open_repository));
|
||||
(actor.start(), log)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue