git-next/crates/config/src/tests.rs
Paul Campbell 9c20e780d0
All checks were successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
Rust / build (push) Successful in 1m34s
feat: update auth of interal repos when changed in config
Closes kemitix/git-next#100
2024-07-10 09:05:36 +01:00

792 lines
25 KiB
Rust

//
use super::*;
use assert2::let_assert;
use secrecy::ExposeSecret;
use std::collections::BTreeMap;
use std::path::PathBuf;
use crate::server::Http;
use crate::server::ServerConfig;
use crate::server::ServerStorage;
use crate::server::Webhook;
use crate::webhook::push::Branch;
mod url;
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
type TestResult = Result<()>;
mod server_repo_config {
use super::*;
#[test]
fn should_not_return_repo_config_when_no_branches() {
let main = None;
let next = None;
let dev = None;
let src = ServerRepoConfig::new(given::a_name(), given::a_name(), None, main, next, dev);
let_assert!(None = src.repo_config());
}
#[test]
fn should_return_repo_config_when_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let src = ServerRepoConfig::new(
given::a_name(),
given::a_name(),
None,
Some(main.clone()),
Some(next.clone()),
Some(dev.clone()),
);
let_assert!(Some(rc) = src.repo_config());
assert_eq!(
rc,
RepoConfig::new(RepoBranches::new(main, next, dev), RepoConfigSource::Server)
);
}
#[test]
fn should_return_repo() {
let repo_path = given::a_name();
let src = ServerRepoConfig::new(
repo_path.clone(),
given::a_name(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.repo(), RepoPath::new(repo_path));
}
#[test]
fn should_return_branch() {
let branch = given::a_name();
let src = ServerRepoConfig::new(
given::a_name(),
branch.clone(),
None,
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(src.branch(), BranchName::new(branch));
}
#[test]
fn should_return_gitdir() {
let gitdir = given::a_name();
let src = ServerRepoConfig::new(
given::a_name(),
given::a_name(),
Some(gitdir.clone().into()),
Some(given::a_name()),
Some(given::a_name()),
Some(given::a_name()),
);
assert_eq!(
src.gitdir(),
Some(GitDir::new(
PathBuf::default().join(gitdir),
git_dir::StoragePathType::External
))
);
}
}
mod repo_config {
use super::*;
#[test]
fn should_parse_toml() -> TestResult {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let toml = format!(
r#"
[branches]
main = "{main}"
next = "{next}"
dev = "{dev}"
"#
);
let rc = RepoConfig::parse(toml.as_str())?;
assert_eq!(
rc,
RepoConfig::new(
RepoBranches::new(main, next, dev),
RepoConfigSource::Repo // reading from repo is the default
)
);
Ok(())
}
#[test]
fn should_return_branches() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let branches = RepoBranches::new(main, next, dev);
let repo_config = RepoConfig::new(branches.clone(), RepoConfigSource::Repo);
assert_eq!(repo_config.branches(), &branches);
}
#[test]
fn should_return_source() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_config =
RepoConfig::new(RepoBranches::new(main, next, dev), RepoConfigSource::Repo);
assert_eq!(repo_config.source(), RepoConfigSource::Repo);
}
}
mod forge_config {
use super::*;
#[test]
fn should_return_repos() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
// alphabetical order by key
let red_name = format!("a-{}", given::a_name());
let blue_name = format!("b-{}", given::a_name());
let red = ServerRepoConfig::new(red_name.clone(), given::a_name(), None, None, None, None);
let blue =
ServerRepoConfig::new(blue_name.clone(), given::a_name(), None, None, None, None);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name.clone(), blue.clone());
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repos = fc.repos().collect::<Vec<_>>();
assert_eq!(
returned_repos,
vec![
// alphabetical order by key
(RepoAlias::new(red_name.as_str()), &red),
(RepoAlias::new(blue_name.as_str()), &blue),
]
);
}
#[test]
fn should_return_forge_type() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos);
assert_eq!(fc.forge_type(), ForgeType::MockForge);
}
#[test]
fn should_return_hostname() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname.clone(), user, token, repos);
assert_eq!(fc.hostname(), Hostname::new(hostname));
}
#[test]
fn should_return_user() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user.clone(), token, repos);
assert_eq!(fc.user(), User::new(user));
}
#[test]
fn should_return_token() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let repos = BTreeMap::new();
let fc = ForgeConfig::new(forge_type, hostname, user, token.clone(), repos);
assert_eq!(fc.token().expose_secret(), token.as_str());
}
#[test]
fn should_return_repo() {
let forge_type = ForgeType::MockForge;
let hostname = given::a_name();
let user = given::a_name();
let token = given::a_name();
let red_name = given::a_name();
let blue_name = given::a_name();
let red = ServerRepoConfig::new(red_name.clone(), given::a_name(), None, None, None, None);
let blue =
ServerRepoConfig::new(blue_name.clone(), given::a_name(), None, None, None, None);
let mut repos = BTreeMap::new();
repos.insert(red_name.clone(), red.clone());
repos.insert(blue_name, blue);
let fc = ForgeConfig::new(forge_type, hostname, user, token, repos);
let returned_repo = fc.get_repo(red_name.as_str());
assert_eq!(returned_repo, Some(&red),);
}
}
mod forge_details {
use super::*;
use secrecy::ExposeSecret;
#[test]
fn should_return_forge_alias() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_alias = ForgeAlias::new(given::a_name());
let forge_details =
ForgeDetails::new(forge_alias.clone(), forge_type, hostname, user, token);
let result = forge_details.forge_alias();
assert_eq!(result, &forge_alias);
}
#[test]
fn should_return_forge_type() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name());
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
let result = forge_details.forge_type();
assert_eq!(result, forge_type);
}
#[test]
fn should_return_hostname() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name());
let forge_details =
ForgeDetails::new(forge_name, forge_type, hostname.clone(), user, token);
let result = forge_details.hostname();
assert_eq!(result, &hostname);
}
#[test]
fn should_return_user() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name());
let forge_details =
ForgeDetails::new(forge_name, forge_type, hostname, user.clone(), token);
let result = forge_details.user();
assert_eq!(result, &user);
}
#[test]
fn should_return_token() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name());
let forge_details =
ForgeDetails::new(forge_name, forge_type, hostname, user, token.clone());
let result = forge_details.token();
assert_eq!(result.expose_secret(), token.expose_secret());
}
#[test]
fn with_hostname_should_return_new_instance() {
let forge_type = ForgeType::MockForge;
let hostname = Hostname::new(given::a_name());
let other_hostname = Hostname::new(given::a_name());
let user = User::new(given::a_name());
let token = ApiToken::new(given::a_name().into());
let forge_name = ForgeAlias::new(given::a_name());
let forge_details = ForgeDetails::new(forge_name, forge_type, hostname, user, token);
let result = forge_details.with_hostname(other_hostname.clone());
assert_eq!(result.hostname(), &other_hostname);
}
#[test]
fn should_convert_from_name_and_config() {
let forge_type = ForgeType::MockForge;
let hostname_value = given::a_name();
let hostname = Hostname::new(hostname_value.clone());
let user_value = given::a_name();
let user = User::new(user_value.clone());
let token_value = given::a_name();
let token = ApiToken::new(token_value.clone().into());
let forge_alias = ForgeAlias::new(given::a_name());
let forge_config = ForgeConfig::new(
forge_type,
hostname_value,
user_value,
token_value,
BTreeMap::new(),
);
let forge_details = ForgeDetails::from((&forge_alias, &forge_config));
assert_eq!(forge_details.forge_alias(), &forge_alias);
assert_eq!(forge_details.hostname(), &hostname);
assert_eq!(forge_details.user(), &user);
assert_eq!(forge_details.token().expose_secret(), token.expose_secret());
}
}
mod forge_name {
use super::*;
use std::path::PathBuf;
#[test]
fn should_convert_to_pathbuf() {
let name = given::a_name();
let forge_alias = ForgeAlias::new(name.clone());
let pathbuf: PathBuf = (&forge_alias).into();
assert_eq!(pathbuf, PathBuf::new().join(name));
}
}
mod forge_type {
use super::*;
#[test]
fn should_display_as_string() {
assert_eq!(ForgeType::MockForge.to_string(), "MockForge".to_string());
}
}
mod gitdir {
use super::*;
use std::path::PathBuf;
#[test]
fn should_return_pathbuf() {
let pathbuf = PathBuf::default().join(given::a_name());
let gitdir = GitDir::new(pathbuf.clone(), git_dir::StoragePathType::Internal);
let result = gitdir.to_path_buf();
assert_eq!(result, pathbuf);
}
#[test]
fn should_display() {
let pathbuf = PathBuf::default().join("foo");
let gitdir = GitDir::new(pathbuf, git_dir::StoragePathType::Internal);
let result = gitdir.to_string();
assert_eq!(result, "foo");
}
#[test]
fn should_convert_to_pathbuf_from_ref() {
let path = given::a_name();
let pathbuf = PathBuf::default().join(path);
let gitdir: GitDir = GitDir::new(pathbuf.clone(), git_dir::StoragePathType::Internal);
let result: PathBuf = (&gitdir).into();
assert_eq!(result, pathbuf);
}
}
mod repo_branches {
use super::*;
#[test]
fn should_return_main() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_branches = RepoBranches::new(main.clone(), next, dev);
assert_eq!(repo_branches.main(), BranchName::new(main));
}
#[test]
fn should_return_next() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_branches = RepoBranches::new(main, next.clone(), dev);
assert_eq!(repo_branches.next(), BranchName::new(next));
}
#[test]
fn should_return_dev() {
let main = given::a_name();
let next = given::a_name();
let dev = given::a_name();
let repo_branches = RepoBranches::new(main, next, dev.clone());
assert_eq!(repo_branches.dev(), BranchName::new(dev));
}
}
mod server {
use super::*;
mod load {
use super::*;
#[test]
fn load_should_parse_server_config() -> TestResult {
let server_config = given::a_server_config();
let fs = kxio::fs::temp()?;
let_assert!(Ok(_) = write_server_config(&server_config, &fs), "write");
let_assert!(Ok(config) = ServerConfig::load(&fs), "load");
assert_eq!(config, server_config, "compare");
Ok(())
}
fn write_server_config(
server_config: &ServerConfig,
fs: &kxio::fs::FileSystem,
) -> TestResult {
let http = &server_config.http()?;
let http_addr = http.ip();
let http_port = server_config.http()?.port();
let webhook_url = server_config.webhook().base_url();
let storage_path = server_config.storage().path();
let forge_alias = server_config
.forges()
.next()
.map(|(fa, _)| fa)
.ok_or("forge missing")?;
let forge_default = server_config
.forge
.get(forge_alias.as_ref())
.ok_or("forge missing")?;
let forge_type = forge_default.forge_type();
let forge_hostname = forge_default.hostname();
let forge_user = forge_default.user();
use secrecy::ExposeSecret;
let forge_token = forge_default.token().expose_secret().to_string();
let mut repos: Vec<String> = vec![];
for (repo_alias, server_repo_config) in forge_default.repos() {
let repo_path = server_repo_config.repo();
let branch = server_repo_config.branch();
let gitdir = server_repo_config
.gitdir()
.map(|gitdir| gitdir.to_path_buf())
.map(|pathbuf| pathbuf.display().to_string())
.map(|gitdir| format!(r#", gitdir = "{gitdir}" "#))
.unwrap_or_default();
let repo_server_config = server_repo_config
.repo_config()
.map(|src| {
let branches = src.branches();
let main = branches.main();
let next = branches.next();
let dev = branches.dev();
format!(r#", main = "{main}", next = "{next}", dev = "{dev}""#)
})
.unwrap_or_default();
repos.push(format!(r#"{repo_alias} = {{ repo = "{repo_path}", branch = "{branch}"{gitdir}{repo_server_config} }}"#));
}
let repos = repos.join("\n");
let file_contents = &format!(
r#"
[http]
addr = "{http_addr}"
port = {http_port}
[webhook]
url = "{webhook_url}"
[storage]
path = {storage_path:?}
[forge.{forge_alias}]
forge_type = "{forge_type}"
hostname = "{forge_hostname}"
user = "{forge_user}"
token = "{forge_token}"
[forge.{forge_alias}.repos]
{repos}
"#
);
println!("{file_contents}");
fs.file_write(
&fs.base().join("git-next-server.toml"),
file_contents.as_str(),
)?;
Ok(())
}
}
}
mod registered_webhook {
use super::*;
#[test]
fn should_return_id() {
let id = given::a_webhook_id();
let auth = WebhookAuth::generate();
let rw = RegisteredWebhook::new(id.clone(), auth);
assert_eq!(rw.id(), &id);
}
#[test]
fn should_return_auth() {
let id = given::a_webhook_id();
let auth = WebhookAuth::generate();
let rw = RegisteredWebhook::new(id, auth.clone());
assert_eq!(rw.auth(), &auth);
}
}
mod webhook {
use super::*;
mod message {
use super::*;
#[test]
fn should_return_forge_alias() {
let forge_alias = given::a_forge_alias();
let message = ForgeNotification::new(
forge_alias.clone(),
given::a_repo_alias(),
Default::default(),
given::a_webhook_message_body(),
);
assert_eq!(message.forge_alias(), &forge_alias);
}
#[test]
fn should_return_repo_alias() {
let repo_alias = given::a_repo_alias();
let message = ForgeNotification::new(
given::a_forge_alias(),
repo_alias.clone(),
Default::default(),
given::a_webhook_message_body(),
);
assert_eq!(message.repo_alias(), &repo_alias);
}
#[test]
fn should_return_body() {
let body = given::a_webhook_message_body();
let message = ForgeNotification::new(
given::a_forge_alias(),
given::a_repo_alias(),
Default::default(),
body.clone(),
);
assert_eq!(message.body().as_bytes(), body.as_bytes());
}
#[test]
fn should_return_header() {
let key = given::a_name();
let value = given::a_name();
let mut headers: BTreeMap<String, String> = Default::default();
headers.insert(key.clone(), value.clone());
let message = ForgeNotification::new(
given::a_forge_alias(),
given::a_repo_alias(),
headers,
given::a_webhook_message_body(),
);
assert_eq!(message.header(&key), Some(value));
}
}
}
mod push {
use super::*;
#[test]
fn should_return_main_branch() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = repo_branches.main();
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.branch(&repo_branches), Some(Branch::Main));
}
#[test]
fn should_return_next_branch() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = repo_branches.next();
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.branch(&repo_branches), Some(Branch::Next));
}
#[test]
fn should_return_dev_branch() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = repo_branches.dev();
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.branch(&repo_branches), Some(Branch::Dev));
}
#[test]
fn should_not_return_other_branches() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = BranchName::new(given::a_name());
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.branch(&repo_branches), None);
}
#[test]
fn should_return_sha() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = repo_branches.main();
let sha = sha.clone();
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.sha(), sha);
}
#[test]
fn should_return_message() {
let repo_branches = given::some_repo_branches();
let sha = given::a_name();
let message = given::a_name();
let push_event = {
let branch_name = repo_branches.main();
let message = message.clone();
crate::webhook::Push::new(branch_name, sha, message)
};
assert_eq!(push_event.message(), message);
}
}
mod given {
use super::*;
use rand::Rng as _;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
pub fn a_name() -> String {
use rand::Rng;
use std::iter;
fn generate(len: usize) -> String {
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let mut rng = rand::thread_rng();
let one_char = || CHARSET[rng.gen_range(0..CHARSET.len())] as char;
iter::repeat_with(one_char).take(len).collect()
}
generate(5)
}
pub fn a_server_config() -> ServerConfig {
ServerConfig::new(
an_http(),
a_webhook(),
a_server_storage(),
some_forge_configs(),
)
}
pub fn an_http() -> Http {
let address = format!(
"{}.{}.{}.{}",
an_octet(),
an_octet(),
an_octet(),
an_octet()
);
Http::new(address, a_port())
}
pub fn an_octet() -> u8 {
rand::thread_rng().gen()
}
pub fn a_port() -> u16 {
rand::thread_rng().gen()
}
pub fn a_webhook() -> Webhook {
Webhook::new(a_name())
}
pub fn a_server_storage() -> ServerStorage {
ServerStorage::new(a_name().into())
}
pub fn some_forge_configs() -> BTreeMap<String, ForgeConfig> {
[(a_name(), a_forge_config())].into()
}
pub fn a_forge_config() -> ForgeConfig {
ForgeConfig::new(
ForgeType::MockForge,
a_name(), // hostname
a_name(), // user
a_name(), // token
some_server_repo_configs(),
)
}
pub fn some_server_repo_configs() -> BTreeMap<String, ServerRepoConfig> {
[(a_name(), a_server_repo_config())].into()
}
pub fn a_pathbuf() -> PathBuf {
let name = a_name();
let path = Path::new(name.as_str());
path.to_path_buf()
}
pub fn a_server_repo_config() -> ServerRepoConfig {
ServerRepoConfig::new(
a_name(), // repo_path
a_name(), // branch
Some(a_pathbuf()), // gitdir
Some(a_name()), // main
Some(a_name()), // next
Some(a_name()), // dev
)
}
pub fn a_webhook_id() -> WebhookId {
WebhookId::new(a_name())
}
pub fn a_forge_alias() -> ForgeAlias {
ForgeAlias::new(a_name())
}
pub fn a_repo_alias() -> RepoAlias {
RepoAlias::new(a_name())
}
pub fn a_webhook_message_body() -> crate::webhook::forge_notification::Body {
crate::webhook::forge_notification::Body::new(a_name())
}
pub fn some_repo_branches() -> RepoBranches {
RepoBranches::new(
a_name(), // main
a_name(), // next
a_name(), // dev
)
}
}