test: migrate tests and grasp-audit to rust-nostr 0.45.0-alpha.1

Complete the 0.45 migration for the integration tests (tests/) and the
grasp-audit workspace member.

- TagKind removed throughout: Tag::custom takes string literals;
  tag.kind() compared as &str; ref-name matches use the raw string.
- EventBuilder finalize traits brought into scope where needed;
  Keys::sign_event is now sync (SignEvent trait).
- Client API: new() is no-arg, NIP-42 auth via
  ClientBuilder::authenticator(SignerAuthenticator::new(keys));
  fetch_events(filter).timeout(d); subscribe(filter); sync(filter).opts();
  Output.val -> Output.value; Relay status via relay.status().is_connected().
- MemoryDatabase via nostr_memory (unbounded/bounded).
- hashes imports moved from nostr_sdk::hashes to nostr::hashes.
- SyncSummary.remote/received are now maps keyed by EventId.

cargo build, cargo build --tests, cargo build -p grasp-audit --tests,
cargo clippy --all-targets --workspace, and cargo fmt all clean.
Lib unit tests (440) pass.
This commit is contained in:
DanConwayDev
2026-06-16 11:31:26 +00:00
parent 4bd8bd4085
commit 58f2dce91b
41 changed files with 535 additions and 722 deletions
+6 -21
View File
@@ -131,19 +131,12 @@ impl AuditConfig {
/// // ]
/// ```
pub fn audit_tags(&self) -> Vec<Tag> {
use nostr_sdk::prelude::{Alphabet, SingleLetterTag};
// Use "t" tags for categorization (standard NIP-01 hashtag type)
let t_tag = SingleLetterTag::lowercase(Alphabet::T);
vec![
Tag::custom(TagKind::SingleLetter(t_tag), vec!["grasp-audit-test-event"]),
Tag::custom("t", vec!["grasp-audit-test-event"]),
Tag::custom("t", vec![format!("audit-{}", self.run_id)]),
Tag::custom(
TagKind::SingleLetter(t_tag),
vec![format!("audit-{}", self.run_id)],
),
Tag::custom(
TagKind::SingleLetter(t_tag),
"t",
vec![format!(
"audit-cleanup-after-{}",
self.cleanup_after.as_secs()
@@ -228,7 +221,7 @@ impl AuditEventBuilder {
builder
};
let event = builder.sign_with_keys(keys)?;
let event = builder.finalize(keys)?;
Ok(event)
}
@@ -256,22 +249,14 @@ mod tests {
#[test]
fn test_audit_tags() {
use nostr_sdk::prelude::{Alphabet, SingleLetterTag};
let config = AuditConfig::isolated();
let tags = config.audit_tags();
assert_eq!(tags.len(), 3);
let t_tag = SingleLetterTag::lowercase(Alphabet::T);
// All tags should be "t" tags (hashtags)
for tag in &tags {
if let TagKind::SingleLetter(letter) = tag.kind() {
assert_eq!(letter, t_tag);
} else {
panic!("Expected SingleLetter tag");
}
assert_eq!(tag.kind(), "t", "Expected 't' (SingleLetter) tag");
}
// Check for "t" tag with "grasp-audit-test-event"
@@ -300,7 +285,7 @@ mod tests {
let keys = Keys::generate();
let event = AuditEventBuilder::new(Kind::TextNote, "test", config.clone())
.tag(Tag::custom(TagKind::Custom("test".into()), vec!["value"]))
.tag(Tag::custom("test", vec!["value"]))
.build(&keys)
.unwrap();
+37 -37
View File
@@ -42,7 +42,9 @@ impl AuditClient {
let maintainer_keys = Keys::generate();
let recursive_maintainer_keys = Keys::generate();
let pr_author_keys = Keys::generate();
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
Self {
client,
config,
@@ -61,7 +63,9 @@ impl AuditClient {
let maintainer_keys = Keys::generate();
let recursive_maintainer_keys = Keys::generate();
let pr_author_keys = Keys::generate();
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
// Add relay and connect
client.add_relay(relay_url).await?;
@@ -74,7 +78,7 @@ impl AuditClient {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = client.relays().await;
connected = relays.values().any(|r| r.is_connected());
connected = relays.values().any(|r| r.status().is_connected());
if connected {
break;
@@ -129,7 +133,9 @@ impl AuditClient {
let maintainer_keys = Keys::generate();
let recursive_maintainer_keys = Keys::generate();
let pr_author_keys = Keys::generate();
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
// Add relay and connect
client.add_relay(relay_url).await?;
@@ -142,7 +148,7 @@ impl AuditClient {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = client.relays().await;
connected = relays.values().any(|r| r.is_connected());
connected = relays.values().any(|r| r.status().is_connected());
if connected {
break;
@@ -235,7 +241,7 @@ impl AuditClient {
// Check if we have any connected relays
let relays = self.client.relays().await;
for relay in relays.values() {
if relay.is_connected() {
if relay.status().is_connected() {
return true;
}
}
@@ -326,7 +332,8 @@ impl AuditClient {
pub async fn is_event_on_relay(&self, id: EventId) -> Result<bool> {
Ok(!self
.client
.fetch_events(vec![Filter::new().id(id)], Duration::from_secs(1))
.fetch_events(vec![Filter::new().id(id)])
.timeout(Duration::from_secs(1))
.await
.context("error trying to query relay for event")?
.is_empty())
@@ -355,7 +362,7 @@ impl AuditClient {
///
/// // Create event with automatic audit tags
/// let event = client.event_builder(Kind::TextNote, "test content")
/// .tag(Tag::custom(TagKind::custom("custom"), vec!["value"]))
/// .tag(Tag::custom("custom", vec!["value"]))
/// .build(client.keys())?;
///
/// // Event now has both your custom tag AND the 3 audit tags
@@ -384,7 +391,8 @@ impl AuditClient {
let events = self
.client
.fetch_events(filter, Duration::from_secs(5))
.fetch_events(filter)
.timeout(Duration::from_secs(5))
.await?;
Ok(events.into_iter().collect())
@@ -400,7 +408,7 @@ impl AuditClient {
let mut all_events = Vec::new();
for filter in filters {
let events = self.client.fetch_events(filter, timeout).await?;
let events = self.client.fetch_events(filter).timeout(timeout).await?;
all_events.extend(events.into_iter());
}
@@ -498,25 +506,19 @@ impl AuditClient {
)
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("name"),
"name",
vec![format!("{} Test Repository", test_name)],
))
.tag(Tag::custom(
TagKind::custom("description"),
"description",
vec![format!("Repository for {} testing", test_name)],
))
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("{}/{}/{}.git", http_url, npub, repo_id)],
))
.tag(Tag::custom(
TagKind::custom("relays"),
vec![relay_url.clone()],
))
.tag(Tag::custom(
TagKind::custom("maintainers"),
maintainer_pubkeys.to_vec(),
))
.tag(Tag::custom("relays", vec![relay_url.clone()]))
.tag(Tag::custom("maintainers", maintainer_pubkeys.to_vec()))
.build(signing_keys)
.map_err(|e| anyhow!("Failed to build repository announcement event: {}", e))?;
@@ -589,7 +591,7 @@ impl AuditClient {
let repo_id = repo_event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or_else(|| anyhow!("Repository event must have a 'd' tag"))?
.to_string();
@@ -598,8 +600,8 @@ impl AuditClient {
let a_tag_value = format!("30617:{}:{}", repo_pubkey, repo_id);
let mut tags = vec![
Tag::custom(TagKind::custom("a"), vec![a_tag_value]),
Tag::custom(TagKind::custom("subject"), vec![issue_title]),
Tag::custom("a", vec![a_tag_value]),
Tag::custom("subject", vec![issue_title]),
];
// Add any additional tags
@@ -632,11 +634,11 @@ impl AuditClient {
let mut tags = vec![
Tag::custom(
TagKind::custom("E"),
"E",
vec![event_id.to_hex(), "".to_string(), "root".to_string()],
),
Tag::event(event_id),
Tag::custom(TagKind::custom("K"), vec![event_kind.as_u16().to_string()]),
Tag::custom("K", vec![event_kind.as_u16().to_string()]),
Tag::public_key(event_pubkey),
];
@@ -677,7 +679,9 @@ mod tests {
let recursive_maintainer_keys = Keys::generate();
let pr_author_keys = Keys::generate();
let client = AuditClient {
client: Client::new(keys.clone()),
client: Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build(),
config: config.clone(),
keys: keys.clone(),
maintainer_keys,
@@ -701,7 +705,9 @@ mod tests {
let recursive_maintainer_keys = Keys::generate();
let pr_author_keys = Keys::generate();
let client = AuditClient {
client: Client::new(keys.clone()),
client: Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build(),
config: config.clone(),
keys: keys.clone(),
maintainer_keys,
@@ -714,7 +720,7 @@ mod tests {
// Create an event with a custom tag
let event = client
.event_builder(Kind::TextNote, "test content")
.tag(Tag::custom(TagKind::custom("custom"), vec!["value"]))
.tag(Tag::custom("custom", vec!["value"]))
.build(&keys)
.unwrap();
@@ -776,18 +782,12 @@ mod tests {
let event = client
.event_builder(Kind::GitRepoAnnouncement, "Test repository")
.tag(Tag::identifier("test-repo"))
.tag(Tag::custom(
TagKind::custom("maintainers"),
maintainer_pubkeys.clone(),
))
.tag(Tag::custom("maintainers", maintainer_pubkeys.clone()))
.build(client.keys())
.unwrap();
// Verify the maintainers tag is present and correctly formatted
let maintainers_tag = event
.tags
.iter()
.find(|t| t.kind() == TagKind::custom("maintainers"));
let maintainers_tag = event.tags.iter().find(|t| t.kind() == "maintainers");
assert!(
maintainers_tag.is_some(),
+29 -42
View File
@@ -962,7 +962,7 @@ impl<'a> TestContext<'a> {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or_else(|| anyhow::anyhow!("Missing d tag in repo announcement"))?
.to_string();
@@ -977,11 +977,11 @@ impl<'a> TestContext<'a> {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![DETERMINISTIC_COMMIT_HASH.to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.custom_time(older_timestamp)
@@ -1004,11 +1004,11 @@ impl<'a> TestContext<'a> {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![DETERMINISTIC_COMMIT_HASH.to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.custom_time(older_timestamp)
@@ -1032,7 +1032,7 @@ impl<'a> TestContext<'a> {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or_else(|| anyhow::anyhow!("Missing repo_id in ValidRepoServed fixture"))?
.to_string();
@@ -1048,17 +1048,14 @@ impl<'a> TestContext<'a> {
"Test PR for GRASP validation",
)
.tag(Tag::custom(
TagKind::custom("a"),
"a",
vec![format!(
"30617:{}:{}",
self.client.public_key().to_hex(), // Owner pubkey
repo_id
)],
))
.tag(Tag::custom(
TagKind::custom("c"),
vec![PR_TEST_COMMIT_HASH.to_string()],
))
.tag(Tag::custom("c", vec![PR_TEST_COMMIT_HASH.to_string()]))
.custom_time(pr_timestamp)
.build(self.client.pr_author_keys())
.map_err(|e| anyhow::anyhow!("Failed to build PR event: {}", e))
@@ -1075,7 +1072,7 @@ impl<'a> TestContext<'a> {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or_else(|| anyhow::anyhow!("Missing repo_id in ValidRepoServed fixture"))?
.to_string();
@@ -1091,17 +1088,14 @@ impl<'a> TestContext<'a> {
"Test PR for GRASP validation",
)
.tag(Tag::custom(
TagKind::custom("a"),
"a",
vec![format!(
"30617:{}:{}",
self.client.public_key().to_hex(), // Owner pubkey
repo_id
)],
))
.tag(Tag::custom(
TagKind::custom("c"),
vec![PR_TEST_COMMIT_HASH.to_string()],
))
.tag(Tag::custom("c", vec![PR_TEST_COMMIT_HASH.to_string()]))
.custom_time(pr_timestamp)
.build(self.client.pr_author_keys())
.map_err(|e| anyhow::anyhow!("Failed to build PR event: {}", e))
@@ -1172,16 +1166,16 @@ impl<'a> TestContext<'a> {
)
.tag(Tag::identifier(repo_id))
.tag(Tag::custom(
TagKind::custom("name"),
"name",
vec![format!("{} (maintainer)", repo_id)],
))
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("{}/{}/{}.git", http_url, maintainer_npub, repo_id)],
))
.tag(Tag::custom(TagKind::custom("relays"), vec![relay_url]))
.tag(Tag::custom("relays", vec![relay_url]))
.tag(Tag::custom(
TagKind::custom("maintainers"),
"maintainers",
vec![self.client.recursive_maintainer_pubkey_hex()],
))
.build(self.client.maintainer_keys())
@@ -1190,10 +1184,9 @@ impl<'a> TestContext<'a> {
/// Extract repo_id from a repo announcement event
fn extract_repo_id(&self, repo: &Event) -> Result<String> {
use nostr_sdk::prelude::*;
repo.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.map(|s| s.to_string())
.ok_or_else(|| anyhow::anyhow!("Missing d tag in repo announcement"))
@@ -1225,12 +1218,12 @@ impl<'a> TestContext<'a> {
.client
.event_builder(Kind::GitRepoAnnouncement, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(TagKind::custom("name"), vec![repo_id.clone()]))
.tag(Tag::custom("name", vec![repo_id.clone()]))
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("{}/{}/{}.git", http_url, npub, repo_id)],
))
.tag(Tag::custom(TagKind::custom("relays"), vec![relay_url]))
.tag(Tag::custom("relays", vec![relay_url]))
.build(self.client.keys())
.map_err(|e| anyhow::anyhow!("Failed to build repo announcement: {}", e))?;
@@ -1274,11 +1267,11 @@ impl<'a> TestContext<'a> {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![DETERMINISTIC_COMMIT_HASH.to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.custom_time(older_timestamp)
@@ -1540,11 +1533,11 @@ impl<'a> TestContext<'a> {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.custom_time(maintainer_timestamp)
@@ -1715,11 +1708,11 @@ impl<'a> TestContext<'a> {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.custom_time(recursive_maintainer_timestamp)
@@ -1883,12 +1876,9 @@ impl<'a> TestContext<'a> {
.client
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom("HEAD", vec!["refs/heads/develop".to_string()]))
.tag(Tag::custom(
TagKind::custom("HEAD"),
vec!["refs/heads/develop".to_string()],
))
.tag(Tag::custom(
TagKind::custom("refs/heads/develop"),
"refs/heads/develop",
vec![RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()],
))
.custom_time(develop_timestamp)
@@ -2043,17 +2033,14 @@ impl<'a> TestContext<'a> {
self.client
.event_builder(Kind::GitPullRequest, "Test PR 2 for GRASP validation")
.tag(Tag::custom(
TagKind::custom("a"),
"a",
vec![format!(
"30617:{}:{}",
self.client.public_key().to_hex(),
repo_id
)],
))
.tag(Tag::custom(
TagKind::custom("c"),
vec![PR_TEST_COMMIT_HASH_2.to_string()],
))
.tag(Tag::custom("c", vec![PR_TEST_COMMIT_HASH_2.to_string()]))
.custom_time(pr_timestamp)
.build(self.client.pr_author_keys())
.map_err(|e| anyhow::anyhow!("Failed to build PR event 2: {}", e))
+15 -33
View File
@@ -324,18 +324,9 @@ pub async fn run_probe(
config.clone(),
)
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("name"),
vec!["GRASP Probe Repository"],
))
.tag(Tag::custom(
TagKind::custom("clone"),
vec![clone_url.clone()],
))
.tag(Tag::custom(
TagKind::custom("relays"),
vec![relay_url.to_string()],
))
.tag(Tag::custom("name", vec!["GRASP Probe Repository"]))
.tag(Tag::custom("clone", vec![clone_url.clone()]))
.tag(Tag::custom("relays", vec![relay_url.to_string()]))
.build(&keys);
announcement_event_opt = ann_result.ok();
@@ -343,12 +334,9 @@ pub async fn run_probe(
let state_result = if let Some(ref ch) = commit_hash {
crate::audit::AuditEventBuilder::new(Kind::RepoState, "", config.clone())
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom("refs/heads/main", vec![ch.clone()]))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
vec![ch.clone()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.build(&keys)
@@ -819,10 +807,7 @@ pub async fn run_probe(
if let Some(ref state_ev) = state_event_opt {
for tag in state_ev.tags.iter() {
let kind_str = match tag.kind() {
TagKind::Custom(ref s) => s.clone(),
_ => continue,
};
let kind_str = tag.kind().to_string();
// Only check refs/heads/* and refs/tags/*, skip HEAD and refs/nostr/*
if !kind_str.starts_with("refs/heads/")
&& !kind_str.starts_with("refs/tags/")
@@ -886,8 +871,8 @@ pub async fn run_probe(
let filter = Filter::new().kind(Kind::GitRepoAnnouncement).limit(1);
let existing = client
.client()
.fetch_events(
filter,
.fetch_events(filter)
.timeout(
deadline
.saturating_duration_since(Instant::now())
.min(Duration::from_secs(5)),
@@ -905,7 +890,7 @@ pub async fn run_probe(
let ann_id = ev
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap_or("unknown")
.to_string();
@@ -947,7 +932,7 @@ pub async fn run_probe(
let ann_id = ev
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap_or("unknown")
.to_string();
@@ -956,7 +941,7 @@ pub async fn run_probe(
let fetch_url = ev
.tags
.iter()
.find(|t| t.kind() == TagKind::custom("clone"))
.find(|t| t.kind() == "clone")
.and_then(|t| t.content())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("{}/{}/{}.git", http_base, ann_npub, ann_id));
@@ -1054,8 +1039,8 @@ pub async fn run_probe(
);
let state_events = client
.client()
.fetch_events(
state_filter,
.fetch_events(state_filter)
.timeout(
deadline
.saturating_duration_since(Instant::now())
.min(Duration::from_secs(5)),
@@ -1087,10 +1072,7 @@ pub async fn run_probe(
for state_ev in state_events.iter() {
let ts = state_ev.created_at.as_secs();
for tag in state_ev.tags.iter() {
let kind_str = match tag.kind() {
TagKind::Custom(ref s) => s.clone(),
_ => continue,
};
let kind_str = tag.kind().to_string();
if !kind_str.starts_with("refs/heads/")
&& !kind_str.starts_with("refs/tags/")
{
@@ -1101,7 +1083,7 @@ pub async fn run_probe(
None => continue,
};
let prev_ts =
latest_ts.get(kind_str.as_ref()).copied().unwrap_or(0);
latest_ts.get(kind_str.as_str()).copied().unwrap_or(0);
if ts >= prev_ts {
expected.insert(kind_str.to_string(), hash);
latest_ts.insert(kind_str.to_string(), ts);
+1 -1
View File
@@ -265,7 +265,7 @@ impl CorsTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -94,7 +94,7 @@
use crate::fixtures::{send_and_verify_accepted, send_and_verify_rejected};
use crate::specs::grasp01::SpecRef;
use crate::{AuditClient, AuditResult, FixtureKind, TestContext, TestResult};
use nostr_sdk::{Event, Filter, Kind, Tag, TagKind, Timestamp, ToBech32};
use nostr_sdk::prelude::{Event, Filter, Kind, Tag, Timestamp, ToBech32};
use std::time::Duration;
/// Test suite for GRASP-01 event acceptance policy
@@ -186,7 +186,7 @@ impl EventAcceptancePolicyTests {
let repo_id = event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in announcement")?
.to_string();
@@ -221,13 +221,13 @@ impl EventAcceptancePolicyTests {
// Verify key tags are present
let has_clone_tag = stored_event.tags.iter().any(|t| {
t.kind() == TagKind::Custom("clone".into())
&& t.content().map(|c| c.contains(&http_url)).unwrap_or(false)
t.kind() == "clone" && t.content().map(|c| c.contains(&http_url)).unwrap_or(false)
});
let has_relays_tag = stored_event.tags.iter().any(|t| {
t.kind() == TagKind::Custom("relays".into()) && t.content() == Some(&relay_url)
});
let has_relays_tag = stored_event
.tags
.iter()
.any(|t| t.kind() == "relays" && t.content() == Some(&relay_url));
if !has_clone_tag {
return Err(format!(
@@ -279,18 +279,12 @@ impl EventAcceptancePolicyTests {
let event = client
.event_builder(Kind::GitRepoAnnouncement, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom("name", vec!["Test Repo No Clone"]))
.tag(Tag::custom(
TagKind::Custom("name".into()),
vec!["Test Repo No Clone"],
))
.tag(Tag::custom(
TagKind::Custom("clone".into()),
"clone",
vec!["https://github.com/user/repo.git"],
)) // NOT this service
.tag(Tag::custom(
TagKind::Custom("relays".into()),
vec![relay_url.clone()],
)) // Correct relay
.tag(Tag::custom("relays", vec![relay_url.clone()])) // Correct relay
.build(client.keys())
.map_err(|e| format!("Failed to build event: {}", e))?;
@@ -360,22 +354,16 @@ impl EventAcceptancePolicyTests {
let event = client
.event_builder(Kind::GitRepoAnnouncement, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom("name", vec!["Test Repo No Relays"]))
.tag(Tag::custom(
TagKind::custom("name"),
vec!["Test Repo No Relays"],
))
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"{}/{}/test-repo.git",
http_url,
client.public_key()
)],
)) // Correct clone
.tag(Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.damus.io"],
)) // NOT this service
.tag(Tag::custom("relays", vec!["wss://relay.damus.io"])) // NOT this service
.build(client.keys())
.map_err(|e| format!("Failed to build event: {}", e))?;
@@ -454,7 +442,7 @@ impl EventAcceptancePolicyTests {
let repo_id = recursive_state
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in recursive maintainer state")?
.to_string();
@@ -479,12 +467,12 @@ impl EventAcceptancePolicyTests {
)
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("name"),
"name",
vec![format!("{} (recursive maintainer view)", repo_id)],
))
// Clone points to another server, NOT the GRASP server
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"https://another-grasp-server.com/{}/{}.git",
recursive_maintainer_npub, repo_id
@@ -492,7 +480,7 @@ impl EventAcceptancePolicyTests {
))
// Relays also points elsewhere (not this server)
.tag(Tag::custom(
TagKind::custom("relays"),
"relays",
vec!["wss://relay.damus.io"],
))
.build(client.recursive_maintainer_keys())
@@ -661,10 +649,10 @@ impl EventAcceptancePolicyTests {
// Create comment with `A` tag (root reference to repo)
let tags = vec![
Tag::custom(
TagKind::custom("A"),
"A",
vec![a_tag_value.clone(), "".to_string(), "root".to_string()],
),
Tag::custom(TagKind::custom("K"), vec!["30617".to_string()]),
Tag::custom("K", vec!["30617".to_string()]),
Tag::public_key(repo.pubkey),
];
@@ -715,7 +703,7 @@ impl EventAcceptancePolicyTests {
let a_tag_value = format!("30617:{}:{}", repo.pubkey, repo_id);
// Create kind 1 note with `q` tag (quote reference to repo)
let tags = vec![Tag::custom(TagKind::custom("q"), vec![a_tag_value])];
let tags = vec![Tag::custom("q", vec![a_tag_value])];
let note = client
.event_builder(Kind::TextNote, "Mentioning this repo")
@@ -766,8 +754,7 @@ impl EventAcceptancePolicyTests {
let repo_b = Self::create_test_repo(client, "repo-b").await?;
// Create Issue B that quotes accepted Issue A via 'q' tag (should make it accepted)
let additional_tags =
vec![Tag::custom(TagKind::custom("q"), vec![issue_a.id.to_hex()])];
let additional_tags = vec![Tag::custom("q", vec![issue_a.id.to_hex()])];
let issue_b = client
.create_issue(&repo_b, "Issue B", "issue content", additional_tags)
@@ -853,7 +840,7 @@ impl EventAcceptancePolicyTests {
let kind1_a = client
.event_builder(Kind::TextNote, "Note A about repo")
.tags(vec![Tag::custom(TagKind::custom("q"), vec![a_tag_value])])
.tags(vec![Tag::custom("q", vec![a_tag_value])])
.build(client.keys())
.map_err(|e| format!("Failed to build kind1 A: {}", e))?;
@@ -938,19 +925,16 @@ impl EventAcceptancePolicyTests {
let issue_tags = vec![
// Reference to accepted repo
Tag::custom(
TagKind::custom("a"),
"a",
vec![format!(
"30617:{}:{}",
repo.pubkey,
Self::extract_d_tag(&repo).unwrap()
)],
),
Tag::custom(
TagKind::custom("subject"),
vec!["Issue referencing kind1".to_string()],
),
Tag::custom("subject", vec!["Issue referencing kind1".to_string()]),
// Quote the Kind 1 that hasn't been sent yet
Tag::custom(TagKind::custom("q"), vec![kind1_note.id.to_hex()]),
Tag::custom("q", vec![kind1_note.id.to_hex()]),
];
let issue = client
@@ -1003,14 +987,14 @@ impl EventAcceptancePolicyTests {
let comment_b_tags = vec![
// NIP-22 tags for the original issue
Tag::custom(
TagKind::custom("E"),
"E",
vec![issue.id.to_hex(), "".to_string(), "root".to_string()],
),
Tag::event(issue.id),
Tag::custom(TagKind::custom("K"), vec![issue.kind.as_u16().to_string()]),
Tag::custom("K", vec![issue.kind.as_u16().to_string()]),
Tag::public_key(issue.pubkey),
// Quote Comment A which hasn't been sent yet
Tag::custom(TagKind::custom("q"), vec![comment_a.id.to_hex()]),
Tag::custom("q", vec![comment_a.id.to_hex()]),
];
let comment_b = client
@@ -1077,8 +1061,8 @@ impl EventAcceptancePolicyTests {
let kind1_b = client
.event_builder(Kind::TextNote, "Note B mentioning Note A")
.tags(vec![
Tag::custom(TagKind::custom("q"), vec![a_tag_value]), // Quote repo (accepted)
Tag::event(kind1_a.id), // Mention unsent Kind 1 A
Tag::custom("q", vec![a_tag_value]), // Quote repo (accepted)
Tag::event(kind1_a.id), // Mention unsent Kind 1 A
])
.build(client.keys())
.map_err(|e| format!("Failed to build kind1 B: {}", e))?;
@@ -1188,11 +1172,8 @@ impl EventAcceptancePolicyTests {
// Create comment that references ONLY repo B (unaccepted)
let tags = vec![
Tag::custom(
TagKind::custom("A"),
vec![repo_b_a_tag, "".to_string(), "root".to_string()],
),
Tag::custom(TagKind::custom("K"), vec!["30617".to_string()]),
Tag::custom("A", vec![repo_b_a_tag, "".to_string(), "root".to_string()]),
Tag::custom("K", vec!["30617".to_string()]),
Tag::public_key(repo_b.pubkey),
];
+3 -3
View File
@@ -68,7 +68,7 @@ impl GitCloneTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -189,7 +189,7 @@ impl GitCloneTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag")
.unwrap()
@@ -293,7 +293,7 @@ impl GitCloneTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
+3 -3
View File
@@ -81,7 +81,7 @@ impl GitFilterTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -203,7 +203,7 @@ impl GitFilterTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag")
.unwrap()
@@ -314,7 +314,7 @@ impl GitFilterTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag")
.unwrap()
+3 -2
View File
@@ -92,7 +92,8 @@ impl Nip01SmokeTests {
let direct_filter = Filter::new().kind(Kind::GitRepoAnnouncement).id(event_id);
let direct_events = client
.client()
.fetch_events(direct_filter, std::time::Duration::from_secs(5))
.fetch_events(direct_filter)
.timeout(std::time::Duration::from_secs(5))
.await
.map_err(|e| format!("Direct query failed: {}", e))?;
let direct_vec: Vec<Event> = direct_events.into_iter().collect();
@@ -209,7 +210,7 @@ impl Nip01SmokeTests {
let wrong_keys = Keys::generate();
let wrong_event = EventBuilder::new(event.kind, event.content.clone())
.tags(event.tags.clone())
.sign_with_keys(&wrong_keys)
.finalize(&wrong_keys)
.map_err(|e| format!("Failed to build wrong event: {}", e))?;
// Create event JSON with mismatched pubkey and signature
+21 -21
View File
@@ -102,7 +102,7 @@ impl PurgatoryTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in repo announcement")?
.to_string();
@@ -168,7 +168,7 @@ impl PurgatoryTests {
let repo_id = state_event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in state event")?
.to_string();
@@ -242,7 +242,7 @@ impl PurgatoryTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in repo announcement")?
.to_string();
@@ -323,7 +323,7 @@ impl PurgatoryTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in repo announcement")?
.to_string();
@@ -332,11 +332,11 @@ impl PurgatoryTests {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec!["abc123".to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.build(client.keys())
@@ -417,7 +417,7 @@ impl PurgatoryTests {
let repo_id = existing_state
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in state event")?
.to_string();
@@ -428,11 +428,11 @@ impl PurgatoryTests {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec!["deadbeefdeadbeefdeadbeefdeadbeefdeadbeef".to_string()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.build(client.keys())
@@ -682,7 +682,7 @@ impl PurgatoryTests {
let repo_id = existing_state
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in state event")?
.to_string();
@@ -716,11 +716,11 @@ impl PurgatoryTests {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![unique_commit.clone()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.build(client.keys())
@@ -744,7 +744,7 @@ impl PurgatoryTests {
let deletion = client
.event_builder(Kind::EventDeletion, "")
.tag(Tag::event(state_event.id))
.tag(Tag::custom(TagKind::custom("k"), vec!["30618"]))
.tag(Tag::custom("k", vec!["30618"]))
.build(client.keys())
.map_err(|e| { cleanup(); format!("Failed to build deletion event: {}", e) })?;
@@ -811,7 +811,7 @@ impl PurgatoryTests {
let repo_id = existing_state
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("Missing d tag in state event")?
.to_string();
@@ -839,15 +839,15 @@ impl PurgatoryTests {
.event_builder(Kind::GitRepoAnnouncement, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("{}/{}/{}.git", http_url, npub, repo_id)],
))
.tag(Tag::custom(
TagKind::custom("relays"),
"relays",
vec![relay_url.clone()],
))
.tag(Tag::custom(
TagKind::custom("maintainers"),
"maintainers",
vec![new_maintainer_hex.clone()],
))
.build(client.keys())
@@ -883,11 +883,11 @@ impl PurgatoryTests {
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
"refs/heads/main",
vec![unique_commit.clone()],
))
.tag(Tag::custom(
TagKind::custom("HEAD"),
"HEAD",
vec!["ref: refs/heads/main".to_string()],
))
.build(&new_maintainer_keys)
@@ -913,8 +913,8 @@ impl PurgatoryTests {
let deletion = client
.event_builder(Kind::EventDeletion, "")
.tag(Tag::custom(TagKind::custom("a"), vec![coord]))
.tag(Tag::custom(TagKind::custom("k"), vec!["30618"]))
.tag(Tag::custom("a", vec![coord]))
.tag(Tag::custom("k", vec!["30618"]))
.build(&new_maintainer_keys)
.map_err(|e| { cleanup(); format!("Failed to build deletion event: {}", e) })?;
@@ -221,7 +221,7 @@ async fn setup_pr_test_repo(
let repo_id = repo_event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.ok_or("No repo identifier in announcement")?
.to_string();
@@ -424,7 +424,7 @@ impl PushAuthorizationTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap()
.to_string();
@@ -569,7 +569,7 @@ impl PushAuthorizationTests {
let repo_id = match state_event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -809,7 +809,7 @@ impl PushAuthorizationTests {
let repo_id = match state_event
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -880,10 +880,7 @@ impl PushAuthorizationTests {
let rogue_state = match client
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
vec![new_commit.clone()],
))
.tag(Tag::custom("refs/heads/main", vec![new_commit.clone()]))
.build(&rogue_keys)
{
Ok(e) => e,
@@ -973,7 +970,7 @@ impl PushAuthorizationTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap()
.to_string();
@@ -1121,7 +1118,7 @@ impl PushAuthorizationTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap_or("unknown")
.to_string();
@@ -1209,7 +1206,7 @@ impl PushAuthorizationTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap_or("unknown")
.to_string();
@@ -1300,7 +1297,7 @@ impl PushAuthorizationTests {
let repo_id = repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.unwrap_or("unknown")
.to_string();
@@ -1436,7 +1433,7 @@ impl PushAuthorizationTests {
let repo_id = match valid_repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -1539,7 +1536,7 @@ impl PushAuthorizationTests {
let repo_id = match valid_repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -1599,16 +1596,13 @@ impl PushAuthorizationTests {
let state_event = match client
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom("HEAD", vec!["refs/heads/develop1".to_string()]))
.tag(Tag::custom(
TagKind::custom("HEAD"),
vec!["refs/heads/develop1".to_string()],
))
.tag(Tag::custom(
TagKind::custom("refs/heads/develop1"),
"refs/heads/develop1",
vec![commit_hash.clone()],
))
.tag(Tag::custom(
TagKind::custom("refs/heads/develop"),
"refs/heads/develop",
vec![RECURSIVE_MAINTAINER_DETERMINISTIC_COMMIT_HASH.to_string()],
))
.build(client.keys())
@@ -70,7 +70,7 @@ impl RepositoryCreationTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -150,7 +150,7 @@ impl RepositoryCreationTests {
let repo_id = match repo
.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
{
Some(id) => id.to_string(),
@@ -108,9 +108,9 @@ impl EventAcceptanceTests {
Kind::GitPullRequest,
"grasp-06 audit: PR for un-announced coord, clone tag on /prs/",
)
.tag(Tag::custom(TagKind::custom("a"), vec![a_tag_value]))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_hex]))
.tag(Tag::custom(TagKind::custom("clone"), vec![clone_url]))
.tag(Tag::custom("a", vec![a_tag_value]))
.tag(Tag::custom("c", vec![commit_hex]))
.tag(Tag::custom("clone", vec![clone_url]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
@@ -209,9 +209,9 @@ impl EventAcceptanceTests {
Kind::GitPullRequest,
"grasp-06 audit: orphan PR must land in purgatory, not be served",
)
.tag(Tag::custom(TagKind::custom("a"), vec![a_tag_value]))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_hex]))
.tag(Tag::custom(TagKind::custom("clone"), vec![clone_url]))
.tag(Tag::custom("a", vec![a_tag_value]))
.tag(Tag::custom("c", vec![commit_hex]))
.tag(Tag::custom("clone", vec![clone_url]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
let event_id = event.id;
@@ -313,12 +313,9 @@ impl EventAcceptanceTests {
Kind::GitPullRequest,
"grasp-06 audit: PR for un-announced coord, clone tag on foreign host",
)
.tag(Tag::custom(TagKind::custom("a"), vec![a_tag_value]))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_hex]))
.tag(Tag::custom(
TagKind::custom("clone"),
vec![foreign_clone_url],
))
.tag(Tag::custom("a", vec![a_tag_value]))
.tag(Tag::custom("c", vec![commit_hex]))
.tag(Tag::custom("clone", vec![foreign_clone_url]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
+7 -7
View File
@@ -175,11 +175,11 @@ impl MirroringTests {
"grasp-06 audit: PR via /prs/ should mirror to announced repo",
)
.tag(Tag::custom(
TagKind::custom("a"),
"a",
vec![format!("30617:{}:{}", owner_pubkey_hex, repo_id)],
))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_hash.clone()]))
.tag(Tag::custom(TagKind::custom("clone"), vec![prs_url.clone()]))
.tag(Tag::custom("c", vec![commit_hash.clone()]))
.tag(Tag::custom("clone", vec![prs_url.clone()]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
let pr_event_id_typed = pr_event.id;
@@ -409,11 +409,11 @@ impl MirroringTests {
"grasp-06 audit: git-first PR via /prs/ should still promote + mirror",
)
.tag(Tag::custom(
TagKind::custom("a"),
"a",
vec![format!("30617:{}:{}", owner_pubkey_hex, repo_id)],
))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_hash.clone()]))
.tag(Tag::custom(TagKind::custom("clone"), vec![prs_url.clone()]))
.tag(Tag::custom("c", vec![commit_hash.clone()]))
.tag(Tag::custom("clone", vec![prs_url.clone()]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
let pr_event_id_typed = pr_event.id;
@@ -678,7 +678,7 @@ impl MirroringTests {
fn extract_repo_id(repo: &Event) -> Result<String, String> {
repo.tags
.iter()
.find(|t| t.kind() == TagKind::d())
.find(|t| t.kind() == "d")
.and_then(|t| t.content())
.map(str::to_string)
.ok_or_else(|| "Missing `d` tag in repo announcement".to_string())
@@ -64,7 +64,7 @@ impl PrsEndpointTests {
// 3. Build a /prs/<valid-npub>/<id>.git/info/refs URL with a known-valid
// npub. Using a fresh random npub guarantees no implementation could
// have a repo there by accident.
let probe_keys = nostr_sdk::Keys::generate();
let probe_keys = nostr_sdk::prelude::Keys::generate();
let probe_npub = probe_keys
.public_key()
.to_bech32()
@@ -139,7 +139,7 @@ impl PrsEndpointTests {
// 2. Build a /prs/<valid-npub>/<random-id>.git URL. Using fresh
// random keys and a UUID identifier guarantees no implementation
// could have prior state for this path.
let probe_keys = nostr_sdk::Keys::generate();
let probe_keys = nostr_sdk::prelude::Keys::generate();
let probe_npub = probe_keys
.public_key()
.to_bech32()
@@ -268,7 +268,7 @@ impl PrsEndpointTests {
// 64-hex without pulling in a `rand` dep. No matching event
// will ever exist; that's fine — the spec's contract here is
// push acceptance, not purgatory release.
let event_id_hex = nostr_sdk::Keys::generate().public_key().to_hex();
let event_id_hex = nostr_sdk::prelude::Keys::generate().public_key().to_hex();
let refname = format!("refs/nostr/{}", event_id_hex);
// 5. Push. Success means: relay routes /prs/, accepts our ref
@@ -473,9 +473,9 @@ impl PushValidationTests {
Kind::GitPullRequest,
"grasp-06 audit: PR with c tag pointing at commit B (not the pushed commit A)",
)
.tag(Tag::custom(TagKind::custom("a"), vec![a_tag_value]))
.tag(Tag::custom(TagKind::custom("c"), vec![commit_b.clone()]))
.tag(Tag::custom(TagKind::custom("clone"), vec![prs_url.clone()]))
.tag(Tag::custom("a", vec![a_tag_value]))
.tag(Tag::custom("c", vec![commit_b.clone()]))
.tag(Tag::custom("clone", vec![prs_url.clone()]))
.build(client.pr_author_keys())
.map_err(|e| format!("Failed to build PR event: {}", e))?;
let pr_event_id_typed = pr_event.id;
@@ -586,7 +586,7 @@ impl PushValidationTests {
/// Fresh random keys + UUID ensure the path has no prior state and no
/// implementation could have a repo there by accident.
fn build_fresh_prs_url(http_url: &str) -> Result<String, String> {
let probe_keys = nostr_sdk::Keys::generate();
let probe_keys = nostr_sdk::prelude::Keys::generate();
let probe_npub = probe_keys
.public_key()
.to_bech32()
+3 -9
View File
@@ -1403,7 +1403,7 @@ fn describe_known_event_mismatch(
#[cfg(test)]
mod tests {
use super::*;
use nostr_sdk::prelude::{EventBuilder, Keys, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Tag};
fn create_test_keys() -> Keys {
Keys::generate()
@@ -1419,10 +1419,7 @@ mod tests {
.iter()
.map(|k| k.public_key().to_hex())
.collect();
tags.push(Tag::custom(
"maintainers",
maintainer_pubkeys,
));
tags.push(Tag::custom("maintainers", maintainer_pubkeys));
}
// Add clone and relay tags for validity
@@ -1430,10 +1427,7 @@ mod tests {
"clone",
vec!["https://example.com/test.git".to_string()],
));
tags.push(Tag::custom(
"relays",
vec!["wss://example.com".to_string()],
));
tags.push(Tag::custom("relays", vec!["wss://example.com".to_string()]));
EventBuilder::new(Kind::GitRepoAnnouncement, "Test repo")
.tags(tags)
+14 -35
View File
@@ -1849,7 +1849,7 @@ mod tests {
#[test]
fn test_extract_identifier_from_pr_event_valid() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
let tags = vec![Tag::custom(
@@ -1868,13 +1868,10 @@ mod tests {
#[test]
fn test_extract_identifier_from_pr_event_missing_tag() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
let tags = vec![Tag::custom(
"c",
vec!["commit123".to_string()],
)];
let tags = vec![Tag::custom("c", vec!["commit123".to_string()])];
let event = EventBuilder::new(Kind::from(1618), "PR content")
.tags(tags)
@@ -1887,7 +1884,7 @@ mod tests {
#[test]
fn test_extract_identifier_from_pr_event_wrong_kind_a_tag() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
let tags = vec![Tag::custom(
@@ -1941,20 +1938,14 @@ mod tests {
#[test]
fn test_extract_tagged_owners_from_pr_events_with_a_tags() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
// Create a PR event with `a` tags referencing repos
let tags = vec![
Tag::custom(
"a",
vec!["30617:abc123def456:test-repo".to_string()],
),
Tag::custom(
"a",
vec!["30617:789xyz000111:another-repo".to_string()],
),
Tag::custom("a", vec!["30617:abc123def456:test-repo".to_string()]),
Tag::custom("a", vec!["30617:789xyz000111:another-repo".to_string()]),
Tag::custom("c", vec!["commit123".to_string()]),
];
@@ -1971,20 +1962,14 @@ mod tests {
#[test]
fn test_extract_tagged_owners_from_pr_events_deduplicates() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
// Create two events with overlapping owners
let tags1 = vec![Tag::custom(
"a",
vec!["30617:same_owner:repo1".to_string()],
)];
let tags1 = vec![Tag::custom("a", vec!["30617:same_owner:repo1".to_string()])];
let tags2 = vec![Tag::custom(
"a",
vec!["30617:same_owner:repo2".to_string()],
)];
let tags2 = vec![Tag::custom("a", vec!["30617:same_owner:repo2".to_string()])];
let event1 = EventBuilder::new(Kind::from(1618), "PR 1")
.tags(tags1)
@@ -2003,16 +1988,13 @@ mod tests {
#[test]
fn test_extract_tagged_owners_ignores_non_30617_a_tags() {
use nostr_sdk::prelude::{EventBuilder, Keys, Kind, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind, Tag};
let keys = Keys::generate();
// Create a PR event with a non-30617 `a` tag
let tags = vec![
Tag::custom(
"a",
vec!["30617:valid_owner:test-repo".to_string()],
),
Tag::custom("a", vec!["30617:valid_owner:test-repo".to_string()]),
Tag::custom(
"a",
vec!["30618:state_event:test-repo".to_string()], // Not 30617
@@ -2037,14 +2019,11 @@ mod tests {
created_at: u64,
nonce: &str,
) -> RepositoryState {
use nostr_sdk::prelude::{EventBuilder, Kind, Tag, Timestamp};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Kind, Tag, Timestamp};
let tags = vec![
Tag::custom("d", vec![identifier.to_string()]),
Tag::custom(
"refs/heads/main",
vec![format!("abc123{}", nonce)],
),
Tag::custom("refs/heads/main", vec![format!("abc123{}", nonce)]),
];
let event = EventBuilder::new(Kind::from(30618), nonce)
+1 -1
View File
@@ -17,9 +17,9 @@ use hyper::server::conn::http1;
use hyper::service::Service;
use hyper::{Method, Request, Response};
use hyper_util::rt::TokioIo;
use nostr_relay_builder::LocalRelay;
use nostr::hashes::sha1::Hash as Sha1Hash;
use nostr::hashes::{Hash, HashEngine};
use nostr_relay_builder::LocalRelay;
use nostr_sdk::prelude::PublicKey;
use tokio::net::TcpListener;
-1
View File
@@ -61,7 +61,6 @@ fn duplicate(message: impl Into<std::borrow::Cow<'static, str>>) -> WritePolicyR
WritePolicyResult::ok_msg(MachineReadablePrefix::Duplicate, message)
}
/// NIP-34 Write Policy with Full GRASP-01 Event Validation
///
/// Validates all events according to GRASP-01 specification using modular sub-policies:
+15 -58
View File
@@ -77,11 +77,7 @@ impl RepositoryAnnouncement {
.collect();
// return error if mutliple clone tags (incorrect formatting)
let clone_tag_count = event
.tags
.iter()
.filter(|t| t.kind() == "clone")
.count();
let clone_tag_count = event.tags.iter().filter(|t| t.kind() == "clone").count();
if clone_tag_count > 1 {
return Err(anyhow::anyhow!("multiple clone tags found. correct format is single clone tag with multiple values"));
@@ -514,7 +510,7 @@ pub fn validate_state(event: &Event) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use nostr_sdk::{EventBuilder, Keys};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys};
fn create_test_keys() -> Keys {
Keys::generate()
@@ -528,10 +524,7 @@ mod tests {
) -> Event {
use nostr_sdk::prelude::Tag;
let mut tags = vec![Tag::custom(
"d",
vec![identifier.to_string()],
)];
let mut tags = vec![Tag::custom("d", vec![identifier.to_string()])];
// NIP-34: Single clone tag with multiple values
if !clone_urls.is_empty() {
@@ -543,10 +536,7 @@ mod tests {
// NIP-34: Single relays tag with multiple values
if !relays.is_empty() {
tags.push(Tag::custom(
"relays",
relays.iter().map(|s| s.to_string()),
));
tags.push(Tag::custom("relays", relays.iter().map(|s| s.to_string())));
}
EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository")
@@ -558,10 +548,7 @@ mod tests {
fn create_state_event(keys: &Keys, identifier: &str, branches: Vec<(&str, &str)>) -> Event {
use nostr_sdk::prelude::Tag;
let mut tags = vec![Tag::custom(
"d",
vec![identifier.to_string()],
)];
let mut tags = vec![Tag::custom("d", vec![identifier.to_string()])];
for (branch, commit) in branches {
tags.push(Tag::custom(
@@ -751,10 +738,7 @@ mod tests {
"clone",
vec!["https://gitnostr.com/alice/test-repo.git".to_string()],
),
Tag::custom(
"relays",
vec!["wss://gitnostr.com".to_string()],
),
Tag::custom("relays", vec!["wss://gitnostr.com".to_string()]),
];
// Add maintainer using NIP-34 "maintainers" tag format
@@ -782,16 +766,10 @@ mod tests {
use nostr_sdk::prelude::Tag;
let keys = create_test_keys();
let mut tags = vec![Tag::custom(
"d",
vec!["test-repo".to_string()],
)];
let mut tags = vec![Tag::custom("d", vec!["test-repo".to_string()])];
// Add branch
tags.push(Tag::custom(
"refs/heads/main",
vec!["a1b2c3d4".to_string()],
));
tags.push(Tag::custom("refs/heads/main", vec!["a1b2c3d4".to_string()]));
// Add tag
tags.push(Tag::custom(
@@ -816,10 +794,7 @@ mod tests {
use nostr_sdk::prelude::Tag;
let keys = create_test_keys();
let mut tags = vec![Tag::custom(
"d",
vec!["test-repo".to_string()],
)];
let mut tags = vec![Tag::custom("d", vec!["test-repo".to_string()])];
// Add branch
tags.push(Tag::custom(
@@ -849,10 +824,7 @@ mod tests {
use nostr_sdk::prelude::Tag;
let keys = create_test_keys();
let mut tags = vec![Tag::custom(
"d",
vec!["test-repo".to_string()],
)];
let mut tags = vec![Tag::custom("d", vec!["test-repo".to_string()])];
// Add branch
tags.push(Tag::custom(
@@ -861,10 +833,7 @@ mod tests {
));
// Add HEAD without "ref: " prefix (also valid)
tags.push(Tag::custom(
"HEAD",
vec!["refs/heads/develop".to_string()],
));
tags.push(Tag::custom("HEAD", vec!["refs/heads/develop".to_string()]));
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
@@ -884,10 +853,7 @@ mod tests {
let keys = create_test_keys();
let tags = vec![
Tag::custom("d", vec!["test-repo".to_string()]),
Tag::custom(
"refs/heads/main",
vec!["a1b2c3d4".to_string()],
),
Tag::custom("refs/heads/main", vec!["a1b2c3d4".to_string()]),
];
let event = EventBuilder::new(Kind::RepoState, "")
@@ -906,22 +872,13 @@ mod tests {
use nostr_sdk::prelude::Tag;
let keys = create_test_keys();
let mut tags = vec![Tag::custom(
"d",
vec!["test-repo".to_string()],
)];
let mut tags = vec![Tag::custom("d", vec!["test-repo".to_string()])];
// Add branch for "main"
tags.push(Tag::custom(
"refs/heads/main",
vec!["a1b2c3d4".to_string()],
));
tags.push(Tag::custom("refs/heads/main", vec!["a1b2c3d4".to_string()]));
// HEAD points to "develop" which doesn't exist in branches
tags.push(Tag::custom(
"HEAD",
vec!["refs/heads/develop".to_string()],
));
tags.push(Tag::custom("HEAD", vec!["refs/heads/develop".to_string()]));
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
+2 -8
View File
@@ -297,10 +297,7 @@ mod tests {
use std::sync::Arc;
fn make_context() -> PolicyContext {
let db = Arc::new(MemoryDatabase::with_opts(MemoryDatabaseOptions {
events: true,
max_events: None,
}));
let db = Arc::new(nostr_memory::MemoryDatabase::unbounded());
let purgatory = Arc::new(Purgatory::new(PathBuf::new()));
let config = crate::config::Config::for_testing();
PolicyContext::new_for_test("test.example.com", db, PathBuf::new(), purgatory, config)
@@ -310,10 +307,7 @@ mod tests {
EventBuilder::new(Kind::GitRepoAnnouncement, "")
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
"clone",
vec!["https://example.com/repo.git"],
),
Tag::custom("clone", vec!["https://example.com/repo.git"]),
])
.finalize(keys)
.unwrap()
+1 -1
View File
@@ -3,12 +3,12 @@ use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use nostr_relay_builder::builder::WritePolicyResult;
use nostr_relay_builder::prelude::MachineReadablePrefix;
/// State Policy - State event validation + ref alignment
///
/// Handles validation of NIP-34 repository state events (kind 30618)
/// and aligns git refs with authorized state according to GRASP-01.
use nostr_relay_builder::prelude::Event;
use nostr_relay_builder::prelude::MachineReadablePrefix;
use super::PolicyContext;
use crate::git;
+1 -1
View File
@@ -347,7 +347,7 @@ pub fn diagnose_state_mismatch(
#[cfg(test)]
mod tests {
use super::*;
use nostr_sdk::prelude::{EventBuilder, Keys, Tag};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Tag};
fn create_test_state_event(identifier: &str, refs: Vec<(&str, &str)>) -> Event {
let keys = Keys::generate();
+26 -79
View File
@@ -26,8 +26,8 @@ pub use types::{
};
use dashmap::DashMap;
use nostr_sdk::prelude::*;
use nostr_sdk::prelude::ToBech32;
use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::HashSet;
@@ -1774,9 +1774,7 @@ mod tests {
// Add some test data
let keys = Keys::generate();
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
purgatory.add_state(
event.clone(),
@@ -1838,9 +1836,7 @@ mod tests {
assert!(!purgatory.has_pending_events("test-repo"));
// Add a state event
let event = EventBuilder::text_note("state")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("state").finalize(&keys).unwrap();
purgatory.add_state(event, "test-repo".to_string(), keys.public_key(), false);
// Now should have pending events
@@ -1932,9 +1928,7 @@ mod tests {
fn test_pr_event_vs_placeholder() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let event = EventBuilder::text_note("test PR")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test PR").finalize(&keys).unwrap();
// Add a PR event with actual event
purgatory.add_pr(
@@ -1989,9 +1983,7 @@ fn test_cleanup_removes_expired_entries() {
let state_event = EventBuilder::text_note("state event")
.finalize(&keys)
.unwrap();
let pr_event = EventBuilder::text_note("pr event")
.finalize(&keys)
.unwrap();
let pr_event = EventBuilder::text_note("pr event").finalize(&keys).unwrap();
// Add entries to purgatory
purgatory.add_state(
@@ -2047,9 +2039,7 @@ fn test_cleanup_preserves_non_expired_entries() {
let state_event = EventBuilder::text_note("state event")
.finalize(&keys)
.unwrap();
let pr_event = EventBuilder::text_note("pr event")
.finalize(&keys)
.unwrap();
let pr_event = EventBuilder::text_note("pr event").finalize(&keys).unwrap();
// Add fresh entries
purgatory.add_state(
@@ -2086,12 +2076,8 @@ fn test_cleanup_mixed_expired_and_fresh() {
let keys = Keys::generate();
// Add multiple state events for same repo
let event1 = EventBuilder::text_note("event1")
.finalize(&keys)
.unwrap();
let event2 = EventBuilder::text_note("event2")
.finalize(&keys)
.unwrap();
let event1 = EventBuilder::text_note("event1").finalize(&keys).unwrap();
let event2 = EventBuilder::text_note("event2").finalize(&keys).unwrap();
purgatory.add_state(event1, "test-repo".to_string(), keys.public_key(), false);
purgatory.add_state(event2, "test-repo".to_string(), keys.public_key(), false);
@@ -2104,12 +2090,8 @@ fn test_cleanup_mixed_expired_and_fresh() {
}
// Add PR events
let pr1 = EventBuilder::text_note("pr1")
.finalize(&keys)
.unwrap();
let pr2 = EventBuilder::text_note("pr2")
.finalize(&keys)
.unwrap();
let pr1 = EventBuilder::text_note("pr1").finalize(&keys).unwrap();
let pr2 = EventBuilder::text_note("pr2").finalize(&keys).unwrap();
purgatory.add_pr(pr1, "pr-1".to_string(), "commit-1".to_string(), false);
purgatory.add_pr(pr2, "pr-2".to_string(), "commit-2".to_string(), false);
@@ -2139,9 +2121,7 @@ fn test_remove_expired_legacy_method() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let state_event = EventBuilder::text_note("state")
.finalize(&keys)
.unwrap();
let state_event = EventBuilder::text_note("state").finalize(&keys).unwrap();
let pr_event = EventBuilder::text_note("pr").finalize(&keys).unwrap();
purgatory.add_state(state_event, "repo".to_string(), keys.public_key(), false);
@@ -2170,9 +2150,7 @@ fn test_expired_event_tracking() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let state_event = EventBuilder::text_note("state")
.finalize(&keys)
.unwrap();
let state_event = EventBuilder::text_note("state").finalize(&keys).unwrap();
let pr_event = EventBuilder::text_note("pr").finalize(&keys).unwrap();
let state_event_id = state_event.id;
@@ -2221,12 +2199,8 @@ fn test_cleanup_expired_events() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let event1 = EventBuilder::text_note("event1")
.finalize(&keys)
.unwrap();
let event2 = EventBuilder::text_note("event2")
.finalize(&keys)
.unwrap();
let event1 = EventBuilder::text_note("event1").finalize(&keys).unwrap();
let event2 = EventBuilder::text_note("event2").finalize(&keys).unwrap();
let event1_id = event1.id;
let event2_id = event2.id;
@@ -2277,9 +2251,7 @@ fn test_expired_events_prevent_readdition() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
let event_id = event.id;
// Add event to purgatory
@@ -2340,9 +2312,7 @@ fn test_user_can_resubmit_expired_event() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
let event_id = event.id;
// Add event to purgatory
@@ -2452,10 +2422,7 @@ async fn test_save_and_restore_pr_events() {
let keys = Keys::generate();
// Add PR event with actual event
let tags = vec![Tag::custom(
"a",
vec!["30617:abc123:test-repo".to_string()],
)];
let tags = vec![Tag::custom("a", vec!["30617:abc123:test-repo".to_string()])];
let pr_event = EventBuilder::new(Kind::from(1618), "PR content")
.tags(tags)
@@ -2532,9 +2499,7 @@ async fn test_save_and_restore_expired_events() {
let purgatory = Purgatory::new(PathBuf::new());
let keys = Keys::generate();
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
let event_id = event.id;
// Add and expire event
@@ -2673,9 +2638,7 @@ async fn test_downtime_calculation() {
let keys = Keys::generate();
// Add state event
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
purgatory.add_state(event.clone(), "repo".to_string(), keys.public_key(), false);
@@ -2729,9 +2692,7 @@ async fn test_expiry_times_preserved() {
let keys = Keys::generate();
// Add state event
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
purgatory.add_state(event.clone(), "repo".to_string(), keys.public_key(), false);
@@ -2837,10 +2798,7 @@ async fn test_mixed_pr_events_and_placeholders() {
let keys = Keys::generate();
// Add PR event with actual event
let tags = vec![Tag::custom(
"a",
vec!["30617:abc123:test-repo".to_string()],
)];
let tags = vec![Tag::custom("a", vec!["30617:abc123:test-repo".to_string()])];
let pr_event = EventBuilder::new(Kind::from(1618), "PR content")
.tags(tags)
@@ -2894,9 +2852,7 @@ async fn test_file_cleanup_after_successful_restore() {
let keys = Keys::generate();
// Add some data
let event = EventBuilder::text_note("test")
.finalize(&keys)
.unwrap();
let event = EventBuilder::text_note("test").finalize(&keys).unwrap();
purgatory.add_state(event, "repo".to_string(), keys.public_key(), false);
// Save to disk
@@ -3076,12 +3032,8 @@ async fn test_comprehensive_roundtrip() {
);
// Add multiple state events
let state1 = EventBuilder::text_note("state 1")
.finalize(&keys1)
.unwrap();
let state2 = EventBuilder::text_note("state 2")
.finalize(&keys2)
.unwrap();
let state1 = EventBuilder::text_note("state 1").finalize(&keys1).unwrap();
let state2 = EventBuilder::text_note("state 2").finalize(&keys2).unwrap();
purgatory.add_state(
state1.clone(),
@@ -3097,10 +3049,7 @@ async fn test_comprehensive_roundtrip() {
);
// Add PR event
let tags = vec![Tag::custom(
"a",
vec!["30617:abc123:repo1".to_string()],
)];
let tags = vec![Tag::custom("a", vec!["30617:abc123:repo1".to_string()])];
let pr_event = EventBuilder::new(Kind::from(1618), "PR")
.tags(tags)
.finalize(&keys1)
@@ -3116,9 +3065,7 @@ async fn test_comprehensive_roundtrip() {
purgatory.add_pr_placeholder("pr-2".to_string(), "commit-2".to_string());
// Add and expire an event
let expired_event = EventBuilder::text_note("expired")
.finalize(&keys1)
.unwrap();
let expired_event = EventBuilder::text_note("expired").finalize(&keys1).unwrap();
let expired_id = expired_event.id;
purgatory.add_state(
expired_event,
+1 -1
View File
@@ -753,7 +753,7 @@ pub mod mock {
// Create minimal repo data with just clone URLs
// In real tests, you'd set up proper announcements
use crate::nostr::events::RepositoryAnnouncement;
use nostr_sdk::{EventBuilder, Keys, Kind};
use nostr_sdk::prelude::{EventBuilder, FinalizeEvent, Keys, Kind};
let keys = Keys::generate();
let mut announcements = Vec::new();
+44 -33
View File
@@ -56,7 +56,7 @@
//!
//! ```rust,ignore
//! use ngit_grasp::sync::rejected_index::{RejectedEventsIndex, RejectionReason, EventType};
//! use nostr_sdk::{Event, PublicKey};
//! use nostr_sdk::prelude::{Event, PublicKey};
//! use std::time::Duration;
//!
//! let index = RejectedEventsIndex::new(
@@ -829,12 +829,13 @@ impl RejectedEventsIndex {
#[cfg(test)]
mod tests {
use super::*;
use nostr_sdk::{Keys, NostrSigner};
use nostr_sdk::prelude::{FinalizeUnsignedEvent, Keys, SignEvent};
async fn create_test_event() -> Event {
let keys = Keys::generate();
let unsigned = nostr_sdk::prelude::EventBuilder::text_note("test").finalize_unsigned(keys.public_key());
keys.sign_event(unsigned).await.unwrap()
let unsigned = nostr_sdk::prelude::EventBuilder::text_note("test")
.finalize_unsigned(keys.public_key());
keys.sign_event(unsigned).unwrap()
}
#[tokio::test]
@@ -1032,11 +1033,13 @@ mod tests {
let keys1 = Keys::generate();
let keys2 = Keys::generate();
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("test1").finalize_unsigned(keys1.public_key());
let event1 = keys1.sign_event(unsigned1).await.unwrap();
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("test1")
.finalize_unsigned(keys1.public_key());
let event1 = keys1.sign_event(unsigned1).unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("test2").finalize_unsigned(keys2.public_key());
let event2 = keys2.sign_event(unsigned2).await.unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("test2")
.finalize_unsigned(keys2.public_key());
let event2 = keys2.sign_event(unsigned2).unwrap();
// Add two different maintainer repos
index.add_announcement(
@@ -1075,13 +1078,14 @@ mod tests {
let keys = Keys::generate();
// Create an announcement event
let unsigned_ann =
nostr_sdk::prelude::EventBuilder::text_note("announcement").finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).await.unwrap();
let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement")
.finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).unwrap();
// Create a state event
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state").finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).await.unwrap();
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state")
.finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).unwrap();
let pubkey = event_ann.pubkey;
let identifier = "test-repo".to_string();
@@ -1134,13 +1138,14 @@ mod tests {
let keys = Keys::generate();
// Create an announcement event
let unsigned_ann =
nostr_sdk::prelude::EventBuilder::text_note("announcement").finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).await.unwrap();
let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement")
.finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).unwrap();
// Create a state event
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state").finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).await.unwrap();
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state")
.finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).unwrap();
let pubkey = event_ann.pubkey;
let identifier = "test-repo".to_string();
@@ -1280,11 +1285,13 @@ mod tests {
let keys = Keys::generate();
// Create two events
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1").finalize_unsigned(keys.public_key());
let event1 = keys.sign_event(unsigned1).await.unwrap();
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1")
.finalize_unsigned(keys.public_key());
let event1 = keys.sign_event(unsigned1).unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2").finalize_unsigned(keys.public_key());
let event2 = keys.sign_event(unsigned2).await.unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2")
.finalize_unsigned(keys.public_key());
let event2 = keys.sign_event(unsigned2).unwrap();
// Add both events
index.add_announcement(
@@ -1491,13 +1498,14 @@ mod tests {
let keys = Keys::generate();
// Create announcement event
let unsigned_ann =
nostr_sdk::prelude::EventBuilder::text_note("announcement").finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).await.unwrap();
let unsigned_ann = nostr_sdk::prelude::EventBuilder::text_note("announcement")
.finalize_unsigned(keys.public_key());
let event_ann = keys.sign_event(unsigned_ann).unwrap();
// Create state event
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state").finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).await.unwrap();
let unsigned_state = nostr_sdk::prelude::EventBuilder::text_note("state")
.finalize_unsigned(keys.public_key());
let event_state = keys.sign_event(unsigned_state).unwrap();
// Add both types
index.add_announcement(
@@ -1545,14 +1553,17 @@ mod tests {
let keys = Keys::generate();
// Create events with different rejection reasons
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1").finalize_unsigned(keys.public_key());
let event1 = keys.sign_event(unsigned1).await.unwrap();
let unsigned1 = nostr_sdk::prelude::EventBuilder::text_note("event1")
.finalize_unsigned(keys.public_key());
let event1 = keys.sign_event(unsigned1).unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2").finalize_unsigned(keys.public_key());
let event2 = keys.sign_event(unsigned2).await.unwrap();
let unsigned2 = nostr_sdk::prelude::EventBuilder::text_note("event2")
.finalize_unsigned(keys.public_key());
let event2 = keys.sign_event(unsigned2).unwrap();
let unsigned3 = nostr_sdk::prelude::EventBuilder::text_note("event3").finalize_unsigned(keys.public_key());
let event3 = keys.sign_event(unsigned3).await.unwrap();
let unsigned3 = nostr_sdk::prelude::EventBuilder::text_note("event3")
.finalize_unsigned(keys.public_key());
let event3 = keys.sign_event(unsigned3).unwrap();
// Add with different rejection reasons
index.add_announcement(
+34 -37
View File
@@ -67,7 +67,7 @@ async fn start_relay_with_grasp_services(services: &str) -> (Child, String, Path
.join("ngit-grasp");
// Generate a test owner npub
let test_keys = nostr_sdk::Keys::generate();
let test_keys = nostr_sdk::prelude::Keys::generate();
let test_npub = test_keys
.public_key()
.to_bech32()
@@ -145,22 +145,21 @@ async fn test_archive_accepts_matching_grasp_service() {
let tags = vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://git.example.com/user/{}.git", identifier)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.example.com".to_string()],
),
Tag::custom("relays", vec!["wss://relay.example.com".to_string()]),
];
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags)
.sign_with_keys(&keys)
.finalize(&keys)
.expect("Failed to sign announcement");
// Send announcement to relay
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client.add_relay(&url).await.expect("Failed to add relay");
client.connect().await;
@@ -205,22 +204,21 @@ async fn test_archive_rejects_non_matching_grasp_service() {
let tags = vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://github.com/user/{}.git", identifier)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.example.com".to_string()],
),
Tag::custom("relays", vec!["wss://relay.example.com".to_string()]),
];
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags)
.sign_with_keys(&keys)
.finalize(&keys)
.expect("Failed to sign announcement");
// Send announcement to relay
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client.add_relay(&url).await.expect("Failed to add relay");
client.connect().await;
@@ -268,21 +266,20 @@ async fn test_archive_multiple_grasp_services() {
let tags1 = vec![
Tag::identifier(identifier1),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://git.example.com/user/{}.git", identifier1)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.example.com".to_string()],
),
Tag::custom("relays", vec!["wss://relay.example.com".to_string()]),
];
let announcement1 = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags1)
.sign_with_keys(&keys1)
.finalize(&keys1)
.expect("Failed to sign announcement");
let client1 = Client::new(keys1.clone());
let client1 = Client::builder()
.authenticator(SignerAuthenticator::new(keys1.clone()))
.build();
client1.add_relay(&url).await.expect("Failed to add relay");
client1.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
@@ -301,24 +298,23 @@ async fn test_archive_multiple_grasp_services() {
let tags2 = vec![
Tag::identifier(identifier2),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"https://gitlab.example.org/user/{}.git",
identifier2
)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.example.com".to_string()],
),
Tag::custom("relays", vec!["wss://relay.example.com".to_string()]),
];
let announcement2 = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags2)
.sign_with_keys(&keys2)
.finalize(&keys2)
.expect("Failed to sign announcement");
let client2 = Client::new(keys2.clone());
let client2 = Client::builder()
.authenticator(SignerAuthenticator::new(keys2.clone()))
.build();
client2.add_relay(&url).await.expect("Failed to add relay");
client2.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
@@ -337,21 +333,20 @@ async fn test_archive_multiple_grasp_services() {
let tags3 = vec![
Tag::identifier(identifier3),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://github.com/user/{}.git", identifier3)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://relay.example.com".to_string()],
),
Tag::custom("relays", vec!["wss://relay.example.com".to_string()]),
];
let announcement3 = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags3)
.sign_with_keys(&keys3)
.finalize(&keys3)
.expect("Failed to sign announcement");
let client3 = Client::new(keys3.clone());
let client3 = Client::builder()
.authenticator(SignerAuthenticator::new(keys3.clone()))
.build();
client3.add_relay(&url).await.expect("Failed to add relay");
client3.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
@@ -429,7 +424,9 @@ async fn test_archive_read_only_creates_bare_repo() {
identifier,
);
let source_client = Client::new(keys.clone());
let source_client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
source_client
.add_relay(source_relay.url())
.await
+9 -6
View File
@@ -14,7 +14,7 @@
//! let mock = MockRelay::start().await;
//!
//! // Use mock.url() for WebSocket connections
//! let client = Client::new(keys);
//! let client = Client::builder().authenticator(SignerAuthenticator::new(keys)).build();
//! client.add_relay(mock.url()).await.unwrap();
//!
//! // All events are accepted without validation
@@ -249,8 +249,8 @@ async fn handle_request(
/// Derive the Sec-WebSocket-Accept key from the request key.
fn derive_accept_key(request_key: &[u8]) -> String {
use nostr_sdk::hashes::sha1::Hash as Sha1Hash;
use nostr_sdk::hashes::{Hash, HashEngine};
use nostr::hashes::sha1::Hash as Sha1Hash;
use nostr::hashes::{Hash, HashEngine};
const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@@ -308,7 +308,9 @@ mod tests {
// Create a client and connect
let keys = Keys::generate();
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client
.add_relay(mock.url())
.await
@@ -320,7 +322,7 @@ mod tests {
// Create and send a simple event
let event = EventBuilder::text_note("Test note from MockRelay test")
.sign_with_keys(&keys)
.finalize(&keys)
.expect("Failed to sign event");
let result = client.send_event(&event).await;
@@ -329,7 +331,8 @@ mod tests {
// Verify event was stored by fetching it back
let filter = Filter::new().id(event.id);
let events = client
.fetch_events(filter, Duration::from_secs(2))
.fetch_events(filter)
.timeout(Duration::from_secs(2))
.await
.expect("Failed to fetch events");
+1 -1
View File
@@ -11,7 +11,7 @@ pub mod sync_helpers;
pub use git_server::{SimpleGitServer, SmartGitServer};
pub use mock_relay::MockRelay;
pub use port::{PortReservation, reserve_port};
pub use port::{reserve_port, PortReservation};
pub use purgatory_helpers::*;
pub use relay::TestRelay;
pub use sync_helpers::*;
+32 -33
View File
@@ -8,7 +8,7 @@
//!
//! # nostr-sdk 0.43 API Notes
//! - Use field access: `event.id`, `event.tags`, `event.tags.iter()`
//! - Use `Tag::custom(TagKind::custom("name"), vec![...])` syntax
//! - Use `Tag::custom("name", vec![...])` syntax
//! - Use `EventBuilder::new(kind, content).tags(tags)` syntax
use nostr_sdk::prelude::*;
@@ -193,50 +193,41 @@ pub fn create_state_event(
) -> Result<Event, String> {
let mut event_tags = vec![
// d-tag (identifier)
Tag::custom(TagKind::d(), vec![identifier.to_string()]),
Tag::custom("d", vec![identifier.to_string()]),
];
// Add clone URLs
if !clone_urls.is_empty() {
let urls: Vec<String> = clone_urls.iter().map(|s| s.to_string()).collect();
event_tags.push(Tag::custom(TagKind::Clone, urls));
event_tags.push(Tag::custom("clone", urls));
}
// Add relay URLs
if !relay_urls.is_empty() {
let urls: Vec<String> = relay_urls.iter().map(|s| s.to_string()).collect();
event_tags.push(Tag::custom(TagKind::Relays, urls));
event_tags.push(Tag::custom("relays", urls));
}
// Add branch refs (refs/heads/*)
for (name, commit) in branches {
let ref_name = format!("refs/heads/{}", name);
event_tags.push(Tag::custom(
TagKind::Custom(ref_name.into()),
vec![commit.to_string()],
));
event_tags.push(Tag::custom(ref_name, vec![commit.to_string()]));
}
// Add tag refs (refs/tags/*)
for (name, commit) in tags {
let ref_name = format!("refs/tags/{}", name);
event_tags.push(Tag::custom(
TagKind::Custom(ref_name.into()),
vec![commit.to_string()],
));
event_tags.push(Tag::custom(ref_name, vec![commit.to_string()]));
}
// Add HEAD pointing to main (if main exists)
if branches.iter().any(|(name, _)| *name == "main") {
event_tags.push(Tag::custom(
TagKind::Custom("HEAD".into()),
vec!["refs/heads/main".to_string()],
));
event_tags.push(Tag::custom("HEAD", vec!["refs/heads/main".to_string()]));
}
EventBuilder::new(Kind::RepoState, "")
.tags(event_tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign state event: {}", e))
}
@@ -262,14 +253,14 @@ pub fn create_pr_event(
) -> Result<Event, String> {
let tags = vec![
// a-tag referencing the repository
Tag::custom(TagKind::custom("a"), vec![repo_coord.to_string()]),
Tag::custom("a", vec![repo_coord.to_string()]),
// c-tag with the commit hash
Tag::custom(TagKind::custom("c"), vec![commit_hash.to_string()]),
Tag::custom("c", vec![commit_hash.to_string()]),
];
EventBuilder::new(Kind::GitPullRequest, title)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign PR event: {}", e))
}
@@ -310,20 +301,20 @@ pub fn create_pr_event_with_clone(
) -> Result<Event, String> {
let mut tags = vec![
// a-tag referencing the repository
Tag::custom(TagKind::custom("a"), vec![repo_coord.to_string()]),
Tag::custom("a", vec![repo_coord.to_string()]),
// c-tag with the commit hash
Tag::custom(TagKind::custom("c"), vec![commit_hash.to_string()]),
Tag::custom("c", vec![commit_hash.to_string()]),
];
// Add clone URLs if provided
if !clone_urls.is_empty() {
let urls: Vec<String> = clone_urls.iter().map(|s| s.to_string()).collect();
tags.push(Tag::custom(TagKind::Clone, urls));
tags.push(Tag::custom("clone", urls));
}
EventBuilder::new(Kind::GitPullRequest, title)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign PR event: {}", e))
}
@@ -362,17 +353,17 @@ pub fn create_announcement_event(
if !clone_urls.is_empty() {
let urls: Vec<String> = clone_urls.iter().map(|s| s.to_string()).collect();
tags.push(Tag::custom(TagKind::custom("clone"), urls));
tags.push(Tag::custom("clone", urls));
}
if !relay_urls.is_empty() {
let urls: Vec<String> = relay_urls.iter().map(|s| s.to_string()).collect();
tags.push(Tag::custom(TagKind::custom("relays"), urls));
tags.push(Tag::custom("relays", urls));
}
EventBuilder::new(Kind::GitRepoAnnouncement, "")
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign announcement event: {}", e))
}
@@ -395,7 +386,9 @@ pub async fn wait_for_event_served(
timeout: Duration,
) -> Result<Event, String> {
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
client
.add_relay(relay_url)
@@ -409,7 +402,7 @@ pub async fn wait_for_event_served(
for _ in 0..20 {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = client.relays().await;
if relays.values().any(|r| r.is_connected()) {
if relays.values().any(|r| r.status().is_connected()) {
connected = true;
break;
}
@@ -428,7 +421,11 @@ pub async fn wait_for_event_served(
while start.elapsed() < timeout {
let filter = Filter::new().id(*event_id);
match client.fetch_events(filter, Duration::from_secs(2)).await {
match client
.fetch_events(filter)
.timeout(Duration::from_secs(2))
.await
{
Ok(events) => {
if let Some(event) = events.into_iter().next() {
client.disconnect().await;
@@ -470,7 +467,9 @@ pub async fn verify_event_not_served(
check_duration: Duration,
) -> Result<(), String> {
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
client
.add_relay(relay_url)
@@ -484,7 +483,7 @@ pub async fn verify_event_not_served(
for _ in 0..20 {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = client.relays().await;
if relays.values().any(|r| r.is_connected()) {
if relays.values().any(|r| r.status().is_connected()) {
connected = true;
break;
}
@@ -498,7 +497,7 @@ pub async fn verify_event_not_served(
// Check that event is NOT served
let filter = Filter::new().id(*event_id);
match client.fetch_events(filter, check_duration).await {
match client.fetch_events(filter).timeout(check_duration).await {
Ok(events) => {
client.disconnect().await;
if events.is_empty() {
+2 -2
View File
@@ -12,7 +12,7 @@
//! `start_on_reservation_*` constructors — the listener stays bound for
//! the duration of the reservation, eliminating the same-process race.
use nostr_sdk::ToBech32;
use nostr_sdk::prelude::ToBech32;
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};
use std::time::{Duration, Instant};
@@ -306,7 +306,7 @@ impl TestRelay {
.join("ngit-grasp");
// Generate a test owner npub (using a random keypair)
let test_keys = nostr_sdk::Keys::generate();
let test_keys = nostr_sdk::prelude::Keys::generate();
let test_npub = test_keys
.public_key()
.to_bech32()
+36 -29
View File
@@ -7,7 +7,7 @@
//!
//! # nostr-sdk 0.43 API Notes
//! - Use field access: `event.id`, `event.tags`, `event.tags.iter()`
//! - Use `Tag::custom(TagKind::custom("name"), vec![...])` syntax
//! - Use `Tag::custom("name", vec![...])` syntax
//! - Use `EventBuilder::new(kind, content).tags(tags)` syntax
use std::collections::HashMap;
@@ -46,7 +46,9 @@ impl TestClient {
/// * `Ok(TestClient)` on successful connection
/// * `Err(String)` if connection fails after all retries
pub async fn new(relay_url: &str, keys: Keys) -> Result<Self, String> {
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client
.add_relay(relay_url)
@@ -74,7 +76,7 @@ impl TestClient {
for attempt in 0..30 {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = self.client.relays().await;
if relays.values().any(|r| r.is_connected()) {
if relays.values().any(|r| r.status().is_connected()) {
return Ok(());
}
if attempt == 29 {
@@ -112,7 +114,7 @@ impl TestClient {
match self.client.send_event(event).await {
Ok(output) => {
if !output.success.is_empty() {
return Ok(output.val);
return Ok(output.value);
}
// Log failures for debugging
if !output.failed.is_empty() {
@@ -215,16 +217,16 @@ fn build_layer2_issue_with_tag(
tag_variant: TagVariant,
) -> Result<Event, String> {
let tag = match tag_variant {
TagVariant::LowercaseA => Tag::custom(TagKind::custom("a"), vec![repo_coord.to_string()]),
TagVariant::UppercaseA => Tag::custom(TagKind::custom("A"), vec![repo_coord.to_string()]),
TagVariant::QuoteQ => Tag::custom(TagKind::custom("q"), vec![repo_coord.to_string()]),
TagVariant::LowercaseA => Tag::custom("a", vec![repo_coord.to_string()]),
TagVariant::UppercaseA => Tag::custom("A", vec![repo_coord.to_string()]),
TagVariant::QuoteQ => Tag::custom("q", vec![repo_coord.to_string()]),
};
let tags = vec![tag];
EventBuilder::new(Kind::GitIssue, title)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign Layer 2 issue event: {}", e))
}
@@ -257,11 +259,11 @@ pub fn build_layer3_comment_event(
// Choose tag based on kind (NIP-22 uses E, NIP-10 style uses e)
let tag = if kind_num == Kind::Comment.as_u16() {
// NIP-22 comment: uppercase 'E' tag
Tag::custom(TagKind::custom("E"), vec![parent_event_id.to_hex()])
Tag::custom("E", vec![parent_event_id.to_hex()])
} else {
// Kind 1 reply: lowercase 'e' tag with root marker (NIP-10)
Tag::custom(
TagKind::custom("e"),
"e",
vec![parent_event_id.to_hex(), "".to_string(), "root".to_string()],
)
};
@@ -270,7 +272,7 @@ pub fn build_layer3_comment_event(
EventBuilder::new(kind, content)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign Layer 3 comment event: {}", e))
}
@@ -281,13 +283,13 @@ pub fn build_layer3_reply_with_e_tag(
content: &str,
) -> Result<Event, String> {
let tag = Tag::custom(
TagKind::custom("e"),
"e",
vec![parent_event_id.to_hex(), "".to_string(), "root".to_string()],
);
EventBuilder::new(Kind::Custom(1), content)
.tags(vec![tag])
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign Layer 3 reply event: {}", e))
}
@@ -297,11 +299,11 @@ pub fn build_layer3_comment_with_uppercase_e_tag(
parent_event_id: &EventId,
content: &str,
) -> Result<Event, String> {
let tag = Tag::custom(TagKind::custom("E"), vec![parent_event_id.to_hex()]);
let tag = Tag::custom("E", vec![parent_event_id.to_hex()]);
EventBuilder::new(Kind::Comment, content)
.tags(vec![tag])
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign Layer 3 comment event: {}", e))
}
@@ -311,11 +313,11 @@ pub fn build_layer3_quote_with_q_tag(
parent_event_id: &EventId,
content: &str,
) -> Result<Event, String> {
let tag = Tag::custom(TagKind::custom("q"), vec![parent_event_id.to_hex()]);
let tag = Tag::custom("q", vec![parent_event_id.to_hex()]);
EventBuilder::new(Kind::Custom(1), content)
.tags(vec![tag])
.sign_with_keys(keys)
.finalize(keys)
.map_err(|e| format!("Failed to sign Layer 3 quote event: {}", e))
}
@@ -355,13 +357,13 @@ pub fn create_repo_announcement(keys: &Keys, domains: &[&str], identifier: &str)
// Build tags for repository announcement
let tags = vec![
Tag::identifier(identifier),
Tag::custom(TagKind::custom("clone"), clone_urls),
Tag::custom(TagKind::custom("relays"), relay_urls),
Tag::custom("clone", clone_urls),
Tag::custom("relays", relay_urls),
];
EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.expect("Failed to sign repo announcement")
}
@@ -514,7 +516,9 @@ pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: D
loop {
// Create a fresh client for each poll attempt (avoids stale connection state)
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
if client.add_relay(relay_url).await.is_err() {
if tokio::time::Instant::now() >= deadline {
@@ -531,7 +535,7 @@ pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: D
for _ in 0..10 {
tokio::time::sleep(Duration::from_millis(100)).await;
let relays = client.relays().await;
if relays.values().any(|r| r.is_connected()) {
if relays.values().any(|r| r.status().is_connected()) {
connected = true;
break;
}
@@ -540,7 +544,10 @@ pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: D
if connected {
// Use a short fetch window — if the event is there, EOSE comes back quickly
let fetch_timeout = Duration::from_millis(500);
let result = client.fetch_events(filter.clone(), fetch_timeout).await;
let result = client
.fetch_events(filter.clone())
.timeout(fetch_timeout)
.await;
client.disconnect().await;
match result {
@@ -1349,10 +1356,10 @@ pub async fn setup_announcement_on_relay(
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(vec![
Tag::identifier(identifier),
Tag::custom(TagKind::custom("clone"), clone_urls.clone()),
Tag::custom(TagKind::custom("relays"), relay_urls.clone()),
Tag::custom("clone", clone_urls.clone()),
Tag::custom("relays", relay_urls.clone()),
])
.sign_with_keys(keys)
.finalize(keys)
.expect("Failed to sign repo announcement");
// Build state event with all domains' clone URLs
@@ -1471,10 +1478,10 @@ pub async fn run_sync_test(historic_events: &[Event], live_events: &[Event]) ->
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(vec![
Tag::identifier("test-repo"),
Tag::custom(TagKind::custom("clone"), clone_urls.clone()),
Tag::custom(TagKind::custom("relays"), relay_urls.clone()),
Tag::custom("clone", clone_urls.clone()),
Tag::custom("relays", relay_urls.clone()),
])
.sign_with_keys(&keys)
.finalize(&keys)
.expect("Failed to sign repo announcement");
// 5. Create state event referencing the commit
+12 -8
View File
@@ -56,7 +56,7 @@ async fn test_nip77_negentropy_sync_finds_events() {
// Build kind 10317 events (replaceable per pubkey, so two keys = two stored events)
let event1 = EventBuilder::new(Kind::GitUserGraspList, "")
.tags(vec![Tag::identifier("grasp-list-nip77-a")])
.sign_with_keys(&keys1)
.finalize(&keys1)
.expect("Failed to sign event 1");
let event1_id = event1.id;
println!(
@@ -67,7 +67,7 @@ async fn test_nip77_negentropy_sync_finds_events() {
let event2 = EventBuilder::new(Kind::GitUserGraspList, "")
.tags(vec![Tag::identifier("grasp-list-nip77-b")])
.sign_with_keys(&keys2)
.finalize(&keys2)
.expect("Failed to sign event 2");
let event2_id = event2.id;
println!(
@@ -102,7 +102,9 @@ async fn test_nip77_negentropy_sync_finds_events() {
// 5. Create a fresh client to perform sync (different instance, no local events)
let sync_keys = Keys::generate(); // Different keys, doesn't matter for sync
let sync_client = Client::new(sync_keys);
let sync_client = Client::builder()
.authenticator(SignerAuthenticator::new(sync_keys))
.build();
sync_client
.add_relay(relay.url())
@@ -122,7 +124,7 @@ async fn test_nip77_negentropy_sync_finds_events() {
let sync_opts = SyncOptions::default();
let result = sync_client.sync(filter, &sync_opts).await;
let result = sync_client.sync(filter).opts(sync_opts).await;
// 7. Cleanup
sync_client.disconnect().await;
@@ -131,7 +133,7 @@ async fn test_nip77_negentropy_sync_finds_events() {
// 8. Verify results
match result {
Ok(output) => {
let reconciliation = output.val;
let reconciliation = output.value;
println!("Negentropy sync completed!");
println!(" Local: {:?}", reconciliation.local);
println!(" Remote: {:?}", reconciliation.remote);
@@ -180,7 +182,9 @@ async fn test_nip77_negentropy_sync_empty_result() {
// 2. Create client
let keys = Keys::generate();
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client
.add_relay(relay.url())
@@ -199,7 +203,7 @@ async fn test_nip77_negentropy_sync_empty_result() {
let sync_opts = SyncOptions::default();
let result = client.sync(filter, &sync_opts).await;
let result = client.sync(filter).opts(sync_opts).await;
// 4. Cleanup
client.disconnect().await;
@@ -208,7 +212,7 @@ async fn test_nip77_negentropy_sync_empty_result() {
// 5. Verify - should succeed but find nothing
match result {
Ok(output) => {
let reconciliation = output.val;
let reconciliation = output.value;
println!("Empty sync completed!");
println!(" Received: {:?}", reconciliation.received);
println!(" Remote: {:?}", reconciliation.remote);
+3 -5
View File
@@ -40,9 +40,7 @@ use std::time::Duration;
/// Helper to create a test event
async fn create_test_event(keys: &Keys, content: &str) -> Event {
EventBuilder::text_note(content)
.sign_with_keys(keys)
.unwrap()
EventBuilder::text_note(content).finalize(keys).unwrap()
}
/// Helper to create a state event with specific refs
@@ -56,14 +54,14 @@ fn create_state_event_with_refs(
// Add ref tags
for (ref_name, commit_hash) in refs {
tags.push(Tag::custom(
TagKind::custom("ref"),
"ref",
vec![ref_name.to_string(), commit_hash.to_string()],
));
}
let event = EventBuilder::new(Kind::from(30618), "")
.tags(tags)
.sign_with_keys(keys)?;
.finalize(keys)?;
Ok(event)
}
+34 -16
View File
@@ -62,7 +62,9 @@ async fn test_push_triggers_unified_processing() {
// 3. Create and send announcement
let announcement = create_repo_announcement(&keys, &[&relay.domain()], identifier);
let client = Client::new(keys.clone());
let client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
client
.add_relay(relay.url())
.await
@@ -163,7 +165,9 @@ async fn test_state_event_syncs_from_remote() {
identifier,
);
let source_client = Client::new(keys.clone());
let source_client = Client::builder()
.authenticator(SignerAuthenticator::new(keys.clone()))
.build();
source_client
.add_relay(source_relay.url())
.await
@@ -327,7 +331,9 @@ async fn test_pr_event_syncs_from_remote() {
identifier,
);
let source_client = Client::new(owner_keys.clone());
let source_client = Client::builder()
.authenticator(SignerAuthenticator::new(owner_keys.clone()))
.build();
source_client
.add_relay(source_relay.url())
.await
@@ -404,7 +410,9 @@ async fn test_pr_event_syncs_from_remote() {
let pr_event_id = pr_event.id;
// Send PR event to source relay using PR author's client
let pr_client = Client::new(pr_author_keys.clone());
let pr_client = Client::builder()
.authenticator(SignerAuthenticator::new(pr_author_keys.clone()))
.build();
pr_client
.add_relay(source_relay.url())
.await
@@ -540,7 +548,9 @@ async fn test_concurrent_state_and_pr_sync() {
identifier,
);
let source_client = Client::new(owner_keys.clone());
let source_client = Client::builder()
.authenticator(SignerAuthenticator::new(owner_keys.clone()))
.build();
source_client
.add_relay(source_relay.url())
.await
@@ -620,7 +630,9 @@ async fn test_concurrent_state_and_pr_sync() {
let pr_event_id = pr_event.id;
// Send PR event to source relay using PR author's client
let pr_client = Client::new(pr_author_keys.clone());
let pr_client = Client::builder()
.authenticator(SignerAuthenticator::new(pr_author_keys.clone()))
.build();
pr_client
.add_relay(source_relay.url())
.await
@@ -852,18 +864,18 @@ async fn test_pr_event_clone_tag_sync_with_partial_oid_aggregation_from_multiple
// Create announcement with custom clone/relay URLs
// Clone URLs: source_grasp + syncing (NOT git_server - PR commit only via PR's clone tag)
// Relay URLs: source_grasp + mock_relay + syncing
let announcement = nostr_sdk::EventBuilder::new(
let announcement = nostr_sdk::prelude::EventBuilder::new(
Kind::GitRepoAnnouncement,
"Repository for PR clone tag + partial OID test",
)
.tags(vec![
nostr_sdk::Tag::identifier(identifier),
nostr_sdk::Tag::custom(
nostr_sdk::TagKind::custom("clone"),
nostr_sdk::prelude::Tag::identifier(identifier),
nostr_sdk::prelude::Tag::custom(
"clone",
vec![clone_url_source.clone(), clone_url_syncing.clone()],
),
nostr_sdk::Tag::custom(
nostr_sdk::TagKind::custom("relays"),
nostr_sdk::prelude::Tag::custom(
"relays",
vec![
source_grasp.url().to_string(),
mock_relay.url().to_string(),
@@ -871,11 +883,13 @@ async fn test_pr_event_clone_tag_sync_with_partial_oid_aggregation_from_multiple
],
),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.expect("Failed to sign announcement");
// Connect to source_grasp and send announcement
let source_client = Client::new(owner_keys.clone());
let source_client = Client::builder()
.authenticator(SignerAuthenticator::new(owner_keys.clone()))
.build();
source_client
.add_relay(source_grasp.url())
.await
@@ -929,7 +943,9 @@ async fn test_pr_event_clone_tag_sync_with_partial_oid_aggregation_from_multiple
// First, send announcement to mock_relay so it has the repo context
// This is needed because the sync system filters events based on whether
// they reference repos that list our relay
let mock_client = Client::new(owner_keys.clone());
let mock_client = Client::builder()
.authenticator(SignerAuthenticator::new(owner_keys.clone()))
.build();
mock_client
.add_relay(mock_relay.url())
.await
@@ -960,7 +976,9 @@ async fn test_pr_event_clone_tag_sync_with_partial_oid_aggregation_from_multiple
// Send PR event to mock_relay
// MockRelay accepts all events without validation (no purgatory)
let pr_client = Client::new(pr_author_keys.clone());
let pr_client = Client::builder()
.authenticator(SignerAuthenticator::new(pr_author_keys.clone()))
.build();
pr_client
.add_relay(mock_relay.url())
.await
+25 -37
View File
@@ -20,10 +20,10 @@ async fn test_reject_state_without_announcement() {
// Create a state event without any announcement
let state_event = EventBuilder::new(Kind::RepoState, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(TagKind::custom("refs/heads/main"), ["abc123"]),
Tag::custom("d", ["test-repo"]),
Tag::custom("refs/heads/main", ["abc123"]),
])
.sign_with_keys(&keys)
.finalize(&keys)
.unwrap();
// Connect to relay
@@ -77,14 +77,11 @@ async fn test_reject_state_from_unauthorized_author() {
// Create announcement
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(
TagKind::custom("clone"),
[format!("https://{}/test.git", relay.domain())],
),
Tag::custom(TagKind::custom("relays"), [relay.url()]),
Tag::custom("d", ["test-repo"]),
Tag::custom("clone", [format!("https://{}/test.git", relay.domain())]),
Tag::custom("relays", [relay.url()]),
])
.sign_with_keys(&announcement_keys)
.finalize(&announcement_keys)
.unwrap();
// Connect to relay
@@ -101,10 +98,10 @@ async fn test_reject_state_from_unauthorized_author() {
// Try to send state event from unauthorized author
let state_event = EventBuilder::new(Kind::RepoState, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(TagKind::custom("refs/heads/main"), ["abc123"]),
Tag::custom("d", ["test-repo"]),
Tag::custom("refs/heads/main", ["abc123"]),
])
.sign_with_keys(&unauthorized_keys)
.finalize(&unauthorized_keys)
.unwrap();
let result = client.send_event(&state_event).await;
@@ -144,14 +141,11 @@ async fn test_accept_state_from_announcement_author() {
// Create announcement
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(
TagKind::custom("clone"),
[format!("https://{}/test.git", relay.domain())],
),
Tag::custom(TagKind::custom("relays"), [relay.url()]),
Tag::custom("d", ["test-repo"]),
Tag::custom("clone", [format!("https://{}/test.git", relay.domain())]),
Tag::custom("relays", [relay.url()]),
])
.sign_with_keys(&keys)
.finalize(&keys)
.unwrap();
// Connect to relay
@@ -168,10 +162,10 @@ async fn test_accept_state_from_announcement_author() {
// Send state event from same author (should be accepted or go to purgatory)
let state_event = EventBuilder::new(Kind::RepoState, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(TagKind::custom("refs/heads/main"), ["abc123"]),
Tag::custom("d", ["test-repo"]),
Tag::custom("refs/heads/main", ["abc123"]),
])
.sign_with_keys(&keys)
.finalize(&keys)
.unwrap();
let result = client.send_event(&state_event).await;
@@ -214,18 +208,12 @@ async fn test_accept_state_from_maintainer() {
// Create announcement with maintainer
let announcement = EventBuilder::new(Kind::GitRepoAnnouncement, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(
TagKind::custom("clone"),
[format!("https://{}/test.git", relay.domain())],
),
Tag::custom(TagKind::custom("relays"), [relay.url()]),
Tag::custom(
TagKind::custom("maintainers"),
[maintainer_keys.public_key().to_hex()],
),
Tag::custom("d", ["test-repo"]),
Tag::custom("clone", [format!("https://{}/test.git", relay.domain())]),
Tag::custom("relays", [relay.url()]),
Tag::custom("maintainers", [maintainer_keys.public_key().to_hex()]),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.unwrap();
// Connect to relay
@@ -242,10 +230,10 @@ async fn test_accept_state_from_maintainer() {
// Send state event from maintainer
let state_event = EventBuilder::new(Kind::RepoState, "")
.tags([
Tag::custom(TagKind::custom("d"), ["test-repo"]),
Tag::custom(TagKind::custom("refs/heads/main"), ["abc123"]),
Tag::custom("d", ["test-repo"]),
Tag::custom("refs/heads/main", ["abc123"]),
])
.sign_with_keys(&maintainer_keys)
.finalize(&maintainer_keys)
.unwrap();
let result = client.send_event(&state_event).await;
+2 -5
View File
@@ -17,14 +17,11 @@ use crate::common::{sync_helpers::*, TestRelay};
///
/// Used to create Layer 2 events like patches that reference a repository.
fn create_event_referencing_repo(keys: &Keys, repo_coord: &str, kind: u16, content: &str) -> Event {
let tags = vec![Tag::custom(
TagKind::custom("a"),
vec![repo_coord.to_string()],
)];
let tags = vec![Tag::custom("a", vec![repo_coord.to_string()])];
EventBuilder::new(Kind::from_u16(kind), content)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.expect("Failed to sign event")
}
+5 -2
View File
@@ -425,7 +425,9 @@ async fn test_pagination_for_large_historic_sync() {
// Query for all issues
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
client
.add_relay(syncing.url())
.await
@@ -436,7 +438,8 @@ async fn test_pagination_for_large_historic_sync() {
tokio::time::sleep(Duration::from_millis(500)).await;
let synced_issues = client
.fetch_events(issues_filter, Duration::from_secs(5))
.fetch_events(issues_filter)
.timeout(Duration::from_secs(5))
.await
.expect("Failed to fetch issues from syncing relay");
+13 -4
View File
@@ -238,7 +238,9 @@ async fn test_live_sync_layer3_events() {
let mut has_correct_ref = false;
if comment_synced {
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
if client.add_relay(relay_b.url()).await.is_ok() {
client.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
@@ -246,7 +248,8 @@ async fn test_live_sync_layer3_events() {
let fetch_filter = Filter::new().kind(Kind::Comment).id(comment_id);
if let Ok(events) = client
.fetch_events(fetch_filter, Duration::from_secs(2))
.fetch_events(fetch_filter)
.timeout(Duration::from_secs(2))
.await
{
if let Some(event) = events.first() {
@@ -376,7 +379,9 @@ async fn test_live_sync_event_ordering() {
// 6. Fetch all events from relay_b
let temp_keys = Keys::generate();
let client = Client::new(temp_keys);
let client = Client::builder()
.authenticator(SignerAuthenticator::new(temp_keys))
.build();
let events_found: Vec<Event>;
if client.add_relay(relay_b.url()).await.is_ok() {
@@ -385,7 +390,11 @@ async fn test_live_sync_event_ordering() {
let filter = Filter::new().kind(Kind::GitIssue).author(keys.public_key());
match client.fetch_events(filter, Duration::from_secs(3)).await {
match client
.fetch_events(filter)
.timeout(Duration::from_secs(3))
.await
{
Ok(events) => {
events_found = events.into_iter().collect();
}
+28 -43
View File
@@ -62,7 +62,7 @@ async fn test_maintainer_announcement_reprocessed_immediately() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"http://{}/{}/{}.git",
relay_a.domain(),
@@ -70,9 +70,9 @@ async fn test_maintainer_announcement_reprocessed_immediately() {
identifier
)],
),
Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]),
Tag::custom("relays", vec![relay_a.url().to_string()]),
])
.sign_with_keys(&maintainer_keys)
.finalize(&maintainer_keys)
.unwrap();
send_to_relay(&relay_a, &maintainer_announcement)
.await
@@ -104,7 +104,7 @@ async fn test_maintainer_announcement_reprocessed_immediately() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"http://{}/{}/{}.git",
relay_b.domain(),
@@ -113,15 +113,12 @@ async fn test_maintainer_announcement_reprocessed_immediately() {
)],
),
Tag::custom(
TagKind::custom("relays"),
"relays",
vec![relay_a.url().to_string(), relay_b.url().to_string()],
),
Tag::custom(
TagKind::custom("maintainers"),
vec![maintainer_keys.public_key().to_hex()],
),
Tag::custom("maintainers", vec![maintainer_keys.public_key().to_hex()]),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.unwrap();
send_to_relay(&relay_b, &owner_announcement).await.unwrap();
@@ -208,15 +205,12 @@ async fn test_maintainer_announcement_cold_index_prevents_refetch() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://example.com/{}.git", identifier)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://example.com".to_string()],
),
Tag::custom("relays", vec!["wss://example.com".to_string()]),
])
.sign_with_keys(&maintainer_keys)
.finalize(&maintainer_keys)
.unwrap();
// Send maintainer announcement - expect it to be rejected
@@ -232,16 +226,13 @@ async fn test_maintainer_announcement_cold_index_prevents_refetch() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://{}/{}.git", relay.domain(), identifier)],
),
Tag::custom(TagKind::custom("relays"), vec![relay.url().to_string()]),
Tag::custom(
TagKind::custom("maintainers"),
vec![maintainer_keys.public_key().to_hex()],
),
Tag::custom("relays", vec![relay.url().to_string()]),
Tag::custom("maintainers", vec![maintainer_keys.public_key().to_hex()]),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.unwrap();
client.send_event(&owner_announcement).await.unwrap();
@@ -324,7 +315,7 @@ async fn test_multiple_maintainers_all_reprocessed() {
.tags(vec![
Tag::identifier(identifier.as_str()),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"http://{}/{}/{}.git",
relay_a.domain(),
@@ -332,9 +323,9 @@ async fn test_multiple_maintainers_all_reprocessed() {
identifier
)],
),
Tag::custom(TagKind::custom("relays"), vec![relay_a.url().to_string()]),
Tag::custom("relays", vec![relay_a.url().to_string()]),
])
.sign_with_keys(maintainer_keys)
.finalize(*maintainer_keys)
.unwrap();
send_to_relay(&relay_a, &announcement).await.unwrap();
// Use push_unique_git_data_to_relay so each maintainer gets a distinct commit
@@ -395,7 +386,7 @@ async fn test_multiple_maintainers_all_reprocessed() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"http://{}/{}/{}.git",
relay_b.domain(),
@@ -404,11 +395,11 @@ async fn test_multiple_maintainers_all_reprocessed() {
)],
),
Tag::custom(
TagKind::custom("relays"),
"relays",
vec![relay_a.url().to_string(), relay_b.url().to_string()],
),
Tag::custom(
TagKind::custom("maintainers"),
"maintainers",
vec![
maintainer1_keys.public_key().to_hex(),
maintainer2_keys.public_key().to_hex(),
@@ -416,7 +407,7 @@ async fn test_multiple_maintainers_all_reprocessed() {
],
),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.unwrap();
send_to_relay(&relay_b, &owner_announcement).await.unwrap();
@@ -482,15 +473,12 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!("https://example.com/{}.git", identifier)],
),
Tag::custom(
TagKind::custom("relays"),
vec!["wss://example.com".to_string()],
),
Tag::custom("relays", vec!["wss://example.com".to_string()]),
])
.sign_with_keys(&maintainer_keys)
.finalize(&maintainer_keys)
.unwrap();
// Send maintainer announcement - expect it to be rejected
@@ -509,7 +497,7 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() {
.tags(vec![
Tag::identifier(identifier),
Tag::custom(
TagKind::custom("clone"),
"clone",
vec![format!(
"http://{}/{}/{}.git",
relay.domain(),
@@ -517,13 +505,10 @@ async fn test_invalid_maintainer_pubkey_handled_gracefully() {
identifier
)],
),
Tag::custom(TagKind::custom("relays"), vec![relay.url().to_string()]),
Tag::custom(
TagKind::custom("maintainers"),
vec!["invalid-hex-not-a-pubkey".to_string()],
),
Tag::custom("relays", vec![relay.url().to_string()]),
Tag::custom("maintainers", vec!["invalid-hex-not-a-pubkey".to_string()]),
])
.sign_with_keys(&owner_keys)
.finalize(&owner_keys)
.unwrap();
send_to_relay(&relay, &owner_announcement).await.unwrap();
+2 -5
View File
@@ -183,14 +183,11 @@ async fn test_metric_values_are_numeric() {
///
/// Used to create Layer 2 events like patches that reference a repository.
fn create_event_referencing_repo(keys: &Keys, repo_coord: &str, kind: u16, content: &str) -> Event {
let tags = vec![Tag::custom(
TagKind::custom("a"),
vec![repo_coord.to_string()],
)];
let tags = vec![Tag::custom("a", vec![repo_coord.to_string()])];
EventBuilder::new(Kind::from_u16(kind), content)
.tags(tags)
.sign_with_keys(keys)
.finalize(keys)
.expect("Failed to sign event")
}