2024-06-04 18:30:11 +01:00
|
|
|
//
|
2024-11-20 22:46:33 +00:00
|
|
|
#![allow(clippy::expect_used)] // used with mock net
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
use crate::ForgeJo;
|
|
|
|
use git_next_core::{
|
2024-07-26 06:49:09 +01:00
|
|
|
git::{self, forge::commit::Status, ForgeLike as _},
|
2024-11-20 22:46:33 +00:00
|
|
|
server::{ListenUrl, RepoListenUrl},
|
2024-07-25 09:02:43 +01:00
|
|
|
BranchName, ForgeAlias, ForgeConfig, ForgeNotification, ForgeType, GitDir, Hostname, RepoAlias,
|
|
|
|
RepoBranches, RepoPath, ServerRepoConfig, StoragePathType, WebhookAuth, WebhookId,
|
|
|
|
};
|
|
|
|
|
|
|
|
use assert2::let_assert;
|
2024-11-20 22:46:33 +00:00
|
|
|
use kxio::net::{MockNet, StatusCode};
|
2024-07-25 09:02:43 +01:00
|
|
|
use secrecy::ExposeSecret;
|
|
|
|
use serde::Serialize;
|
|
|
|
use serde_json::json;
|
|
|
|
|
|
|
|
use std::collections::BTreeMap;
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
mod forgejo {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_return_name() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
assert_eq!(forge.name(), "forgejo");
|
|
|
|
}
|
|
|
|
|
|
|
|
mod is_message_authorised {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_return_true_with_valid_header() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let auth = given::a_webhook_auth();
|
|
|
|
let message = given::a_webhook_message(given::Header::Valid(auth.clone()));
|
|
|
|
assert!(forge.is_message_authorised(&message, &auth));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn should_return_false_with_missing_header() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let auth = given::a_webhook_auth();
|
|
|
|
let message = given::a_webhook_message(given::Header::Missing);
|
|
|
|
assert!(!forge.is_message_authorised(&message, &auth));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn should_return_false_with_non_basic_prefix() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let auth = given::a_webhook_auth();
|
|
|
|
let message = given::a_webhook_message(given::Header::NonBasic);
|
|
|
|
assert!(!forge.is_message_authorised(&message, &auth));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn should_return_false_with_non_ulid_value() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let auth = given::a_webhook_auth();
|
|
|
|
let message = given::a_webhook_message(given::Header::NonUlid);
|
|
|
|
assert!(!forge.is_message_authorised(&message, &auth));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn should_return_false_with_wrong_ulid_value() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let auth = given::a_webhook_auth();
|
|
|
|
let message = given::a_webhook_message(given::Header::WrongUlid);
|
|
|
|
assert!(!forge.is_message_authorised(&message, &auth));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod parse_webhook_body {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_parse_valid_body() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-06-01 14:49:28 +01:00
|
|
|
let repo_branches = given::repo_branches();
|
|
|
|
let next = repo_branches.next();
|
|
|
|
let sha = given::a_name();
|
|
|
|
let message = given::a_name();
|
2024-07-25 09:02:43 +01:00
|
|
|
let body = git_next_core::webhook::forge_notification::Body::new(
|
2024-06-01 14:49:28 +01:00
|
|
|
json!({"ref":format!("refs/heads/{next}"),"after":sha,"head_commit":{"message":message}})
|
|
|
|
.to_string(),
|
|
|
|
);
|
|
|
|
let_assert!(Ok(push) = forge.parse_webhook_body(&body));
|
|
|
|
assert_eq!(push.sha(), sha);
|
|
|
|
assert_eq!(push.message(), message);
|
|
|
|
assert_eq!(
|
|
|
|
push.branch(&repo_branches),
|
2024-07-25 09:02:43 +01:00
|
|
|
Some(git_next_core::webhook::push::Branch::Next)
|
2024-06-01 14:49:28 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn should_error_invalid_body() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let forge = given::a_forgejo_forge(&given::repo_details(&fs), given::a_network());
|
2024-07-25 09:02:43 +01:00
|
|
|
let body = git_next_core::webhook::forge_notification::Body::new(
|
|
|
|
r#"{"type":"invalid"}"#.to_string(),
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
let_assert!(Err(_) = forge.parse_webhook_body(&body));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod commit_status {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_pass_for_success() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
given::a_commit_state("success", &net, &repo_details, &commit);
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Pass
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_pending_for_pending() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
given::a_commit_state("pending", &net, &repo_details, &commit);
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Pending
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_fail_for_failure() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
given::a_commit_state("failure", &net, &repo_details, &commit);
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Fail
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_fail_for_error() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
given::a_commit_state("error", &net, &repo_details, &commit);
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Fail
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_pending_for_blank() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
given::a_commit_state("", &net, &repo_details, &commit);
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Pending
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_pending_for_no_statuses() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
|
|
|
net.on()
|
|
|
|
.get(given::a_commit_status_url(&repo_details, &commit))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body(
|
|
|
|
json!({
|
|
|
|
"state": "" // blank => Pending
|
|
|
|
})
|
|
|
|
.to_string(),
|
|
|
|
)
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-11-20 22:46:33 +00:00
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Pending
|
|
|
|
);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_pending_for_network_error() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let commit = given::a_commit();
|
2024-11-20 22:46:33 +00:00
|
|
|
let mock_net = given::a_network();
|
|
|
|
mock_net
|
|
|
|
.on()
|
|
|
|
.get(given::a_commit_status_url(&repo_details, &commit))
|
|
|
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body("book today")
|
|
|
|
.expect("mock");
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, mock_net);
|
|
|
|
assert_eq!(
|
|
|
|
forge.commit_status(&commit).await.expect("status"),
|
|
|
|
Status::Pending
|
2024-06-01 14:49:28 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod list_webhooks {
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_a_list_of_matching_webhooks() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
let hook_id_1 = given::a_forgejo_webhook_id();
|
|
|
|
let hook_id_2 = given::a_forgejo_webhook_id();
|
|
|
|
let hook_id_3 = given::a_forgejo_webhook_id();
|
|
|
|
let mut net = given::a_network();
|
|
|
|
|
|
|
|
let mut args = with::WebhookArgs {
|
|
|
|
net: &mut net,
|
|
|
|
hostname,
|
|
|
|
repo_path,
|
|
|
|
token,
|
|
|
|
};
|
|
|
|
// page 1 with three items
|
|
|
|
with::get_webhooks_by_page(
|
|
|
|
1,
|
|
|
|
&[
|
2024-07-31 07:20:42 +01:00
|
|
|
with::ReturnedWebhook::new(hook_id_1, &repo_listen_url),
|
|
|
|
with::ReturnedWebhook::new(hook_id_2, &repo_listen_url),
|
|
|
|
with::ReturnedWebhook::new(hook_id_3, &given::a_repo_listen_url(&repo_details)),
|
2024-06-01 14:49:28 +01:00
|
|
|
],
|
|
|
|
&mut args,
|
|
|
|
);
|
|
|
|
// page 2 with no items - stops pagination
|
|
|
|
with::get_webhooks_by_page(2, &[], &mut args);
|
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Ok(result) = forge.list_webhooks(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert_eq!(
|
|
|
|
result,
|
|
|
|
vec![
|
|
|
|
WebhookId::new(format!("{hook_id_1}")),
|
|
|
|
WebhookId::new(format!("{hook_id_2}"))
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_any_network_error() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
2024-06-01 14:49:28 +01:00
|
|
|
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.get(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page=1&token={token}"
|
|
|
|
))
|
|
|
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body("error_message")
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Err(_) = forge.list_webhooks(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod unregister_webhook {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_delete_webhook() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
let webhook_id = given::a_webhook_id();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
2024-06-01 14:49:28 +01:00
|
|
|
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.delete(format!(
|
2024-06-01 14:49:28 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
2024-11-20 22:46:33 +00:00
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body("")
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-08-06 16:15:56 +01:00
|
|
|
let_assert!(Ok(()) = forge.unregister_webhook(&webhook_id).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
2024-06-06 17:45:33 +01:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_error_on_network_error() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-06 17:45:33 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
let webhook_id = given::a_webhook_id();
|
2024-11-20 22:46:33 +00:00
|
|
|
let net = given::a_network();
|
2024-06-06 17:45:33 +01:00
|
|
|
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.delete(format!(
|
2024-06-06 17:45:33 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
2024-11-20 22:46:33 +00:00
|
|
|
))
|
|
|
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body("error")
|
|
|
|
.expect("mock");
|
2024-06-06 17:45:33 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
|
|
|
let_assert!(Err(err) = forge.unregister_webhook(&webhook_id).await);
|
|
|
|
assert!(
|
|
|
|
matches!(err, git::forge::webhook::Error::FailedToUnregister(_)),
|
|
|
|
"{err:?}"
|
|
|
|
);
|
|
|
|
}
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mod register_webhook {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_register_a_new_webhook() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
repo_details.repo_config.is_some(),
|
|
|
|
"repo_details needs to have repo_config for this test"
|
|
|
|
);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
|
|
|
|
let mut net = given::a_network();
|
|
|
|
let mut args = with::WebhookArgs {
|
|
|
|
net: &mut net,
|
|
|
|
hostname,
|
|
|
|
repo_path,
|
|
|
|
token,
|
|
|
|
};
|
|
|
|
|
|
|
|
// there are no existing matching webhooks
|
|
|
|
with::get_webhooks_by_page(1, &[], &mut args);
|
|
|
|
// register the webhook will succeed
|
|
|
|
let webhook_id = given::a_forgejo_webhook_id();
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.post(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body(json!({"id": webhook_id, "config":{}}).to_string())
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Ok(registered_webhook) = forge.register_webhook(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert_eq!(
|
|
|
|
registered_webhook.id(),
|
|
|
|
&WebhookId::new(format!("{webhook_id}"))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_abort_if_repo_config_missing() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let mut repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
repo_details.repo_config.take();
|
|
|
|
assert!(
|
|
|
|
repo_details.repo_config.is_none(),
|
|
|
|
"repo_details needs to NOT have repo_config for this test"
|
|
|
|
);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let net = given::a_network();
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
matches!(err, git::forge::webhook::Error::NoRepoConfig),
|
|
|
|
"{err:?}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_unregister_existing_webhooks_before_registering() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
repo_details.repo_config.is_some(),
|
|
|
|
"repo_details needs to have repo_config for this test"
|
|
|
|
);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
|
|
|
|
let mut net = given::a_network();
|
|
|
|
let mut args = with::WebhookArgs {
|
|
|
|
net: &mut net,
|
|
|
|
hostname,
|
|
|
|
repo_path,
|
|
|
|
token,
|
|
|
|
};
|
2024-08-06 16:15:56 +01:00
|
|
|
let hook_1 =
|
|
|
|
with::ReturnedWebhook::new(given::a_forgejo_webhook_id(), &repo_listen_url);
|
|
|
|
let hook_2 =
|
|
|
|
with::ReturnedWebhook::new(given::a_forgejo_webhook_id(), &repo_listen_url);
|
|
|
|
let hook_3 = with::ReturnedWebhook::new(
|
2024-06-01 14:49:28 +01:00
|
|
|
given::a_forgejo_webhook_id(),
|
2024-07-31 07:20:42 +01:00
|
|
|
&given::a_repo_listen_url(&repo_details),
|
2024-06-01 14:49:28 +01:00
|
|
|
);
|
2024-08-06 16:15:56 +01:00
|
|
|
let hooks = [hook_1, hook_2, hook_3];
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
// there are three existing webhooks, two are matching webhooks
|
|
|
|
with::get_webhooks_by_page(1, &hooks, &mut args);
|
|
|
|
with::get_webhooks_by_page(2, &[], &mut args);
|
|
|
|
// should unregister 1 and 2, but not 3
|
|
|
|
with::unregister_webhook(&hooks[0], &mut args);
|
|
|
|
with::unregister_webhook(&hooks[1], &mut args);
|
|
|
|
// register the webhook will succeed
|
|
|
|
let webhook_id = given::a_forgejo_webhook_id();
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.post(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body(json!({"id": webhook_id, "config":{}}).to_string())
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Ok(registered_webhook) = forge.register_webhook(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert_eq!(
|
|
|
|
registered_webhook.id(),
|
|
|
|
&WebhookId::new(format!("{webhook_id}"))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_error_if_empty_network_response() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
repo_details.repo_config.is_some(),
|
|
|
|
"repo_details needs to have repo_config for this test"
|
|
|
|
);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
|
|
|
|
let mut net = given::a_network();
|
|
|
|
let mut args = with::WebhookArgs {
|
|
|
|
net: &mut net,
|
|
|
|
hostname,
|
|
|
|
repo_path,
|
|
|
|
token,
|
|
|
|
};
|
|
|
|
|
|
|
|
// there are no existing matching webhooks
|
|
|
|
with::get_webhooks_by_page(1, &[], &mut args);
|
|
|
|
// register the webhook will return empty response
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.post(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body(json!({}).to_string()) // empty response)
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
2024-11-20 22:46:33 +00:00
|
|
|
matches!(err, git::forge::webhook::Error::NetworkResponseEmpty),
|
2024-06-01 14:49:28 +01:00
|
|
|
"{err:?}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn should_return_error_on_network_error() {
|
2024-06-07 20:01:29 +01:00
|
|
|
let fs = given::a_filesystem();
|
|
|
|
let repo_details = given::repo_details(&fs);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
repo_details.repo_config.is_some(),
|
|
|
|
"repo_details needs to have repo_config for this test"
|
|
|
|
);
|
2024-07-31 07:20:42 +01:00
|
|
|
let repo_listen_url = given::a_repo_listen_url(&repo_details);
|
2024-06-01 14:49:28 +01:00
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
|
|
|
|
let mut net = given::a_network();
|
|
|
|
let mut args = with::WebhookArgs {
|
|
|
|
net: &mut net,
|
|
|
|
hostname,
|
|
|
|
repo_path,
|
|
|
|
token,
|
|
|
|
};
|
|
|
|
|
|
|
|
// there are no existing matching webhooks
|
|
|
|
with::get_webhooks_by_page(1, &[], &mut args);
|
|
|
|
// register the webhook will return empty response
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.post(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?token={token}"
|
|
|
|
))
|
|
|
|
.respond(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body("error")
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
let forge = given::a_forgejo_forge(&repo_details, net);
|
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
let_assert!(Err(err) = forge.register_webhook(&repo_listen_url).await);
|
2024-06-01 14:49:28 +01:00
|
|
|
assert!(
|
|
|
|
matches!(err, git::forge::webhook::Error::FailedToRegister(_)),
|
|
|
|
"{err:?}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mod with {
|
2024-07-31 07:20:42 +01:00
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
use super::*;
|
2024-06-01 14:49:28 +01:00
|
|
|
|
|
|
|
pub fn get_webhooks_by_page(
|
|
|
|
page: u8,
|
|
|
|
response: &[ReturnedWebhook],
|
|
|
|
args: &mut WebhookArgs,
|
|
|
|
) {
|
|
|
|
let hostname = args.hostname;
|
|
|
|
let repo_path = args.repo_path;
|
|
|
|
let token = args.token;
|
2024-11-20 22:46:33 +00:00
|
|
|
args.net
|
|
|
|
.on()
|
|
|
|
.get(format!(
|
2024-06-01 14:49:28 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks?page={page}&token={token}"
|
2024-11-20 22:46:33 +00:00
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body(json!(response).to_string())
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
pub fn unregister_webhook(hook1: &ReturnedWebhook, args: &mut WebhookArgs) {
|
|
|
|
let webhook_id = hook1.id;
|
|
|
|
let hostname = args.hostname;
|
|
|
|
let repo_path = args.repo_path;
|
|
|
|
let token = args.token;
|
2024-11-20 22:46:33 +00:00
|
|
|
args.net
|
|
|
|
.on()
|
|
|
|
.delete(format!(
|
2024-06-01 14:49:28 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/hooks/{webhook_id}?token={token}"
|
2024-11-20 22:46:33 +00:00
|
|
|
))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body("")
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
pub struct WebhookArgs<'a> {
|
2024-11-20 22:46:33 +00:00
|
|
|
pub net: &'a MockNet,
|
2024-06-01 14:49:28 +01:00
|
|
|
pub hostname: &'a Hostname,
|
|
|
|
pub repo_path: &'a RepoPath,
|
|
|
|
pub token: &'a str,
|
|
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct ReturnedWebhook {
|
|
|
|
pub id: i64,
|
|
|
|
pub config: Config,
|
|
|
|
}
|
|
|
|
impl ReturnedWebhook {
|
2024-07-31 07:20:42 +01:00
|
|
|
pub fn new(id: i64, url: &RepoListenUrl) -> Self {
|
2024-06-01 14:49:28 +01:00
|
|
|
Self {
|
|
|
|
id,
|
2024-07-31 07:20:42 +01:00
|
|
|
config: Config {
|
|
|
|
url: url.to_string(),
|
|
|
|
},
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct Config {
|
2024-07-31 07:20:42 +01:00
|
|
|
pub url: String,
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mod given {
|
2024-06-07 20:01:29 +01:00
|
|
|
|
2024-07-31 07:20:42 +01:00
|
|
|
use git::RepoDetails;
|
|
|
|
use git_next_core::server::RepoListenUrl;
|
2024-11-20 22:46:33 +00:00
|
|
|
use kxio::net::{MockNet, StatusCode};
|
2024-07-31 07:20:42 +01:00
|
|
|
|
2024-06-01 14:49:28 +01:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
pub fn a_commit_state(
|
|
|
|
state: impl AsRef<str>,
|
2024-11-20 22:46:33 +00:00
|
|
|
net: &MockNet,
|
2024-06-01 14:49:28 +01:00
|
|
|
repo_details: &git::RepoDetails,
|
|
|
|
commit: &git::Commit,
|
|
|
|
) {
|
2024-11-20 22:46:33 +00:00
|
|
|
net.on()
|
|
|
|
.get(a_commit_status_url(repo_details, commit))
|
|
|
|
.respond(StatusCode::OK)
|
|
|
|
.body((json!({"state":state.as_ref()})).to_string())
|
|
|
|
.expect("mock");
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_commit_status_url(
|
|
|
|
repo_details: &git::RepoDetails,
|
|
|
|
commit: &git::Commit,
|
|
|
|
) -> String {
|
|
|
|
let hostname = repo_details.forge.hostname();
|
|
|
|
let repo_path = &repo_details.repo_path;
|
|
|
|
let token = repo_details.forge.token().expose_secret();
|
|
|
|
format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{repo_path}/commits/{commit}/status?token={token}"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Header {
|
2024-07-25 09:02:43 +01:00
|
|
|
Valid(WebhookAuth),
|
2024-06-01 14:49:28 +01:00
|
|
|
Missing,
|
|
|
|
NonBasic,
|
|
|
|
NonUlid,
|
|
|
|
WrongUlid,
|
|
|
|
}
|
2024-06-07 20:01:29 +01:00
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_webhook_message(header: Header) -> ForgeNotification {
|
|
|
|
ForgeNotification::new(
|
2024-06-01 14:49:28 +01:00
|
|
|
given::a_forge_alias(),
|
|
|
|
given::a_repo_alias(),
|
|
|
|
given::webhook_headers(header),
|
|
|
|
given::a_webhook_message_body(),
|
|
|
|
)
|
|
|
|
}
|
2024-06-07 20:01:29 +01:00
|
|
|
|
2024-06-19 07:03:08 +01:00
|
|
|
pub fn webhook_headers(header: Header) -> BTreeMap<String, String> {
|
|
|
|
let mut headers = BTreeMap::new();
|
2024-06-01 14:49:28 +01:00
|
|
|
match header {
|
|
|
|
Header::Valid(auth) => {
|
|
|
|
headers.insert("authorization".to_string(), format!("Basic {auth}"));
|
|
|
|
}
|
|
|
|
Header::Missing => { /* don't add any header */ }
|
|
|
|
Header::NonBasic => {
|
|
|
|
headers.insert("authorization".to_string(), "Non-Basic".to_string());
|
|
|
|
}
|
|
|
|
Header::NonUlid => {
|
|
|
|
headers.insert("authorization".to_string(), "Basic 123456".to_string());
|
|
|
|
}
|
|
|
|
Header::WrongUlid => {
|
|
|
|
headers.insert(
|
|
|
|
"authorization".to_string(),
|
2024-07-25 09:02:43 +01:00
|
|
|
format!("Basic {}", WebhookAuth::generate()),
|
2024-06-01 14:49:28 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
headers
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_forgejo_forge(
|
|
|
|
repo_details: &git::RepoDetails,
|
2024-11-20 22:46:33 +00:00
|
|
|
net: impl Into<kxio::net::Net>,
|
2024-06-01 14:49:28 +01:00
|
|
|
) -> ForgeJo {
|
2024-11-20 22:46:33 +00:00
|
|
|
let net: kxio::net::Net = net.into();
|
|
|
|
ForgeJo::new(repo_details.clone(), net)
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
2024-06-07 20:01:29 +01:00
|
|
|
|
|
|
|
pub fn a_forgejo_webhook_id() -> i64 {
|
|
|
|
use rand::RngCore as _;
|
|
|
|
rand::thread_rng().next_u32().into()
|
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_webhook_auth() -> WebhookAuth {
|
|
|
|
WebhookAuth::generate()
|
2024-06-07 20:01:29 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_webhook_message_body() -> git_next_core::webhook::forge_notification::Body {
|
|
|
|
git_next_core::webhook::forge_notification::Body::new(a_name())
|
2024-06-07 20:01:29 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn repo_branches() -> RepoBranches {
|
|
|
|
RepoBranches::new(a_name(), a_name(), a_name())
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_forge_alias() -> ForgeAlias {
|
|
|
|
ForgeAlias::new(a_name())
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_repo_alias() -> RepoAlias {
|
|
|
|
RepoAlias::new(a_name())
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
2024-06-07 20:01:29 +01:00
|
|
|
|
2024-11-20 22:46:33 +00:00
|
|
|
pub fn a_network() -> kxio::net::MockNet {
|
|
|
|
kxio::net::mock()
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-09-05 09:41:17 +01:00
|
|
|
pub fn maybe_a_number() -> Option<u32> {
|
|
|
|
use rand::Rng;
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
if Rng::gen_ratio(&mut rng, 1, 2) {
|
|
|
|
Some(a_number())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_number() -> u32 {
|
|
|
|
use rand::Rng;
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
rng.gen_range(0..100)
|
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_webhook_id() -> WebhookId {
|
|
|
|
WebhookId::new(a_name())
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_branch_name() -> BranchName {
|
|
|
|
BranchName::new(a_name())
|
2024-06-07 20:01:29 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_git_dir(fs: &kxio::fs::FileSystem) -> GitDir {
|
2024-06-07 20:01:29 +01:00
|
|
|
let dir_name = a_name();
|
|
|
|
let dir = fs.base().join(dir_name);
|
2024-07-25 09:02:43 +01:00
|
|
|
GitDir::new(dir, StoragePathType::Internal)
|
2024-06-07 20:01:29 +01:00
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_forge_config() -> ForgeConfig {
|
|
|
|
ForgeConfig::new(
|
|
|
|
ForgeType::MockForge,
|
2024-06-07 20:01:29 +01:00
|
|
|
a_name(),
|
|
|
|
a_name(),
|
|
|
|
a_name(),
|
2024-09-05 09:41:17 +01:00
|
|
|
maybe_a_number(),
|
2024-08-06 16:15:56 +01:00
|
|
|
BTreeMap::default(), // no repos
|
2024-06-07 20:01:29 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-25 09:02:43 +01:00
|
|
|
pub fn a_server_repo_config() -> ServerRepoConfig {
|
2024-08-31 10:59:16 +01:00
|
|
|
let main = a_branch_name().peel();
|
|
|
|
let next = a_branch_name().peel();
|
|
|
|
let dev = a_branch_name().peel();
|
2024-07-25 09:02:43 +01:00
|
|
|
ServerRepoConfig::new(
|
2024-06-07 20:01:29 +01:00
|
|
|
format!("{}/{}", a_name(), a_name()),
|
|
|
|
main.clone(),
|
|
|
|
None,
|
|
|
|
Some(main),
|
|
|
|
Some(next),
|
|
|
|
Some(dev),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_commit() -> git::Commit {
|
|
|
|
git::Commit::new(a_commit_sha(), a_commit_message())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_commit_message() -> git::commit::Message {
|
|
|
|
git::commit::Message::new(a_name())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn a_commit_sha() -> git::commit::Sha {
|
|
|
|
git::commit::Sha::new(a_name())
|
|
|
|
}
|
|
|
|
|
2024-11-20 22:46:33 +00:00
|
|
|
pub fn a_filesystem() -> kxio::fs::TempFileSystem {
|
|
|
|
kxio::fs::temp().expect("temp fs")
|
2024-06-07 20:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn repo_details(fs: &kxio::fs::FileSystem) -> git::RepoDetails {
|
2024-06-20 19:03:11 +01:00
|
|
|
let generation = git::Generation::default();
|
2024-06-07 20:01:29 +01:00
|
|
|
let repo_alias = a_repo_alias();
|
|
|
|
let server_repo_config = a_server_repo_config();
|
|
|
|
let forge_alias = a_forge_alias();
|
|
|
|
let forge_config = a_forge_config();
|
|
|
|
let gitdir = a_git_dir(fs);
|
|
|
|
git::RepoDetails::new(
|
|
|
|
generation,
|
|
|
|
&repo_alias,
|
|
|
|
&server_repo_config,
|
|
|
|
&forge_alias,
|
|
|
|
&forge_config,
|
|
|
|
gitdir,
|
|
|
|
)
|
|
|
|
}
|
2024-07-31 07:20:42 +01:00
|
|
|
|
|
|
|
pub fn a_repo_listen_url(repo_details: &RepoDetails) -> RepoListenUrl {
|
|
|
|
let listen_url = a_listen_url();
|
|
|
|
let forge_alias = repo_details.forge.forge_alias().clone();
|
|
|
|
let repo_alias = repo_details.repo_alias.clone();
|
|
|
|
RepoListenUrl::new((listen_url, forge_alias, repo_alias))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn a_listen_url() -> ListenUrl {
|
|
|
|
ListenUrl::new(a_name())
|
|
|
|
}
|
2024-06-01 14:49:28 +01:00
|
|
|
}
|
|
|
|
}
|