use anyhow::{Context, Result, bail};
use nostr_sdk::prelude::*;
use test_harness::{Harness, tag_value, tag_values};
const DISPLAY_NAME: &str = "grasp defaults";
const IDENTIFIER: &str = "grasp-defaults";
async fn announcement(harness: &Harness, author: PublicKey) -> Result<Event> {
harness
.relay("default")
.events(Filter::new().author(author).kind(Kind::GitRepoAnnouncement))
.await?
.into_iter()
.find(|event| tag_value(event, "d").as_deref() == Some(IDENTIFIER))
.with_context(|| {
format!(
"no kind-30617 with `d` = {IDENTIFIER:?} on the default relay after `ngit init`"
)
})
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn additional_clone_supplements_default_grasp_hosting() -> Result<()> {
let harness = Harness::builder(
env!("CARGO_BIN_EXE_ngit"),
env!("CARGO_BIN_EXE_git-remote-nostr"),
)
.with_relay("default")
.with_grasp_server("repo")
.with_vanilla_git_server("host")
.build()
.await?;
let (repo, state) = harness.arrange_init_state_a_fresh().await?;
let grasp = harness.grasp("repo");
let grasp_http_url = grasp.url().to_string();
let grasp_relay_url = grasp.relay_url();
let vanilla_url = harness.vanilla_git_server("host").url().to_string();
let init = repo
.ngit([
"init",
"--name",
DISPLAY_NAME,
"--identifier",
IDENTIFIER,
"--additional-clone",
&vanilla_url,
"-d",
])
.output()
.await
.context("failed to spawn ngit init with an additional clone url")?;
if !init.status.success() {
bail!(
"ngit init exited non-zero ({:?})\nstdout: {}\nstderr: {}",
init.status,
String::from_utf8_lossy(&init.stdout),
String::from_utf8_lossy(&init.stderr),
);
}
let announcement = announcement(&harness, state.keys.public_key()).await?;
let clone_urls = tag_values(&announcement, "clone");
assert!(
clone_urls.iter().any(|url| url == &vanilla_url),
"the additional clone url {vanilla_url:?} should be announced \
verbatim; got {clone_urls:?}",
);
let grasp_clone_prefix = format!("{grasp_http_url}/");
assert!(
clone_urls.iter().any(|url| {
url.starts_with(&grasp_clone_prefix)
&& url.contains(&state.npub)
&& url.ends_with(&format!("/{IDENTIFIER}.git"))
}),
"`-d` should still host the repository on the default grasp server \
({grasp_http_url}) alongside the additional clone url; got \
{clone_urls:?}",
);
let relays = tag_values(&announcement, "relays");
assert!(
relays.contains(&grasp_relay_url),
"the default grasp server's relay ({grasp_relay_url}) should be \
announced; got {relays:?}",
);
Ok(())
}
#[tokio::test]
async fn opting_out_without_own_hosting_is_refused() -> Result<()> {
let harness = Harness::builder(
env!("CARGO_BIN_EXE_ngit"),
env!("CARGO_BIN_EXE_git-remote-nostr"),
)
.with_relay("default")
.with_grasp_server("repo")
.build()
.await?;
let (repo, state) = harness.arrange_init_state_a_fresh().await?;
let init = repo
.ngit([
"init",
"--name",
DISPLAY_NAME,
"--identifier",
IDENTIFIER,
"--grasp-server",
"",
"-d",
])
.output()
.await
.context("failed to spawn ngit init with an unusable opt-out")?;
assert!(
!init.status.success(),
"opting out of grasp hosting without an additional relay and clone \
url must fail\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&init.stdout),
String::from_utf8_lossy(&init.stderr),
);
let stderr = String::from_utf8_lossy(&init.stderr);
assert!(
stderr.contains("--additional-relay") && stderr.contains("--additional-clone"),
"the refusal should name the flags that supply the missing hosting: {stderr}",
);
let announcements = harness
.relay("default")
.events(
Filter::new()
.author(state.keys.public_key())
.kind(Kind::GitRepoAnnouncement),
)
.await?;
assert!(
announcements.is_empty(),
"the refused init must not publish an announcement; got {announcements:?}",
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn an_empty_grasp_server_value_opts_out_of_grasp_hosting() -> Result<()> {
let harness = Harness::builder(
env!("CARGO_BIN_EXE_ngit"),
env!("CARGO_BIN_EXE_git-remote-nostr"),
)
.with_relay("default")
.with_grasp_server("repo")
.with_vanilla_git_server("host")
.build()
.await?;
let (repo, state) = harness.arrange_init_state_a_fresh().await?;
let grasp = harness.grasp("repo");
let grasp_http_url = grasp.url().to_string();
let grasp_relay_url = grasp.relay_url();
let vanilla_url = harness.vanilla_git_server("host").url().to_string();
let default_relay_url = harness.relay("default").url().to_string();
let init = repo
.ngit([
"init",
"--name",
DISPLAY_NAME,
"--identifier",
IDENTIFIER,
"--grasp-server",
"",
"--additional-clone",
&vanilla_url,
"--additional-relay",
&default_relay_url,
"-d",
])
.output()
.await
.context("failed to spawn ngit init with an empty --grasp-server value")?;
if !init.status.success() {
bail!(
"ngit init exited non-zero ({:?})\nstdout: {}\nstderr: {}",
init.status,
String::from_utf8_lossy(&init.stdout),
String::from_utf8_lossy(&init.stderr),
);
}
let announcement = announcement(&harness, state.keys.public_key()).await?;
let clone_urls = tag_values(&announcement, "clone");
assert_eq!(
clone_urls,
vec![vanilla_url.clone()],
"the opt-out should leave the additional clone url as the only git \
server (no {grasp_http_url} entry)",
);
let relays = tag_values(&announcement, "relays");
assert!(
!relays.contains(&grasp_relay_url),
"the opt-out should not announce the grasp server's relay \
({grasp_relay_url}); got {relays:?}",
);
assert!(
relays
.iter()
.any(|relay| relay.trim_end_matches('/') == default_relay_url.trim_end_matches('/')),
"the announcement should carry the supplied additional relay \
({default_relay_url}); got {relays:?}",
);
Ok(())
}