diff --git a/Cargo.toml b/Cargo.toml index 481246e6..f35c90ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,3 +93,4 @@ tokio = { version = "1.37" } # Testing assert2 = "0.3" +pretty_assertions = "1.4" diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index fd8f3be2..6e4e5282 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -56,6 +56,7 @@ actix = { workspace = true } [dev-dependencies] # # Testing assert2 = { workspace = true } +pretty_assertions = { workspace = true } [lints.clippy] nursery = { level = "warn", priority = -1 } diff --git a/crates/config/src/tests/mod.rs b/crates/config/src/tests/mod.rs index de4e729b..20918292 100644 --- a/crates/config/src/tests/mod.rs +++ b/crates/config/src/tests/mod.rs @@ -459,19 +459,103 @@ mod server { mod load { // - use std::collections::{BTreeMap, HashMap}; + use std::{ + collections::{BTreeMap, HashMap}, + net::SocketAddr, + str::FromStr as _, + }; use assert2::let_assert; + use pretty_assertions::assert_eq; use crate::{ server::{Http, ServerConfig, ServerStorage, Webhook}, tests::TestResult, - ForgeConfig, ForgeType, RepoBranches, RepoConfig, RepoConfigSource, ServerRepoConfig, + ForgeAlias, ForgeConfig, ForgeType, RepoAlias, RepoBranches, RepoConfig, + RepoConfigSource, ServerRepoConfig, }; #[test] fn load_should_parse_server_config() -> TestResult { let fs = kxio::fs::temp()?; + given_server_config(&fs)?; + let_assert!(Ok(config) = ServerConfig::load(&fs)); + let expected = ServerConfig::new( + Http::new("0.0.0.0".to_string(), 8080), + expected_webhook(), + expected_storage(), + HashMap::from([("default".to_string(), expected_forge_config())]), + ); + assert_eq!(config, expected, "ServerConfig"); + + if let Some(forge) = config.forge.get("world") { + if let Some(repo) = forge.get_repo("sam") { + let repo_config = repo.repo_config(); + let expected = Some(RepoConfig::new( + RepoBranches::new( + "master".to_string(), + "upcoming".to_string(), + "sam-dev".to_string(), + ), + RepoConfigSource::Server, + )); + assert_eq!(repo_config, expected, "RepoConfig"); + } + } + + Ok(()) + } + + #[test] + fn should_return_forges() -> TestResult { + let fs = kxio::fs::temp()?; + given_server_config(&fs)?; + let config = ServerConfig::load(&fs)?; + let forges = config.forges().collect::>(); + let forge_config = expected_forge_config(); + let expected = vec![(ForgeAlias::new("default".to_string()), &forge_config)]; + assert_eq!(forges, expected); + Ok(()) + } + + #[test] + fn should_return_storage() -> TestResult { + let fs = kxio::fs::temp()?; + given_server_config(&fs)?; + let config = ServerConfig::load(&fs)?; + let storage = config.storage(); + assert_eq!(storage, &expected_storage()); + assert_eq!(storage.path(), std::path::Path::new("/opt/git-next/data")); + Ok(()) + } + + #[test] + fn should_return_webhook() -> TestResult { + let fs = kxio::fs::temp()?; + given_server_config(&fs)?; + let config = ServerConfig::load(&fs)?; + let webhook = config.webhook(); + assert_eq!(webhook, &expected_webhook()); + assert_eq!( + webhook + .url(&ForgeAlias::new("a".to_string()), &RepoAlias::new("b")) + .as_ref(), + "http://localhost:9909/a/b" + ); + Ok(()) + } + + #[test] + fn should_return_http() -> TestResult { + let fs = kxio::fs::temp()?; + given_server_config(&fs)?; + let config = ServerConfig::load(&fs)?; + let_assert!(Ok(http) = config.http()); + assert_eq!(http, expected_http()); + Ok(()) + } + + fn given_server_config(fs: &kxio::fs::FileSystem) -> Result<(), kxio::fs::Error> { fs.file_write( &fs.base().join("git-next-server.toml"), r#" @@ -480,7 +564,7 @@ mod server { port = 8080 [webhook] - url = "http://localhost:9909/webhook" + url = "http://localhost:9909" [storage] path = "/opt/git-next/data" @@ -502,76 +586,186 @@ mod server { next = "upcoming" dev = "sam-dev" "#, - ) - ?; - let_assert!(Ok(config) = ServerConfig::load(&fs)); - let expected = ServerConfig::new( - Http::new("0.0.0.0".to_string(), 8080), - Webhook::new("http://localhost:9909/webhook".to_string()), - ServerStorage::new("/opt/git-next/data".into()), - HashMap::from([( - "default".to_string(), - ForgeConfig::new( - ForgeType::MockForge, - "git.example.net".to_string(), - "Bob".to_string(), - "API-Token".to_string(), - BTreeMap::from([ - ( - "hello".to_string(), - ServerRepoConfig::new( - "user/hello".to_string(), - "main".to_string(), - Some("/opt/git/user/hello.git".into()), - None, - None, - None, - ), - ), - ( - "world".to_string(), - ServerRepoConfig::new( - "user/world".to_string(), - "master".to_string(), - None, - Some("main".to_string()), - Some("next".to_string()), - Some("dev".to_string()), - ), - ), - ( - "sam".to_string(), - ServerRepoConfig::new( - "user/sam".to_string(), - "main".to_string(), - None, - Some("master".to_string()), - Some("upcoming".to_string()), - Some("sam-dev".to_string()), - ), - ), - ]), - ), - )]), - ); - assert_eq!(config, expected, "ServerConfig"); + ) + } - if let Some(forge) = config.forge.get("world") { - if let Some(repo) = forge.get_repo("sam") { - let repo_config = repo.repo_config(); - let expected = Some(RepoConfig::new( - RepoBranches::new( - "master".to_string(), - "upcoming".to_string(), - "sam-dev".to_string(), + fn expected_storage() -> ServerStorage { + ServerStorage::new("/opt/git-next/data".into()) + } + + fn expected_webhook() -> Webhook { + Webhook::new("http://localhost:9909".to_string()) + } + + fn expected_http() -> SocketAddr { + SocketAddr::from_str("0.0.0.0:8080").unwrap_or_else(|_| panic!()) + } + + fn expected_forge_config() -> ForgeConfig { + ForgeConfig::new( + ForgeType::MockForge, + "git.example.net".to_string(), + "Bob".to_string(), + "API-Token".to_string(), + BTreeMap::from([ + ( + "hello".to_string(), + ServerRepoConfig::new( + "user/hello".to_string(), + "main".to_string(), + Some("/opt/git/user/hello.git".into()), + None, + None, + None, ), - RepoConfigSource::Server, - )); - assert_eq!(repo_config, expected, "RepoConfig"); - } - } - - Ok(()) + ), + ( + "world".to_string(), + ServerRepoConfig::new( + "user/world".to_string(), + "master".to_string(), + None, + Some("main".to_string()), + Some("next".to_string()), + Some("dev".to_string()), + ), + ), + ( + "sam".to_string(), + ServerRepoConfig::new( + "user/sam".to_string(), + "main".to_string(), + None, + Some("master".to_string()), + Some("upcoming".to_string()), + Some("sam-dev".to_string()), + ), + ), + ]), + ) } } } +mod registered_webhook { + + use crate::{RegisteredWebhook, WebhookAuth, WebhookId}; + + #[test] + fn should_return_id() { + let id = WebhookId::new("a".to_string()); + let auth = WebhookAuth::generate(); + let rw = RegisteredWebhook::new(id.clone(), auth); + + assert_eq!(rw.id(), &id); + } + #[test] + fn should_return_auth() { + let id = WebhookId::new("a".to_string()); + let auth = WebhookAuth::generate(); + let rw = RegisteredWebhook::new(id, auth.clone()); + + assert_eq!(rw.auth(), &auth); + } +} +mod webhook { + mod message { + use crate::{webhook::message::Body, ForgeAlias, RepoAlias, WebhookMessage}; + + #[test] + fn should_return_forge_alias() { + let message = given_message(); + assert_eq!(message.forge_alias(), &expected_forge_alias()); + } + #[test] + fn should_return_repo_alias() { + let message = given_message(); + assert_eq!(message.repo_alias(), &expected_repo_alias()); + } + #[test] + fn should_return_body() { + let message = given_message(); + assert_eq!(message.body().as_bytes(), expected_body().as_bytes()); + } + #[test] + fn should_return_header() { + let message = given_message(); + assert_eq!(message.header("c"), Some("d".to_string())); + } + fn given_message() -> WebhookMessage { + WebhookMessage::new( + expected_forge_alias(), + expected_repo_alias(), + expected_headers(), + expected_body(), + ) + } + fn expected_forge_alias() -> ForgeAlias { + ForgeAlias::new("a".to_string()) + } + fn expected_repo_alias() -> RepoAlias { + RepoAlias::new("b") + } + fn expected_headers() -> std::collections::HashMap { + [("c", "d"), ("e", "f")] + .into_iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect() + } + fn expected_body() -> Body { + Body::new("g".to_string()) + } + } +} +mod push { + use crate::{ + webhook::{push::Branch, Push}, + BranchName, RepoBranches, + }; + + #[test] + fn should_return_main_branch() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(repo_branches.main()); + assert_eq!(push_event.branch(&repo_branches), Some(Branch::Main)); + } + #[test] + fn should_return_next_branch() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(repo_branches.next()); + assert_eq!(push_event.branch(&repo_branches), Some(Branch::Next)); + } + #[test] + fn should_return_dev_branch() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(repo_branches.dev()); + assert_eq!(push_event.branch(&repo_branches), Some(Branch::Dev)); + } + #[test] + fn should_not_return_other_branches() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(BranchName::new("foo")); + assert_eq!(push_event.branch(&repo_branches), None); + } + #[test] + fn should_return_sha() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(repo_branches.main()); + assert_eq!(push_event.sha(), "sha"); + } + #[test] + fn should_return_message() { + let repo_branches = given_repo_branches(); + let push_event = given_push_event(repo_branches.main()); + assert_eq!(push_event.message(), "message"); + } + fn given_push_event(branch_name: BranchName) -> Push { + Push::new(branch_name, "sha".to_string(), "message".to_string()) + } + fn given_repo_branches() -> RepoBranches { + RepoBranches::new( + "a-main".to_string(), + "b-next".to_string(), + "c-dev".to_string(), + ) + } +} diff --git a/crates/config/src/webhook/push.rs b/crates/config/src/webhook/push.rs index ce67acb5..941f9222 100644 --- a/crates/config/src/webhook/push.rs +++ b/crates/config/src/webhook/push.rs @@ -31,7 +31,7 @@ impl Push { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum Branch { Main, Next, diff --git a/server-default.toml b/server-default.toml index c5258fb0..2e87e42b 100644 --- a/server-default.toml +++ b/server-default.toml @@ -3,7 +3,7 @@ addr = "0.0.0.0" port = 8080 [webhook] -url = "https://localhost:8080" +url = "https://localhost:8080" # don't include any query path or a trailing slash [storage] path = "./data" @@ -15,7 +15,6 @@ forge_type = "ForgeJo" hostname = "git.example.net" user = "git-next" # the user to perform actions as token = "API-Token" -# path to private SSH key for user? [forge.default.repos] hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/projects/user/hello.git" } # maps to https://git.example.net/user/hello on the branch 'main'