forked from kemitix/git-next
tests: repo-actor: add more tests
This commit is contained in:
parent
ffab1986a7
commit
e585b07f6b
24 changed files with 767 additions and 141 deletions
|
@ -3,7 +3,7 @@ use crate as config;
|
||||||
|
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
|
|
||||||
#[derive(Debug, Constructor)]
|
#[derive(Debug, Constructor, derive_with::With)]
|
||||||
pub struct Push {
|
pub struct Push {
|
||||||
branch: config::BranchName,
|
branch: config::BranchName,
|
||||||
sha: String,
|
sha: String,
|
||||||
|
@ -30,7 +30,6 @@ impl Push {
|
||||||
&self.message
|
&self.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Branch {
|
pub enum Branch {
|
||||||
Main,
|
Main,
|
||||||
|
|
|
@ -42,6 +42,7 @@ ulid = { workspace = true }
|
||||||
|
|
||||||
# boilerplate
|
# boilerplate
|
||||||
derive_more = { workspace = true }
|
derive_more = { workspace = true }
|
||||||
|
derive-with = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
# Actors
|
# 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))]
|
#[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 {
|
fn handle(&mut self, msg: WebhookNotification, ctx: &mut Self::Context) -> Self::Result {
|
||||||
let Some(expected_authorization) = &self.webhook_auth else {
|
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");
|
warn!("Don't know what authorization to expect");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(config) = &self.repo_details.repo_config else {
|
let Some(config) = &self.repo_details.repo_config else {
|
||||||
|
actor::logger(&self.log, "server has no repo config");
|
||||||
warn!("No repo config");
|
warn!("No repo config");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -25,6 +27,7 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
.forge
|
.forge
|
||||||
.is_message_authorised(&msg, expected_authorization)
|
.is_message_authorised(&msg, expected_authorization)
|
||||||
{
|
{
|
||||||
|
actor::logger(&self.log, "message authorisation is invalid");
|
||||||
warn!(
|
warn!(
|
||||||
"Invalid authorization - expected {}",
|
"Invalid authorization - expected {}",
|
||||||
expected_authorization
|
expected_authorization
|
||||||
|
@ -34,11 +37,13 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
let body = msg.body();
|
let body = msg.body();
|
||||||
match self.forge.parse_webhook_body(body) {
|
match self.forge.parse_webhook_body(body) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
actor::logger(&self.log, "message parse error - not a push");
|
||||||
warn!(?err, "Not a 'push'");
|
warn!(?err, "Not a 'push'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(push) => match push.branch(config.branches()) {
|
Ok(push) => match push.branch(config.branches()) {
|
||||||
None => {
|
None => {
|
||||||
|
actor::logger(&self.log, "unknown branch");
|
||||||
warn!(
|
warn!(
|
||||||
?push,
|
?push,
|
||||||
"Unrecognised branch, we should be filtering to only the ones we want"
|
"Unrecognised branch, we should be filtering to only the ones we want"
|
||||||
|
@ -46,8 +51,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(config::webhook::push::Branch::Main) => {
|
Some(config::webhook::push::Branch::Main) => {
|
||||||
|
actor::logger(&self.log, "message is for main branch");
|
||||||
let commit = git::Commit::from(push);
|
let commit = git::Commit::from(push);
|
||||||
if self.last_main_commit.as_ref() == Some(&commit) {
|
if self.last_main_commit.as_ref() == Some(&commit) {
|
||||||
|
actor::logger(&self.log, "not a new commit on main");
|
||||||
info!(
|
info!(
|
||||||
branch = %config.branches().main(),
|
branch = %config.branches().main(),
|
||||||
%commit,
|
%commit,
|
||||||
|
@ -58,8 +65,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
self.last_main_commit.replace(commit);
|
self.last_main_commit.replace(commit);
|
||||||
}
|
}
|
||||||
Some(config::webhook::push::Branch::Next) => {
|
Some(config::webhook::push::Branch::Next) => {
|
||||||
|
actor::logger(&self.log, "message is for next branch");
|
||||||
let commit = git::Commit::from(push);
|
let commit = git::Commit::from(push);
|
||||||
if self.last_next_commit.as_ref() == Some(&commit) {
|
if self.last_next_commit.as_ref() == Some(&commit) {
|
||||||
|
actor::logger(&self.log, "not a new commit on next");
|
||||||
info!(
|
info!(
|
||||||
branch = %config.branches().next(),
|
branch = %config.branches().next(),
|
||||||
%commit,
|
%commit,
|
||||||
|
@ -70,8 +79,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
self.last_next_commit.replace(commit);
|
self.last_next_commit.replace(commit);
|
||||||
}
|
}
|
||||||
Some(config::webhook::push::Branch::Dev) => {
|
Some(config::webhook::push::Branch::Dev) => {
|
||||||
|
actor::logger(&self.log, "message is for dev branch");
|
||||||
let commit = git::Commit::from(push);
|
let commit = git::Commit::from(push);
|
||||||
if self.last_dev_commit.as_ref() == Some(&commit) {
|
if self.last_dev_commit.as_ref() == Some(&commit) {
|
||||||
|
actor::logger(&self.log, "not a new commit on dev");
|
||||||
info!(
|
info!(
|
||||||
branch = %config.branches().dev(),
|
branch = %config.branches().dev(),
|
||||||
%commit,
|
%commit,
|
||||||
|
@ -88,7 +99,10 @@ impl Handler<WebhookNotification> for actor::RepoActor {
|
||||||
token = %message_token,
|
token = %message_token,
|
||||||
"New commit"
|
"New commit"
|
||||||
);
|
);
|
||||||
ctx.address()
|
actor::do_send(
|
||||||
.do_send(actor::messages::ValidateRepo::new(message_token));
|
ctx.address(),
|
||||||
|
actor::messages::ValidateRepo::new(message_token),
|
||||||
|
&self.log,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,22 @@ mod tests;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
|
|
||||||
|
use derive_more::Deref;
|
||||||
use git_next_config as config;
|
use git_next_config as config;
|
||||||
use git_next_git as git;
|
use git_next_git as git;
|
||||||
|
|
||||||
use kxio::network::Network;
|
use kxio::network::Network;
|
||||||
use tracing::{info, warn, Instrument};
|
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.
|
/// An actor that represents a Git Repository.
|
||||||
///
|
///
|
||||||
|
@ -48,7 +57,7 @@ pub type RepoActorLog = std::sync::Arc<std::sync::Mutex<Vec<String>>>;
|
||||||
/// WebhookMessage --> ValidateRepo
|
/// 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)]
|
#[display("{}:{}:{}", generation, repo_details.forge.forge_alias(), repo_details.repo_alias)]
|
||||||
pub struct RepoActor {
|
pub struct RepoActor {
|
||||||
sleep_duration: std::time::Duration,
|
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 {
|
impl Actor for RepoActor {
|
||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
#[tracing::instrument(name = "RepoActor::stopping", skip_all, fields(repo = %self.repo_details))]
|
#[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::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod when_at_dev {
|
mod when_at_dev {
|
||||||
// next and dev branches are the same
|
// next and dev branches are the same
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -27,12 +28,14 @@ mod when_at_dev {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod can_advance {
|
mod can_advance {
|
||||||
// dev has at least one commit ahead of next
|
// dev has at least one commit ahead of next
|
||||||
use super::*;
|
use super::*;
|
||||||
mod to_wip_commit {
|
mod to_wip_commit {
|
||||||
// commit on dev is either invalid message or a WIP
|
// commit on dev is either invalid message or a WIP
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_push() -> TestResult {
|
fn should_not_push() -> TestResult {
|
||||||
let next = given::a_commit();
|
let next = given::a_commit();
|
||||||
|
@ -58,9 +61,11 @@ mod can_advance {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod to_invalid_commit {
|
mod to_invalid_commit {
|
||||||
// commit on dev is either invalid message or a WIP
|
// commit on dev is either invalid message or a WIP
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_push_and_error() -> TestResult {
|
fn should_not_push_and_error() -> TestResult {
|
||||||
let next = given::a_commit();
|
let next = given::a_commit();
|
||||||
|
@ -90,13 +95,12 @@ mod can_advance {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod to_valid_commit {
|
mod to_valid_commit {
|
||||||
// commit on dev is valid conventional commit message
|
// commit on dev is valid conventional commit message
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod push_is_err {
|
mod push_is_err {
|
||||||
use git::repository::{OnFetch, OnPush};
|
|
||||||
|
|
||||||
// the git push command fails
|
// the git push command fails
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -129,12 +133,11 @@ mod can_advance {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod push_is_ok {
|
mod push_is_ok {
|
||||||
// the git push command succeeds
|
// the git push command succeeds
|
||||||
use git_next_git::repository::{OnFetch, OnPush};
|
|
||||||
|
|
||||||
// the git push command fails
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_ok() -> TestResult {
|
fn should_ok() -> TestResult {
|
||||||
let next = given::a_commit();
|
let next = given::a_commit();
|
||||||
|
|
|
@ -1,22 +1,5 @@
|
||||||
//
|
//
|
||||||
use super::*;
|
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(
|
pub fn has_all_valid_remote_defaults(
|
||||||
open_repository: &mut MockOpenRepositoryLike,
|
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_url = given::a_webhook_url(forge_alias, repo_alias);
|
||||||
let webhook = given::a_webhook(&webhook_url);
|
let webhook = given::a_webhook(&webhook_url);
|
||||||
let generation = Generation::default();
|
let generation = Generation::default();
|
||||||
let log = Arc::new(Mutex::new(vec![]));
|
let log = RepoActorLog::default();
|
||||||
let actors_log = log.clone();
|
let actors_log = log.clone();
|
||||||
(
|
(
|
||||||
actor::RepoActor::new(
|
actor::RepoActor::new(
|
||||||
|
@ -276,3 +259,11 @@ pub fn a_hostname() -> config::Hostname {
|
||||||
pub fn a_registered_webhook() -> RegisteredWebhook {
|
pub fn a_registered_webhook() -> RegisteredWebhook {
|
||||||
RegisteredWebhook::new(given::a_webhook_id(), given::a_webhook_auth())
|
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(()));
|
.return_once(|_, _, _, _| Ok(()));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
System::current().stop();
|
||||||
|
@ -69,8 +72,11 @@ async fn when_server_config_should_fetch_then_push_then_revalidate() -> TestResu
|
||||||
.return_once(|_, _, _, _| Ok(()));
|
.return_once(|_, _, _, _| Ok(()));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
addr.send(actor::messages::AdvanceMain::new(next_commit.clone()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
System::current().stop();
|
||||||
|
|
|
@ -26,8 +26,11 @@ async fn should_fetch_then_push_then_revalidate() -> TestResult {
|
||||||
.return_once(|_, _, _, _| Ok(()));
|
.return_once(|_, _, _, _| Ok(()));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::AdvanceNext::new((
|
addr.send(actor::messages::AdvanceNext::new((
|
||||||
next_commit.clone(),
|
next_commit.clone(),
|
||||||
dev_commit_log,
|
dev_commit_log,
|
||||||
|
|
|
@ -15,8 +15,11 @@ async fn should_passthrough_to_receive_ci_status() -> TestResult {
|
||||||
);
|
);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
Box::new(forge),
|
||||||
|
);
|
||||||
addr.send(actor::messages::CheckCIStatus::new(next_commit.clone()))
|
addr.send(actor::messages::CheckCIStatus::new(next_commit.clone()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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));
|
.return_once(|| Box::new(load_config_open_repo));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::LoadConfigFromRepo::new())
|
addr.send(actor::messages::LoadConfigFromRepo::new())
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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));
|
.return_once(|| Box::new(load_config_open_repo));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::LoadConfigFromRepo::new())
|
addr.send(actor::messages::LoadConfigFromRepo::new())
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
System::current().stop();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix::prelude::*;
|
|
||||||
|
|
||||||
#[actix::test]
|
#[actix::test]
|
||||||
async fn should_store_repo_config_in_actor() -> TestResult {
|
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();
|
let new_repo_config = given::a_repo_config();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||||
new_repo_config.clone(),
|
new_repo_config.clone(),
|
||||||
))
|
))
|
||||||
|
@ -36,8 +38,11 @@ async fn should_validate_repo() -> TestResult {
|
||||||
let new_repo_config = given::a_repo_config();
|
let new_repo_config = given::a_repo_config();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||||
new_repo_config.clone(),
|
new_repo_config.clone(),
|
||||||
))
|
))
|
||||||
|
@ -63,8 +68,11 @@ async fn should_register_webhook() -> TestResult {
|
||||||
let new_repo_config = given::a_repo_config();
|
let new_repo_config = given::a_repo_config();
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveRepoConfig::new(
|
addr.send(actor::messages::ReceiveRepoConfig::new(
|
||||||
new_repo_config.clone(),
|
new_repo_config.clone(),
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//
|
//
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix::prelude::*;
|
|
||||||
|
|
||||||
mod advance_main;
|
mod advance_main;
|
||||||
mod advance_next;
|
mod advance_next;
|
||||||
|
@ -11,3 +10,5 @@ mod loaded_config;
|
||||||
mod receive_ci_status;
|
mod receive_ci_status;
|
||||||
mod register_webhook;
|
mod register_webhook;
|
||||||
mod validate_repo;
|
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");
|
let next_commit = given::a_named_commit("next");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||||
next_commit.clone(),
|
next_commit.clone(),
|
||||||
git::forge::commit::Status::Pass,
|
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");
|
let next_commit = given::a_named_commit("next");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||||
next_commit.clone(),
|
next_commit.clone(),
|
||||||
git::forge::commit::Status::Pending,
|
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");
|
let next_commit = given::a_named_commit("next");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(actor::messages::ReceiveCIStatus::new((
|
addr.send(actor::messages::ReceiveCIStatus::new((
|
||||||
next_commit.clone(),
|
next_commit.clone(),
|
||||||
git::forge::commit::Status::Fail,
|
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));
|
forge.expect_duplicate().return_once(|| Box::new(my_forge));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
Box::new(forge),
|
||||||
|
);
|
||||||
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
||||||
System::current().stop();
|
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));
|
forge.expect_duplicate().return_once(|| Box::new(my_forge));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, Box::new(forge));
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
Box::new(forge),
|
||||||
|
);
|
||||||
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
addr.send(actor::messages::RegisterWebhook::new()).await?;
|
||||||
System::current().stop();
|
System::current().stop();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
//
|
//
|
||||||
use crate::messages::{MessageToken, ValidateRepo};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test_log::test(actix::test)]
|
#[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);
|
expect::push_ok(&mut open_repository);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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);
|
expect::push_ok(&mut open_repository);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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);
|
expect::push_ok(&mut open_repository);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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));
|
.return_once(|_, _| Ok(dev_branch_log));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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));
|
.return_once(move |_, _| Ok(dev_commit_log));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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));
|
.return_once(move |_, _| Ok(dev_commit_log));
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let (addr, log) =
|
let (addr, log) = when::start_actor_with_open_repository(
|
||||||
when::start_actor_with_open_repository(open_repository, repo_details, given::a_forge());
|
Box::new(open_repository),
|
||||||
|
repo_details,
|
||||||
|
given::a_forge(),
|
||||||
|
);
|
||||||
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
addr.send(crate::messages::ValidateRepo::new(MessageToken::default()))
|
||||||
.await?;
|
.await?;
|
||||||
System::current().stop();
|
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::*;
|
use super::*;
|
||||||
|
|
||||||
#[tokio::test]
|
#[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(
|
pub fn start_actor_with_open_repository(
|
||||||
open_repository: MockOpenRepositoryLike,
|
open_repository: Box<dyn OpenRepositoryLike>,
|
||||||
repo_details: git::RepoDetails,
|
repo_details: git::RepoDetails,
|
||||||
forge: Box<dyn git::ForgeLike>,
|
forge: Box<dyn git::ForgeLike>,
|
||||||
) -> (actix::Addr<RepoActor>, RepoActorLog) {
|
) -> (actix::Addr<RepoActor>, RepoActorLog) {
|
||||||
|
@ -28,7 +28,7 @@ pub fn start_actor_with_open_repository(
|
||||||
forge,
|
forge,
|
||||||
given::a_network().into(),
|
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)
|
(actor.start(), log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue