refactor: replace hardcoded Kind constants with rust-nostr variants

- Replace KIND_REPOSITORY_ANNOUNCEMENT with Kind::GitRepoAnnouncement
- Replace KIND_REPOSITORY_STATE with Kind::RepoState
- Replace KIND_PR with Kind::GitPullRequest
- Replace KIND_PR_UPDATE with Kind::GitPullRequestUpdate
- Replace KIND_USER_GRASP_LIST with Kind::GitUserGraspList
- Replace KIND_PATCH with Kind::GitPatch
- Replace KIND_ISSUE with Kind::GitIssue
- Replace KIND_COMMENT with Kind::Comment
- Replace all Kind::Custom(30617|30618|1617|1618|1619|1621|1111|10317) patterns
- Remove all hardcoded KIND_* constants from events.rs
- Update all match statements to use Kind enum directly
- Update all filter builders to use Kind variants
- Update all test helpers and assertions

Benefits:
- Type safety: compiler prevents wrong kind numbers
- Readability: Kind::GitRepoAnnouncement is self-documenting
- Maintainability: single source of truth (rust-nostr)
- IDE support: full autocompletion and refactoring
- Standards: aligns with rust-nostr best practices

Files modified: 21
Constants removed: 9
Patterns replaced: 100+
Tests passing: 222/222
This commit is contained in:
DanConwayDev
2026-01-08 11:23:28 +00:00
parent 075307804b
commit 5d02ad6b89
20 changed files with 154 additions and 193 deletions
+2 -2
View File
@@ -492,7 +492,7 @@ impl AuditClient {
// Add any additional tags
tags.extend(additional_tags);
self.event_builder(Kind::Custom(1621), content)
self.event_builder(Kind::GitIssue, content)
.tags(tags)
.build(self.keys())
.map_err(|e| anyhow!("Failed to build issue event: {}", e))
@@ -530,7 +530,7 @@ impl AuditClient {
// Add any additional tags
tags.extend(additional_tags);
self.event_builder(Kind::Custom(1111), content)
self.event_builder(Kind::Comment, content)
.tags(tags)
.build(self.keys())
.map_err(|e| anyhow!("Failed to build comment event: {}", e))
+7 -7
View File
@@ -677,7 +677,7 @@ impl<'a> TestContext<'a> {
// Tag format: ["refs/heads/main", "<commit_hash>"]
// Note: We build the state but DON'T send it here - the caller will send it
self.client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
@@ -713,7 +713,7 @@ impl<'a> TestContext<'a> {
// Build NIP-34 PR event (kind 1618)
self.client
.event_builder(
Kind::Custom(1618), // NIP-34 PR kind (has 'c' tag for commit)
Kind::GitPullRequest, // NIP-34 PR kind (has 'c' tag for commit)
"Test PR for GRASP validation",
)
.tag(Tag::custom(
@@ -756,7 +756,7 @@ impl<'a> TestContext<'a> {
// Build NIP-34 PR event (kind 1618)
self.client
.event_builder(
Kind::Custom(1618), // NIP-34 PR kind (has 'c' tag for commit)
Kind::GitPullRequest, // NIP-34 PR kind (has 'c' tag for commit)
"Test PR for GRASP validation",
)
.tag(Tag::custom(
@@ -884,7 +884,7 @@ impl<'a> TestContext<'a> {
let state_event = self
.client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
@@ -1057,7 +1057,7 @@ impl<'a> TestContext<'a> {
let maintainer_state_event = self
.client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
@@ -1230,7 +1230,7 @@ impl<'a> TestContext<'a> {
let recursive_maintainer_state_event = self
.client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
@@ -1397,7 +1397,7 @@ impl<'a> TestContext<'a> {
let develop_state_event = self
.client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("HEAD"),
@@ -659,7 +659,7 @@ impl EventAcceptancePolicyTests {
];
let comment = client
.event_builder(Kind::Custom(1111), "Comment on repo")
.event_builder(Kind::Comment, "Comment on repo")
.tags(tags)
.build(client.keys())
.map_err(|e| format!("Failed to build comment: {}", e))?;
@@ -935,7 +935,7 @@ impl EventAcceptancePolicyTests {
];
let issue = client
.event_builder(Kind::Custom(1621), "issue content")
.event_builder(Kind::GitIssue, "issue content")
.tags(issue_tags)
.build(client.keys())
.map_err(|e| format!("Failed to build issue: {}", e))?;
@@ -995,7 +995,7 @@ impl EventAcceptancePolicyTests {
];
let comment_b = client
.event_builder(Kind::Custom(1111), "Comment B quoting Comment A")
.event_builder(Kind::Comment, "Comment B quoting Comment A")
.tags(comment_b_tags)
.build(client.keys())
.map_err(|e| format!("Failed to build comment B: {}", e))?;
@@ -1172,7 +1172,7 @@ impl EventAcceptancePolicyTests {
];
let comment = client
.event_builder(Kind::Custom(1111), "Comment on unaccepted repo")
.event_builder(Kind::Comment, "Comment on unaccepted repo")
.tags(tags)
.build(client.keys())
.map_err(|e| format!("Failed to build comment: {}", e))?;
+3 -3
View File
@@ -78,7 +78,7 @@ impl Nip01SmokeTests {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// Step 2: VERIFY - Query event back
let filter = Filter::new().kind(Kind::Custom(30617)).id(event_id);
let filter = Filter::new().kind(Kind::GitRepoAnnouncement).id(event_id);
let events = client
.query(filter)
@@ -88,7 +88,7 @@ impl Nip01SmokeTests {
if events.is_empty() {
// Debug: try querying without audit client filtering
eprintln!("Event not found with audit client query, trying direct client query...");
let direct_filter = Filter::new().kind(Kind::Custom(30617)).id(event_id);
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))
@@ -140,7 +140,7 @@ impl Nip01SmokeTests {
// Step 2: VERIFY - Subscribe to NIP-34 announcements from this author
let filter = Filter::new()
.kind(Kind::Custom(30617))
.kind(Kind::GitRepoAnnouncement)
.author(client.public_key());
let events = client
@@ -877,7 +877,7 @@ impl PushAuthorizationTests {
// Create a rogue state event announcing the new commit
// This event has the correct repo_id but is signed by a non-maintainer
let rogue_state = match client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("refs/heads/main"),
@@ -1591,7 +1591,7 @@ impl PushAuthorizationTests {
// This references a commit that doesn't yet exist on the relay
// ============================================================
let state_event = match client
.event_builder(Kind::Custom(30618), "")
.event_builder(Kind::RepoState, "")
.tag(Tag::identifier(&repo_id))
.tag(Tag::custom(
TagKind::custom("HEAD"),
+12 -20
View File
@@ -36,11 +36,9 @@ use std::sync::Arc;
use tracing::{debug, info, warn};
use crate::nostr::builder::SharedDatabase;
use crate::nostr::events::{
RepositoryAnnouncement, RepositoryState, KIND_PR, KIND_PR_UPDATE, KIND_REPOSITORY_ANNOUNCEMENT,
KIND_REPOSITORY_STATE,
};
use crate::nostr::events::{RepositoryAnnouncement, RepositoryState};
use crate::purgatory::Purgatory;
use nostr_sdk::Kind;
/// Perform GRASP authorization for a push operation
///
@@ -241,10 +239,7 @@ pub async fn fetch_repository_data(
identifier: &str,
) -> Result<RepositoryData> {
let filter = Filter::new()
.kinds([
Kind::from(KIND_REPOSITORY_ANNOUNCEMENT),
Kind::from(KIND_REPOSITORY_STATE),
])
.kinds([Kind::GitRepoAnnouncement, Kind::RepoState])
.custom_tag(
SingleLetterTag::lowercase(Alphabet::D),
identifier.to_string(),
@@ -268,11 +263,11 @@ pub async fn fetch_repository_data(
let mut states = Vec::new();
for event in events {
if event.kind == Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
if event.kind == Kind::GitRepoAnnouncement {
if let Ok(announcement) = RepositoryAnnouncement::from_event(event) {
announcements.push(announcement);
}
} else if event.kind == Kind::from(KIND_REPOSITORY_STATE) {
} else if event.kind == Kind::RepoState {
if let Ok(state) = RepositoryState::from_event(event) {
states.push(state);
}
@@ -714,10 +709,7 @@ impl AuthorizationContext {
/// This matches the reference implementation's filter logic
pub fn create_filter(identifier: &str) -> Filter {
Filter::new()
.kinds([
Kind::from(KIND_REPOSITORY_ANNOUNCEMENT),
Kind::from(KIND_REPOSITORY_STATE),
])
.kinds([Kind::GitRepoAnnouncement, Kind::RepoState])
.custom_tag(
SingleLetterTag::lowercase(Alphabet::D),
identifier.to_string(),
@@ -754,7 +746,7 @@ impl AuthorizationContext {
for event in &self.events {
// Check if it's a repository state event
if event.kind != Kind::from(KIND_REPOSITORY_STATE) {
if event.kind != Kind::RepoState {
continue;
}
@@ -806,7 +798,7 @@ impl AuthorizationContext {
for event in &self.events {
// Only look at announcements
if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
if event.kind != Kind::GitRepoAnnouncement {
continue;
}
@@ -838,7 +830,7 @@ impl AuthorizationContext {
pub fn is_state_authorized(&self, state_pubkey: &str, identifier: &str) -> bool {
for event in &self.events {
// Only look at announcements
if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
if event.kind != Kind::GitRepoAnnouncement {
continue;
}
@@ -1093,7 +1085,7 @@ pub async fn get_event_commit_tag(
// Query for PR (1618) and PR Update (1619) events with this ID
let filter = Filter::new()
.ids([*event_id])
.kinds([Kind::from(KIND_PR), Kind::from(KIND_PR_UPDATE)]);
.kinds([Kind::GitPullRequest, Kind::GitPullRequestUpdate]);
let events: Vec<Event> = database
.query(filter)
@@ -1224,7 +1216,7 @@ mod tests {
vec!["wss://example.com".to_string()],
));
EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repo")
EventBuilder::new(Kind::GitRepoAnnouncement, "Test repo")
.tags(tags)
.sign_with_keys(keys)
.unwrap()
@@ -1240,7 +1232,7 @@ mod tests {
));
}
EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(keys)
.unwrap()
+8 -9
View File
@@ -12,10 +12,7 @@ use nostr_lmdb::NostrLmdb;
use nostr_relay_builder::prelude::*;
use crate::config::{Config, DatabaseBackend};
use crate::nostr::events::{
RepositoryAnnouncement, KIND_PR, KIND_PR_UPDATE, KIND_REPOSITORY_ANNOUNCEMENT,
KIND_REPOSITORY_STATE, KIND_USER_GRASP_LIST,
};
use crate::nostr::events::RepositoryAnnouncement;
use crate::nostr::policy::{
AnnouncementPolicy, AnnouncementResult, PolicyContext, PrEventPolicy, ReferenceResult,
RelatedEventPolicy, StatePolicy, StateResult,
@@ -377,11 +374,13 @@ impl WritePolicy for Nip34WritePolicy {
// Sync uses localhost:0 as a dummy address
let is_synced = addr.ip().is_loopback() && addr.port() == 0;
match event.kind.as_u16() {
KIND_REPOSITORY_ANNOUNCEMENT => self.handle_announcement(event).await,
KIND_REPOSITORY_STATE => self.handle_state(event, is_synced).await,
KIND_PR | KIND_PR_UPDATE => self.handle_pr_event(event, is_synced).await,
KIND_USER_GRASP_LIST => {
match event.kind {
Kind::GitRepoAnnouncement => self.handle_announcement(event).await,
Kind::RepoState => self.handle_state(event, is_synced).await,
Kind::GitPullRequest | Kind::GitPullRequestUpdate => {
self.handle_pr_event(event, is_synced).await
}
Kind::GitUserGraspList => {
// Accept all kind 10317 (User Grasp List) events
// for better GRASP repository discovery
tracing::debug!(
+24 -32
View File
@@ -9,20 +9,12 @@
use anyhow::{anyhow, Result};
use nostr_sdk::{Event, Kind, TagKind, ToBech32};
/// NIP-34 Repository Announcement (kind 30617)
pub const KIND_REPOSITORY_ANNOUNCEMENT: u16 = 30617;
/// NIP-34 Repository State Announcement (kind 30618)
pub const KIND_REPOSITORY_STATE: u16 = 30618;
/// NIP-34 Pull Request (kind 1618) - has `c` tag for commit
pub const KIND_PR: u16 = 1618;
/// NIP-34 Pull Request Update (kind 1619) - has `c` tag for commit
pub const KIND_PR_UPDATE: u16 = 1619;
/// User Grasp List (kind 10317) - user's personal list of GRASP repositories
pub const KIND_USER_GRASP_LIST: u16 = 10317;
// NOTE: Using rust-nostr Kind variants instead of hardcoded constants:
// - KIND_REPOSITORY_ANNOUNCEMENT -> Kind::GitRepoAnnouncement (30617)
// - KIND_REPOSITORY_STATE -> Kind::RepoState (30618)
// - KIND_PR -> Kind::GitPullRequest (1618)
// - KIND_PR_UPDATE -> Kind::GitPullRequestUpdate (1619)
// - KIND_USER_GRASP_LIST -> Kind::GitUserGraspList (10317)
/// Repository announcement details extracted from NIP-34 event
#[derive(Debug, Clone)]
@@ -40,10 +32,10 @@ pub struct RepositoryAnnouncement {
impl RepositoryAnnouncement {
/// Parse a repository announcement from a NIP-34 kind 30617 event
pub fn from_event(event: Event) -> Result<Self> {
if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
if event.kind != Kind::GitRepoAnnouncement {
return Err(anyhow!(
"Invalid event kind: expected {}, got {}",
KIND_REPOSITORY_ANNOUNCEMENT,
Kind::GitRepoAnnouncement,
event.kind
));
}
@@ -197,10 +189,10 @@ pub struct TagState {
impl RepositoryState {
/// Parse a repository state from a NIP-34 kind 30618 event
pub fn from_event(event: Event) -> Result<Self> {
if event.kind != Kind::from(KIND_REPOSITORY_STATE) {
if event.kind != Kind::RepoState {
return Err(anyhow!(
"Invalid event kind: expected {}, got {}",
KIND_REPOSITORY_STATE,
Kind::RepoState,
event.kind
));
}
@@ -346,10 +338,10 @@ impl RepositoryState {
/// Returns Ok(()) if valid, Err with reason if invalid.
pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> {
// Must be kind 30617
if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) {
if event.kind != Kind::GitRepoAnnouncement {
return Err(anyhow!(
"Invalid kind: expected {}",
KIND_REPOSITORY_ANNOUNCEMENT
Kind::GitRepoAnnouncement
));
}
@@ -381,8 +373,8 @@ pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> {
/// Returns Ok(()) if valid, Err with reason if invalid.
pub fn validate_state(event: &Event) -> Result<()> {
// Must be kind 30618
if event.kind != Kind::from(KIND_REPOSITORY_STATE) {
return Err(anyhow!("Invalid kind: expected {}", KIND_REPOSITORY_STATE));
if event.kind != Kind::RepoState {
return Err(anyhow!("Invalid kind: expected {}", Kind::RepoState));
}
// Must have identifier
@@ -433,7 +425,7 @@ mod tests {
));
}
EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository")
.tags(tags)
.sign_with_keys(keys)
.unwrap()
@@ -454,7 +446,7 @@ mod tests {
));
}
EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(keys)
.unwrap()
@@ -483,7 +475,7 @@ mod tests {
#[test]
fn test_parse_announcement_missing_identifier() {
let keys = create_test_keys();
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
let event = EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository")
.sign_with_keys(&keys)
.unwrap();
@@ -579,7 +571,7 @@ mod tests {
#[test]
fn test_validate_state_missing_identifier() {
let keys = create_test_keys();
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.sign_with_keys(&keys)
.unwrap();
@@ -614,7 +606,7 @@ mod tests {
vec![maintainer_keys.public_key().to_hex()],
));
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository")
let event = EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
@@ -649,7 +641,7 @@ mod tests {
vec!["e5f6g7h8".to_string()],
));
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
@@ -683,7 +675,7 @@ mod tests {
vec!["ref: refs/heads/main".to_string()],
));
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
@@ -716,7 +708,7 @@ mod tests {
vec!["refs/heads/develop".to_string()],
));
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
@@ -740,7 +732,7 @@ mod tests {
),
];
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
@@ -773,7 +765,7 @@ mod tests {
vec!["refs/heads/develop".to_string()],
));
let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "")
let event = EventBuilder::new(Kind::RepoState, "")
.tags(tags)
.sign_with_keys(&keys)
.unwrap();
+2 -6
View File
@@ -5,9 +5,7 @@
use nostr_relay_builder::prelude::{Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag};
use super::PolicyContext;
use crate::nostr::events::{
validate_announcement, RepositoryAnnouncement, KIND_REPOSITORY_ANNOUNCEMENT,
};
use crate::nostr::events::{validate_announcement, RepositoryAnnouncement};
/// Result of announcement policy evaluation
#[derive(Debug)]
@@ -121,9 +119,7 @@ impl AnnouncementPolicy {
author: &PublicKey,
) -> Result<bool, String> {
// Query all announcements with this identifier that are already in the database
let filter = Filter::new()
.kind(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT))
.custom_tag(
let filter = Filter::new().kind(Kind::GitRepoAnnouncement).custom_tag(
SingleLetterTag::lowercase(Alphabet::D),
identifier.to_string(),
);
+3 -3
View File
@@ -20,9 +20,9 @@ use nostr_sdk::prelude::*;
/// Note: 10317 (User Grasp List) is synced for better GRASP discovery.
pub fn build_announcement_filter(since: Option<Timestamp>) -> Filter {
let filter = Filter::new().kinds([
Kind::Custom(30617), // Repository announcements
Kind::Custom(30618), // Maintainer lists
Kind::Custom(10317), // User Grasp List
Kind::GitRepoAnnouncement, // Repository announcements
Kind::RepoState, // Repository state
Kind::GitUserGraspList, // User Grasp List
]);
match since {
+12 -12
View File
@@ -149,7 +149,7 @@ impl SelfSubscriber {
match notification {
Ok(RelayPoolNotification::Event { event, .. }) => {
// Only process 30617 events that list our relay
if event.kind == Kind::Custom(30617) {
if event.kind == Kind::GitRepoAnnouncement {
if !self.lists_our_relay(&event) {
return LoopControl::Continue;
}
@@ -236,7 +236,7 @@ impl SelfSubscriber {
/// Format: 30617:pubkey:identifier
fn extract_repo_id(event: &Event) -> Option<String> {
// For kind 30617, extract d tag and build addressable ref
if event.kind == Kind::Custom(30617) {
if event.kind == Kind::GitRepoAnnouncement {
for tag in event.tags.iter() {
let tag_vec = tag.as_slice();
if tag_vec.len() >= 2 && tag_vec[0] == "d" {
@@ -296,21 +296,21 @@ impl SelfSubscriber {
);
Filter::new()
.kinds(vec![
Kind::Custom(30617), // Repository Announcements
Kind::Custom(1617), // Patches
Kind::Custom(1621), // Issues
Kind::Custom(1618), // Pull Requests
Kind::Custom(10317), // User Grasp List
Kind::GitRepoAnnouncement, // Repository Announcements
Kind::GitPatch, // Patches
Kind::GitIssue, // Issues
Kind::GitPullRequest, // Pull Requests
Kind::GitUserGraspList, // User Grasp List
])
.since(since)
} else {
// First connection - no since filter
Filter::new().kinds(vec![
Kind::Custom(30617), // Repository Announcements
Kind::Custom(1617), // Patches
Kind::Custom(1621), // Issues
Kind::Custom(1618), // Pull Requests
Kind::Custom(10317), // User Grasp List
Kind::GitRepoAnnouncement, // Repository Announcements
Kind::GitPatch, // Patches
Kind::GitIssue, // Issues
Kind::GitPullRequest, // Pull Requests
Kind::GitUserGraspList, // User Grasp List
])
};
+9 -11
View File
@@ -16,11 +16,9 @@ use std::path::Path;
use std::process::Command;
use std::time::Duration;
/// NIP-34 Repository State (kind 30618)
pub const KIND_STATE: u16 = 30618;
/// NIP-34 Pull Request (kind 1618)
pub const KIND_PR: u16 = 1618;
// NOTE: Using rust-nostr Kind variants:
// - Kind::RepoState.as_u16() -> Kind::RepoState (30618)
// - Kind::GitPullRequest.as_u16() -> Kind::GitPullRequest (1618)
/// Commit variants for deterministic test commits
#[derive(Debug, Clone, Copy)]
@@ -236,7 +234,7 @@ pub fn create_state_event(
));
}
EventBuilder::new(Kind::Custom(KIND_STATE), "")
EventBuilder::new(Kind::RepoState, "")
.tags(event_tags)
.sign_with_keys(keys)
.map_err(|e| format!("Failed to sign state event: {}", e))
@@ -269,7 +267,7 @@ pub fn create_pr_event(
Tag::custom(TagKind::custom("c"), vec![commit_hash.to_string()]),
];
EventBuilder::new(Kind::Custom(KIND_PR), title)
EventBuilder::new(Kind::GitPullRequest, title)
.tags(tags)
.sign_with_keys(keys)
.map_err(|e| format!("Failed to sign PR event: {}", e))
@@ -323,7 +321,7 @@ pub fn create_pr_event_with_clone(
tags.push(Tag::custom(TagKind::Clone, urls));
}
EventBuilder::new(Kind::Custom(KIND_PR), title)
EventBuilder::new(Kind::GitPullRequest, title)
.tags(tags)
.sign_with_keys(keys)
.map_err(|e| format!("Failed to sign PR event: {}", e))
@@ -705,7 +703,7 @@ mod tests {
)
.expect("Failed to create state event");
assert_eq!(event.kind.as_u16(), KIND_STATE);
assert_eq!(event.kind.as_u16(), Kind::RepoState.as_u16());
// Check d-tag
let has_d_tag = event.tags.iter().any(|tag| {
@@ -747,7 +745,7 @@ mod tests {
let event = create_pr_event(&keys, &repo_coord, "def456abc123", "Test PR")
.expect("Failed to create PR event");
assert_eq!(event.kind.as_u16(), KIND_PR);
assert_eq!(event.kind.as_u16(), Kind::GitPullRequest.as_u16());
// Check a-tag
let has_a_tag = event.tags.iter().any(|tag| {
@@ -815,7 +813,7 @@ mod tests {
)
.expect("Failed to create PR event with clone");
assert_eq!(event.kind.as_u16(), KIND_PR);
assert_eq!(event.kind.as_u16(), Kind::GitPullRequest.as_u16());
// Check a-tag
let has_a_tag = event.tags.iter().any(|tag| {
+16 -25
View File
@@ -17,14 +17,10 @@ use nostr_sdk::prelude::*;
use super::relay::TestRelay;
/// Kind 1618 - Issue (NIP-34 git-related event)
pub const KIND_ISSUE: u16 = 1621;
/// Kind 1111 - NIP-22 Comment
pub const KIND_COMMENT: u16 = 1111;
/// Kind 30617 - Repository state/announcement (NIP-34)
pub const KIND_REPOSITORY_STATE: u16 = 30617;
// NOTE: Using rust-nostr Kind variants:
// - Kind::GitIssue.as_u16() -> Kind::GitIssue (1621)
// - Kind::Comment.as_u16() -> Kind::Comment (1111)
// - Kind::GitRepoAnnouncement.as_u16() -> Kind::GitRepoAnnouncement (30617)
/// Test client with built-in retry logic for connect and send operations.
///
@@ -225,7 +221,7 @@ fn build_layer2_issue_with_tag(
let tags = vec![tag];
EventBuilder::new(Kind::Custom(KIND_ISSUE), title)
EventBuilder::new(Kind::GitIssue, title)
.tags(tags)
.sign_with_keys(keys)
.map_err(|e| format!("Failed to sign Layer 2 issue event: {}", e))
@@ -240,7 +236,7 @@ fn build_layer2_issue_with_tag(
/// * `keys` - Keys for signing the event
/// * `parent_event_id` - Event ID being referenced (e.g., an issue or patch)
/// * `content` - Comment content
/// * `kind` - Event kind (Kind::Custom(1) for reply, Kind::Custom(1111) for NIP-22 comment)
/// * `kind` - Event kind (Kind::TextNote for reply, Kind::Comment for NIP-22 comment)
///
/// # Tag Types
/// - For kind 1111: Uses uppercase 'E' tag (NIP-22 style)
@@ -258,7 +254,7 @@ pub fn build_layer3_comment_event(
let kind_num = kind.as_u16();
// Choose tag based on kind (NIP-22 uses E, NIP-10 style uses e)
let tag = if kind_num == KIND_COMMENT {
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()])
} else {
@@ -302,7 +298,7 @@ pub fn build_layer3_comment_with_uppercase_e_tag(
) -> Result<Event, String> {
let tag = Tag::custom(TagKind::custom("E"), vec![parent_event_id.to_hex()]);
EventBuilder::new(Kind::Custom(KIND_COMMENT), content)
EventBuilder::new(Kind::Comment, content)
.tags(vec![tag])
.sign_with_keys(keys)
.map_err(|e| format!("Failed to sign Layer 3 comment event: {}", e))
@@ -362,7 +358,7 @@ pub fn create_repo_announcement(keys: &Keys, domains: &[&str], identifier: &str)
Tag::custom(TagKind::custom("relays"), relay_urls),
];
EventBuilder::new(Kind::Custom(KIND_REPOSITORY_STATE), "Repository state")
EventBuilder::new(Kind::GitRepoAnnouncement, "Repository state")
.tags(tags)
.sign_with_keys(keys)
.expect("Failed to sign repo announcement")
@@ -503,7 +499,7 @@ fn check_sync_connections_in_metrics(metrics: &str, expected: usize) -> bool {
/// # Example
/// ```ignore
/// let filter = Filter::new()
/// .kind(Kind::Custom(1618))
/// .kind(Kind::GitPullRequest)
/// .author(keys.public_key())
/// .id(event.id);
///
@@ -559,7 +555,7 @@ pub async fn wait_for_event_on_relay(relay_url: &str, filter: Filter, timeout: D
pub fn repo_coord(keys: &Keys, identifier: &str) -> String {
format!(
"{}:{}:{}",
KIND_REPOSITORY_STATE,
Kind::GitRepoAnnouncement.as_u16(),
keys.public_key().to_hex(),
identifier
)
@@ -897,7 +893,7 @@ mod tests {
build_layer2_issue_event(&keys, &coord, "Test Issue").expect("Should create event");
// nostr-sdk 0.43: use field access
assert_eq!(event.kind.as_u16(), KIND_ISSUE);
assert_eq!(event.kind.as_u16(), Kind::GitIssue.as_u16());
// Check the tag exists
let has_a_tag = event.tags.iter().any(|tag| {
@@ -942,15 +938,10 @@ mod tests {
let keys = Keys::generate();
let parent_id = EventId::all_zeros();
let event = build_layer3_comment_event(
&keys,
&parent_id,
"Test comment",
Kind::Custom(KIND_COMMENT),
)
let event = build_layer3_comment_event(&keys, &parent_id, "Test comment", Kind::Comment)
.expect("Should create event");
assert_eq!(event.kind.as_u16(), KIND_COMMENT);
assert_eq!(event.kind.as_u16(), Kind::Comment.as_u16());
// NIP-22 comment should have uppercase 'E' tag
let has_e_tag = event.tags.iter().any(|tag| {
@@ -1003,7 +994,7 @@ mod tests {
let event = build_layer3_comment_with_uppercase_e_tag(&keys, &parent_id, "Comment content")
.expect("Should create event");
assert_eq!(event.kind.as_u16(), KIND_COMMENT);
assert_eq!(event.kind.as_u16(), Kind::Comment.as_u16());
let has_upper_e_tag = event.tags.iter().any(|tag| {
let slice = tag.as_slice();
@@ -1123,7 +1114,7 @@ async fn send_to_relay(relay: &TestRelay, event: &Event) -> Result<(), String> {
/// // Assert issue synced to result.syncing_relay
///
/// // Live sync test
/// let comment = build_layer3_comment_event(&keys, &issue.id, "Live Comment", Kind::Custom(1111))?;
/// let comment = build_layer3_comment_event(&keys, &issue.id, "Live Comment", Kind::Comment)?;
/// let result = run_sync_test(&[], &[comment]).await;
/// // Assert comment synced to result.syncing_relay
/// ```
+5 -6
View File
@@ -56,7 +56,7 @@ async fn test_nip77_negentropy_sync_finds_events() {
// Create a second event (issue referencing the repo)
let repo_coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_STATE,
Kind::GitRepoAnnouncement.as_u16(),
keys.public_key().to_hex(),
"test-repo-nip77"
);
@@ -103,10 +103,9 @@ async fn test_nip77_negentropy_sync_finds_events() {
tokio::time::sleep(Duration::from_millis(500)).await;
// 6. Perform negentropy sync with filter matching our events
let filter = Filter::new().author(keys.public_key()).kinds(vec![
Kind::Custom(KIND_REPOSITORY_STATE),
Kind::Custom(KIND_ISSUE),
]);
let filter = Filter::new()
.author(keys.public_key())
.kinds(vec![Kind::GitRepoAnnouncement, Kind::GitIssue]);
println!("Starting negentropy sync with filter: {:?}", filter);
@@ -183,7 +182,7 @@ async fn test_nip77_negentropy_sync_empty_result() {
// 3. Sync with filter that won't match anything
let filter = Filter::new()
.author(keys.public_key()) // Random new key, no events exist
.kind(Kind::Custom(KIND_REPOSITORY_STATE));
.kind(Kind::GitRepoAnnouncement);
println!("Starting negentropy sync with empty filter");
+1 -1
View File
@@ -795,7 +795,7 @@ async fn test_pr_event_clone_tag_sync_with_partial_oid_aggregation_from_multiple
// 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(
nostr_sdk::Kind::Custom(30617),
Kind::GitRepoAnnouncement,
"Repository for PR clone tag + partial OID test",
)
.tags(vec![
+14 -14
View File
@@ -14,8 +14,8 @@ use nostr_sdk::prelude::*;
use crate::common::{sync_helpers::*, TestRelay};
/// Kind 1617 - Patch event (NIP-34)
const KIND_PATCH: u16 = 1617;
// NOTE: Using rust-nostr Kind variant:
// - Kind::GitPatch.as_u16() -> Kind::GitPatch (1617)
/// Create an event referencing a repository coordinate via 'a' tag.
///
@@ -26,7 +26,7 @@ fn create_event_referencing_repo(keys: &Keys, repo_coord: &str, kind: u16, conte
vec![repo_coord.to_string()],
)];
EventBuilder::new(Kind::Custom(kind), content)
EventBuilder::new(Kind::from_u16(kind), content)
.tags(tags)
.sign_with_keys(keys)
.expect("Failed to sign event")
@@ -82,14 +82,18 @@ async fn test_discovers_layer3_via_layer2() {
// 5. Build the repo coordinate for the 'a' tag in the patch
let repo_coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_STATE,
Kind::GitRepoAnnouncement.as_u16(),
keys.public_key().to_hex(),
"test-repo-discovery"
);
// 6. Create a patch event (Layer 2) that references the announcement
let patch =
create_event_referencing_repo(&keys, &repo_coord, KIND_PATCH, "Test patch proposal");
let patch = create_event_referencing_repo(
&keys,
&repo_coord,
Kind::GitPatch.as_u16(),
"Test patch proposal",
);
let patch_id = patch.id;
println!("Created patch {} (kind {})", patch_id, patch.kind.as_u16());
@@ -134,9 +138,7 @@ async fn test_discovers_layer3_via_layer2() {
tokio::time::sleep(Duration::from_secs(3)).await;
// 10. Verify patch was synced to relay_b
let filter = Filter::new()
.kind(Kind::Custom(KIND_PATCH))
.author(keys.public_key());
let filter = Filter::new().kind(Kind::GitPatch).author(keys.public_key());
let patch_synced = wait_for_event_on_relay(relay_b.url(), filter, Duration::from_secs(5)).await;
@@ -250,9 +252,7 @@ async fn test_relay_discovery_via_announcements_with_historic_sync() {
tokio::time::sleep(Duration::from_secs(3)).await;
// 8. Verify Layer 2 event synced to relay_b
let issue_filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.author(keys.public_key());
let issue_filter = Filter::new().kind(Kind::GitIssue).author(keys.public_key());
let issue_synced =
wait_for_event_on_relay(relay_b.url(), issue_filter, Duration::from_secs(5)).await;
@@ -389,7 +389,7 @@ async fn test_recursive_relay_discovery_via_announcements_with_historic_sync() {
// 8. Verify announcement_x was synced to relay_a (from bootstrap relay_b)
let filter_x = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(keys_x.public_key());
let announcement_x_synced =
@@ -402,7 +402,7 @@ async fn test_recursive_relay_discovery_via_announcements_with_historic_sync() {
// 9. Verify announcement_y was synced to relay_a (from discovered relay_c)
let filter_y = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(keys_y.public_key());
let announcement_y_synced =
+7 -9
View File
@@ -29,7 +29,7 @@ async fn test_bootstrap_syncs_existing_layer2_events() {
// Verify announcement synced to syncing relay
let filter = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(result.maintainer_keys.public_key());
let synced =
@@ -64,7 +64,7 @@ async fn test_relay_replays_events_after_restart() {
// Verify announcement synced on first run
let filter = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(result.maintainer_keys.public_key());
let synced_first = wait_for_event_on_relay(
@@ -173,7 +173,7 @@ async fn test_announcement_not_listing_relay_is_not_synced() {
// Verify announcement did NOT sync to syncing relay
let filter = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(keys.public_key());
let synced = wait_for_event_on_relay(syncing.url(), filter, Duration::from_secs(2)).await;
@@ -274,7 +274,7 @@ async fn test_history_sync_without_negentropy() {
// Verify announcement synced to syncing relay via HISTORY sync
let filter = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(keys.public_key());
let synced = wait_for_event_on_relay(syncing.url(), filter, Duration::from_secs(5)).await;
@@ -339,7 +339,7 @@ async fn test_pagination_for_large_historic_sync() {
// Create 40 issue events to test pagination (with limit=10, threshold=7)
let repo_coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_STATE,
Kind::GitRepoAnnouncement.as_u16(),
keys.public_key().to_hex(),
repo_id
);
@@ -416,16 +416,14 @@ async fn test_pagination_for_large_historic_sync() {
// Verify announcement synced
let announcement_filter = Filter::new()
.kind(Kind::Custom(KIND_REPOSITORY_STATE))
.kind(Kind::GitRepoAnnouncement)
.author(keys.public_key());
let announcement_synced =
wait_for_event_on_relay(syncing.url(), announcement_filter, Duration::from_secs(3)).await;
// Verify ALL 40 issues synced
let issues_filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.author(keys.public_key());
let issues_filter = Filter::new().kind(Kind::GitIssue).author(keys.public_key());
// Query for all issues
let temp_keys = Keys::generate();
+5 -9
View File
@@ -115,7 +115,7 @@ async fn test_live_sync_layer2_events() {
// 9. Wait and verify event syncs to relay_b
let filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.kind(Kind::GitIssue)
.author(keys.public_key())
.id(issue_id);
@@ -237,7 +237,7 @@ async fn test_live_sync_layer3_events() {
// 6. Now wait for issue to sync to relay_b (this triggers Layer 3 filter creation)
tokio::time::sleep(Duration::from_secs(2)).await;
let issue_filter = Filter::new().kind(Kind::Custom(KIND_ISSUE)).id(issue_id);
let issue_filter = Filter::new().kind(Kind::GitIssue).id(issue_id);
let issue_synced =
wait_for_event_on_relay(relay_b.url(), issue_filter, Duration::from_secs(3)).await;
println!("Issue synced to relay_b: {}", issue_synced);
@@ -247,7 +247,7 @@ async fn test_live_sync_layer3_events() {
// 7. Wait and verify comment syncs to relay_b
let comment_filter = Filter::new()
.kind(Kind::Custom(KIND_COMMENT))
.kind(Kind::Comment)
.author(keys.public_key())
.id(comment_id);
@@ -267,9 +267,7 @@ async fn test_live_sync_layer3_events() {
client.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
let fetch_filter = Filter::new()
.kind(Kind::Custom(KIND_COMMENT))
.id(comment_id);
let fetch_filter = Filter::new().kind(Kind::Comment).id(comment_id);
if let Ok(events) = client
.fetch_events(fetch_filter, Duration::from_secs(2))
@@ -418,9 +416,7 @@ async fn test_live_sync_event_ordering() {
client.connect().await;
tokio::time::sleep(Duration::from_millis(500)).await;
let filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.author(keys.public_key());
let filter = Filter::new().kind(Kind::GitIssue).author(keys.public_key());
match client.fetch_events(filter, Duration::from_secs(3)).await {
Ok(events) => {
+7 -7
View File
@@ -17,7 +17,7 @@ use nostr_sdk::prelude::*;
use crate::common::{
sync_helpers::{
create_repo_announcement, fetch_metrics, wait_for_sync_connection, MetricsTestHarness,
ParsedMetrics, TestClient, KIND_REPOSITORY_STATE,
ParsedMetrics, TestClient,
},
TestRelay,
};
@@ -175,8 +175,8 @@ async fn test_metric_values_are_numeric() {
// Phase 2: Real Metrics Tests (Using MetricsTestHarness)
// ============================================================================
/// Kind 1617 - Patch event (NIP-34)
const KIND_PATCH: u16 = 1617;
// NOTE: Using rust-nostr Kind variant:
// - Kind::GitPatch.as_u16() -> Kind::GitPatch (1617)
/// Create an event referencing a repository coordinate via 'a' tag.
///
@@ -187,7 +187,7 @@ fn create_event_referencing_repo(keys: &Keys, repo_coord: &str, kind: u16, conte
vec![repo_coord.to_string()],
)];
EventBuilder::new(Kind::Custom(kind), content)
EventBuilder::new(Kind::from_u16(kind), content)
.tags(tags)
.sign_with_keys(keys)
.expect("Failed to sign event")
@@ -239,7 +239,7 @@ async fn test_startup_sync_event_count() {
// 5. Build the repo coordinate for the 'a' tag in the patches
let repo_coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_STATE,
Kind::GitRepoAnnouncement.as_u16(),
keys.public_key().to_hex(),
"test-repo-metrics"
);
@@ -250,7 +250,7 @@ async fn test_startup_sync_event_count() {
create_event_referencing_repo(
&keys,
&repo_coord,
KIND_PATCH,
Kind::GitPatch.as_u16(),
&format!("Test patch {}", i),
)
})
@@ -320,7 +320,7 @@ async fn test_startup_sync_event_count() {
// 12. Verify patches actually synced (functional check)
let filter = Filter::new()
.kind(Kind::Custom(KIND_PATCH))
.kind(Kind::Custom(Kind::GitPatch.as_u16()))
.author(keys.public_key());
let patches_synced = crate::common::sync_helpers::wait_for_event_on_relay(
+7 -7
View File
@@ -110,7 +110,7 @@ async fn test_layer2_sync_with_lowercase_a_tag() {
// 5. Wait and verify event syncs to relay_b
let filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.kind(Kind::GitIssue)
.author(keys.public_key())
.id(issue_id);
@@ -212,7 +212,7 @@ async fn test_layer2_sync_with_uppercase_a_tag() {
// 5. Wait and verify event syncs to relay_b
let filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.kind(Kind::GitIssue)
.author(keys.public_key())
.id(issue_id);
@@ -309,7 +309,7 @@ async fn test_layer2_sync_with_q_tag() {
// 5. Wait and verify event syncs to relay_b
let filter = Filter::new()
.kind(Kind::Custom(KIND_ISSUE))
.kind(Kind::GitIssue)
.author(keys.public_key())
.id(issue_id);
@@ -403,7 +403,7 @@ async fn test_layer3_sync_with_lowercase_e_tag() {
println!("Layer 2 issue {} sent to relay_a", issue_id);
// 5. Wait for issue to sync to relay_b
let issue_filter = Filter::new().kind(Kind::Custom(KIND_ISSUE)).id(issue_id);
let issue_filter = Filter::new().kind(Kind::GitIssue).id(issue_id);
let issue_synced =
wait_for_event_on_relay(relay_b.url(), issue_filter, Duration::from_secs(5)).await;
println!("Issue synced to relay_b: {}", issue_synced);
@@ -527,7 +527,7 @@ async fn test_layer3_sync_with_uppercase_e_tag() {
println!("Layer 2 issue {} sent to relay_a", issue_id);
// 5. Wait for issue to sync to relay_b
let issue_filter = Filter::new().kind(Kind::Custom(KIND_ISSUE)).id(issue_id);
let issue_filter = Filter::new().kind(Kind::GitIssue).id(issue_id);
let issue_synced =
wait_for_event_on_relay(relay_b.url(), issue_filter, Duration::from_secs(5)).await;
println!("Issue synced to relay_b: {}", issue_synced);
@@ -567,7 +567,7 @@ async fn test_layer3_sync_with_uppercase_e_tag() {
// 7. Wait and verify comment syncs to relay_b
let comment_filter = Filter::new()
.kind(Kind::Custom(KIND_COMMENT)) // Kind 1111
.kind(Kind::Comment) // Kind 1111
.author(keys.public_key())
.id(comment_id);
@@ -655,7 +655,7 @@ async fn test_layer3_sync_with_q_tag() {
println!("Layer 2 issue {} sent to relay_a", issue_id);
// 5. Wait for issue to sync to relay_b
let issue_filter = Filter::new().kind(Kind::Custom(KIND_ISSUE)).id(issue_id);
let issue_filter = Filter::new().kind(Kind::GitIssue).id(issue_id);
let issue_synced =
wait_for_event_on_relay(relay_b.url(), issue_filter, Duration::from_secs(5)).await;
println!("Issue synced to relay_b: {}", issue_synced);