remove stupid tests and methods

This commit is contained in:
DanConwayDev
2025-12-05 16:37:09 +00:00
parent b7aa7b72c1
commit b4da09689e
4 changed files with 173 additions and 395 deletions
+19 -22
View File
@@ -70,10 +70,8 @@ impl SyncConnection {
tracing::info!("Sync connection established to {}", url);
// Create subscription manager for this connection
let subscription_manager = SubscriptionManager::new(
filter_service.clone(),
remote_domain.to_string(),
);
let subscription_manager =
SubscriptionManager::new(filter_service.clone(), remote_domain.to_string());
Ok(Self {
url: url.to_string(),
@@ -208,10 +206,8 @@ impl SyncConnection {
/// - kind 30617/30618: New announcement → add Layer 2 subscription
/// - kind 1617/1618/1619/1621/1622: New PR/Issue → add Layer 3 subscription
async fn handle_dynamic_subscription(&mut self, event: &Event) {
let kind = event.kind.as_u16();
// Check if this is an announcement kind (triggers Layer 2 subscription)
if SubscriptionManager::is_announcement_kind(kind) {
if matches!(event.kind, Kind::GitRepoAnnouncement | Kind::RepoState) {
if let Some(new_filters) = self.subscription_manager.add_announcement(event) {
tracing::info!(
"New announcement {} on {}, adding {} Layer 2 filter(s) (total filters: {})",
@@ -224,8 +220,11 @@ impl SyncConnection {
}
}
// Check if this is a PR/Issue kind (triggers Layer 3 subscription)
if SubscriptionManager::is_pr_issue_kind(kind) {
// Check if this is a Patch/PR/Issue kind (triggers Layer 3 subscription)
if matches!(
event.kind,
Kind::GitPatch | Kind::GitIssue | Kind::Custom(1618)
) {
if let Some(new_filters) = self.subscription_manager.add_event(event) {
tracing::info!(
"New PR/Issue {} on {}, adding {} Layer 3 filter(s) (total filters: {})",
@@ -366,11 +365,13 @@ pub async fn connect_with_retry(
);
}
match SyncConnection::new(url, filter_service.clone(), &remote_domain, metrics.clone()).await {
match SyncConnection::new(url, filter_service.clone(), &remote_domain, metrics.clone())
.await
{
Ok(conn) => {
// Record successful connection
health_tracker.record_success(url);
// Record metrics
if let Some(ref m) = metrics {
m.record_connection_attempt(url, true);
@@ -379,7 +380,7 @@ pub async fn connect_with_retry(
m.record_health_state(url, health_tracker.get_state(url));
m.record_failure_count(url, 0);
}
tracing::info!("Sync connection established to {}", url);
// Run the connection (this blocks until disconnection)
@@ -388,7 +389,7 @@ pub async fn connect_with_retry(
// Connection ended - record as failure for reconnection backoff
// (The connection ending is considered a failure even if it worked for a while)
health_tracker.record_failure(url);
// Update metrics for disconnection
if let Some(ref m) = metrics {
m.set_relay_connected(url, false);
@@ -396,7 +397,7 @@ pub async fn connect_with_retry(
m.record_health_state(url, health_tracker.get_state(url));
m.record_failure_count(url, health_tracker.get_failure_count(url));
}
tracing::warn!("Sync connection to {} ended, will reconnect", url);
}
Err(e) => {
@@ -405,14 +406,14 @@ pub async fn connect_with_retry(
let failure_count = health_tracker.get_failure_count(url);
let state = health_tracker.get_state(url);
// Record metrics
if let Some(ref m) = metrics {
m.record_connection_attempt(url, false);
m.set_relay_connected(url, false);
m.record_health_state(url, state);
m.record_failure_count(url, failure_count);
// Track dead relays
if state == super::health::HealthState::Dead {
m.inc_dead_count();
@@ -435,11 +436,7 @@ pub async fn connect_with_retry(
.get_remaining_backoff(url)
.unwrap_or(Duration::from_secs(5));
tracing::debug!(
"Waiting {:?} before reconnecting to {}",
wait_duration,
url
);
tracing::debug!("Waiting {:?} before reconnecting to {}", wait_duration, url);
tokio::time::sleep(wait_duration).await;
}
}
@@ -473,4 +470,4 @@ mod tests {
Some("relay.example.com".to_string())
);
}
}
}
+11 -60
View File
@@ -26,12 +26,6 @@ use super::filter::FilterService;
/// Maximum number of filters before consolidation is triggered
const CONSOLIDATION_THRESHOLD: usize = 150;
/// Kind 30617 - Repository Announcement (NIP-34)
const KIND_REPOSITORY_ANNOUNCEMENT: u16 = 30617;
/// Kind 30618 - Maintainer List (NIP-34)
const KIND_MAINTAINER_LIST: u16 = 30618;
/// Manages subscriptions for a single relay connection
///
/// Tracks which announcements and events have been subscribed to,
@@ -113,10 +107,7 @@ impl SubscriptionManager {
// Build Layer 3 filter for this event
// Layer 3 filters target events with 'e' tags pointing to this event
let filter = Filter::new().custom_tag(
SingleLetterTag::lowercase(Alphabet::E),
event_id,
);
let filter = Filter::new().custom_tag(SingleLetterTag::lowercase(Alphabet::E), event_id);
Some(vec![filter])
}
@@ -212,67 +203,27 @@ impl SubscriptionManager {
}
};
// Determine the kind for the coordinate
let kind = event.kind.as_u16();
if kind != KIND_REPOSITORY_ANNOUNCEMENT && kind != KIND_MAINTAINER_LIST {
// Verify this is an announcement kind
if !matches!(event.kind, Kind::GitRepoAnnouncement | Kind::RepoState) {
tracing::warn!(
"Event {} is not an announcement (kind {}), cannot build Layer 2 filter",
event.id.to_hex(),
kind
event.kind
);
return Vec::new();
}
// Build the addressable coordinate: kind:pubkey:identifier
let coord = format!("{}:{}:{}", kind, event.pubkey.to_hex(), identifier);
let coord = format!(
"{}:{}:{}",
event.kind.as_u16(),
event.pubkey.to_hex(),
identifier
);
// Create filter with 'a' tag for this coordinate
let filter = Filter::new().custom_tag(
SingleLetterTag::lowercase(Alphabet::A),
coord,
);
let filter = Filter::new().custom_tag(SingleLetterTag::lowercase(Alphabet::A), coord);
vec![filter]
}
/// Check if an event kind is an announcement kind
pub fn is_announcement_kind(kind: u16) -> bool {
kind == KIND_REPOSITORY_ANNOUNCEMENT || kind == KIND_MAINTAINER_LIST
}
/// Check if an event kind is a PR/Issue/Patch kind that should trigger Layer 3
pub fn is_pr_issue_kind(kind: u16) -> bool {
matches!(
kind,
1617 | // Patch proposal (NIP-34)
1618 | // PR
1619 | // PR Update
1621 | // Issue
1622 // Reply
)
}
}
#[cfg(test)]
mod tests {
use super::SubscriptionManager;
#[test]
fn test_is_announcement_kind() {
assert!(SubscriptionManager::is_announcement_kind(30617));
assert!(SubscriptionManager::is_announcement_kind(30618));
assert!(!SubscriptionManager::is_announcement_kind(1));
assert!(!SubscriptionManager::is_announcement_kind(1617));
}
#[test]
fn test_is_pr_issue_kind() {
assert!(SubscriptionManager::is_pr_issue_kind(1617));
assert!(SubscriptionManager::is_pr_issue_kind(1618));
assert!(SubscriptionManager::is_pr_issue_kind(1619));
assert!(SubscriptionManager::is_pr_issue_kind(1621));
assert!(SubscriptionManager::is_pr_issue_kind(1622));
assert!(!SubscriptionManager::is_pr_issue_kind(30617));
assert!(!SubscriptionManager::is_pr_issue_kind(1));
}
}
+43 -126
View File
@@ -16,89 +16,6 @@
use ngit_grasp::sync::SubscriptionManager;
// ============================================================================
// Configuration Constants Tests
// ============================================================================
/// Test that default startup delay is 30 seconds
#[test]
fn test_default_startup_delay_is_30_seconds() {
// The spec requires 30s warm-up before startup catchup
const EXPECTED_STARTUP_DELAY: u64 = 30;
// This is defined in negentropy.rs as DEFAULT_STARTUP_DELAY_SECS
// We verify the expected value matches the spec
assert_eq!(EXPECTED_STARTUP_DELAY, 30);
}
/// Test that default reconnect delay is 10 seconds
#[test]
fn test_default_reconnect_delay_is_10_seconds() {
// The spec requires 10s delay after reconnection before catchup
const EXPECTED_RECONNECT_DELAY: u64 = 10;
assert_eq!(EXPECTED_RECONNECT_DELAY, 10);
}
/// Test that reconnect lookback is 3 days
#[test]
fn test_reconnect_lookback_is_3_days() {
// The spec requires 3 days lookback for reconnect catchup
const EXPECTED_LOOKBACK_DAYS: u64 = 3;
const EXPECTED_LOOKBACK_SECS: u64 = 3 * 24 * 60 * 60; // 259,200 seconds
assert_eq!(EXPECTED_LOOKBACK_DAYS, 3);
assert_eq!(EXPECTED_LOOKBACK_SECS, 259200);
}
/// Test daily catchup interval is 24 hours
#[test]
fn test_daily_catchup_interval_is_24_hours() {
// The spec requires daily catchup once per 24 hours
const EXPECTED_DAILY_INTERVAL_SECS: u64 = 86400; // 24 * 60 * 60
assert_eq!(EXPECTED_DAILY_INTERVAL_SECS, 86400);
}
/// Test relay stagger delay is 5 minutes
#[test]
fn test_relay_stagger_is_5_minutes() {
// The spec requires 5-minute stagger between relays for catchup
const EXPECTED_STAGGER_SECS: u64 = 300; // 5 * 60
assert_eq!(EXPECTED_STAGGER_SECS, 300);
}
// ============================================================================
// Filter Compatibility Tests
// ============================================================================
/// Test that catchup uses announcement kinds (30617, 30618)
#[test]
fn test_catchup_uses_announcement_kinds() {
// Layer 1 filters should include announcement kinds
assert!(SubscriptionManager::is_announcement_kind(30617));
assert!(SubscriptionManager::is_announcement_kind(30618));
}
/// Test that catchup uses PR/Issue kinds for Layer 3
#[test]
fn test_catchup_uses_pr_issue_kinds() {
// Layer 3 should track PR and Issue kinds
assert!(SubscriptionManager::is_pr_issue_kind(1617)); // Patch proposal
assert!(SubscriptionManager::is_pr_issue_kind(1618)); // PR
assert!(SubscriptionManager::is_pr_issue_kind(1619)); // PR Update
assert!(SubscriptionManager::is_pr_issue_kind(1621)); // Issue
assert!(SubscriptionManager::is_pr_issue_kind(1622)); // Reply
}
/// Test that non-sync kinds are not included in catchup
#[test]
fn test_catchup_excludes_non_sync_kinds() {
// Regular text notes and other kinds should not be included
assert!(!SubscriptionManager::is_announcement_kind(1)); // Text note
assert!(!SubscriptionManager::is_announcement_kind(4)); // DM
assert!(!SubscriptionManager::is_pr_issue_kind(1)); // Text note
assert!(!SubscriptionManager::is_pr_issue_kind(30617)); // Announcement (wrong layer)
}
// ============================================================================
// Catchup State Machine Tests
// ============================================================================
@@ -108,17 +25,17 @@ fn test_catchup_excludes_non_sync_kinds() {
fn test_startup_catchup_runs_once() {
// After startup catchup completes, should_run_startup_catchup should return false
// This is handled by the startup_catchup_completed flag in NegentropyService
// Simulating the state machine:
let mut startup_completed = false;
// Before running, should return true (if delay elapsed)
let should_run_before = !startup_completed;
assert!(should_run_before);
// After running, mark as completed
startup_completed = true;
// Now should return false
let should_run_after = !startup_completed;
assert!(!should_run_after);
@@ -128,12 +45,12 @@ fn test_startup_catchup_runs_once() {
#[test]
fn test_daily_catchup_interval_check() {
use std::time::{Duration, Instant};
const DAILY_INTERVAL_SECS: u64 = 86400;
// Simulate last catchup time
let last_catchup = Instant::now();
// Immediately after, should not run
let should_run_immediately = last_catchup.elapsed() >= Duration::from_secs(DAILY_INTERVAL_SECS);
assert!(!should_run_immediately);
@@ -144,10 +61,10 @@ fn test_daily_catchup_interval_check() {
fn test_new_relay_should_run_daily_catchup() {
use std::collections::HashMap;
use std::time::Instant;
let last_daily_catchup: HashMap<String, Instant> = HashMap::new();
let relay_url = "wss://test-relay.example.com";
// No previous catchup recorded, should return true
let should_run = !last_daily_catchup.contains_key(relay_url);
assert!(should_run);
@@ -159,14 +76,14 @@ fn test_reconnect_catchup_after_reconnection() {
// Reconnect catchup should only trigger when:
// 1. Connection was previously successful (had_previous_connection = true)
// 2. Connection was lost and restored
let mut had_previous_connection = false;
// First connection - should NOT trigger reconnect catchup
let is_reconnection_first = had_previous_connection;
assert!(!is_reconnection_first);
had_previous_connection = true;
// Second connection (after disconnection) - SHOULD trigger
let is_reconnection_second = had_previous_connection;
assert!(is_reconnection_second);
@@ -185,10 +102,10 @@ fn test_gap_events_validated_through_policy() {
// 2. Check if event exists locally
// 3. Validate through Nip34WritePolicy
// 4. Store if accepted
// This is verified by the implementation in negentropy.rs:run_catchup()
// where PolicyResult::Accept leads to storage and PolicyResult::Reject is logged
assert!(true); // Flow verification - actual validation tested in other tests
}
@@ -197,14 +114,14 @@ fn test_gap_events_validated_through_policy() {
fn test_gap_events_logged_at_warn_level() {
// The spec requires gap events to be logged at WARN level
// to distinguish them from live events (which are logged at INFO)
// This is implemented in negentropy.rs with:
// tracing::warn!("Gap event filled via {} catchup: {} (kind {})", ...)
// We verify the logging pattern exists by testing the catchup types
let catchup_types = ["startup", "reconnect", "daily"];
assert_eq!(catchup_types.len(), 3);
for catchup_type in catchup_types {
assert!(!catchup_type.is_empty());
}
@@ -218,21 +135,21 @@ fn test_gap_events_logged_at_warn_level() {
#[test]
fn test_stagger_delay_for_multiple_relays() {
const STAGGER_SECS: u64 = 300; // 5 minutes
let _relay_urls = vec![
"wss://relay1.example.com",
"wss://relay2.example.com",
"wss://relay2.example.com",
"wss://relay3.example.com",
];
// First relay (index 0) should have no stagger
let stagger_0 = 0 * STAGGER_SECS;
assert_eq!(stagger_0, 0);
// Second relay (index 1) should have 5 minute stagger
let stagger_1 = 1 * STAGGER_SECS;
assert_eq!(stagger_1, 300);
// Third relay (index 2) should have 10 minute stagger
let stagger_2 = 2 * STAGGER_SECS;
assert_eq!(stagger_2, 600);
@@ -242,15 +159,15 @@ fn test_stagger_delay_for_multiple_relays() {
#[test]
fn test_startup_catchup_waits_for_warmup() {
use std::time::{Duration, Instant};
const STARTUP_DELAY_SECS: u64 = 30;
let startup_time = Instant::now();
// Immediately after startup, should not run (delay not elapsed)
let elapsed = startup_time.elapsed();
let should_run = elapsed >= Duration::from_secs(STARTUP_DELAY_SECS);
// This should be false since we just created startup_time
assert!(!should_run);
}
@@ -265,7 +182,7 @@ fn test_reconnect_lookback_calculation() {
// 3 days = 3 * 24 * 60 * 60 = 259,200 seconds
let lookback_days: u64 = 3;
let lookback_secs = lookback_days * 24 * 60 * 60;
assert_eq!(lookback_secs, 259200);
}
@@ -290,10 +207,10 @@ fn test_startup_catchup_scenario() {
// 2. Run full reconciliation (no time limit)
// 3. Mark as completed (runs only once)
// 4. Stagger between relays (5 minutes)
const STARTUP_DELAY: u64 = 30;
const STAGGER: u64 = 300;
assert_eq!(STARTUP_DELAY, 30);
assert_eq!(STAGGER, 300);
}
@@ -306,10 +223,10 @@ fn test_reconnect_catchup_scenario() {
// 2. Wait 10s reconnect delay
// 3. Only fetch last 3 days of events
// 4. Runs in background (doesn't block connection)
const RECONNECT_DELAY: u64 = 10;
const LOOKBACK_DAYS: u64 = 3;
assert_eq!(RECONNECT_DELAY, 10);
assert_eq!(LOOKBACK_DAYS, 3);
}
@@ -322,11 +239,11 @@ fn test_daily_catchup_scenario() {
// 2. Run if 24h elapsed since last catchup for that relay
// 3. Full reconciliation (no time limit)
// 4. Stagger between relays (5 minutes)
const CHECK_INTERVAL: u64 = 3600; // 1 hour
const DAILY_INTERVAL: u64 = 86400; // 24 hours
const STAGGER: u64 = 300; // 5 minutes
assert_eq!(CHECK_INTERVAL, 3600);
assert_eq!(DAILY_INTERVAL, 86400);
assert_eq!(STAGGER, 300);
@@ -343,10 +260,10 @@ fn test_existing_events_skipped() {
// 1. Fetch events from relay
// 2. For each event, check if it exists locally
// 3. Skip if exists, validate and store if not
// This is implemented in negentropy.rs:event_exists_locally()
// which queries the database for the event by ID
const SKIP_EXISTING: bool = true;
assert!(SKIP_EXISTING);
}
@@ -355,15 +272,15 @@ fn test_existing_events_skipped() {
#[test]
fn test_duplicate_prevention() {
use std::collections::HashSet;
let mut processed_ids: HashSet<String> = HashSet::new();
let event_id = "abc123def456".to_string();
// First time seeing this event - should process
let is_new = !processed_ids.contains(&event_id);
assert!(is_new);
processed_ids.insert(event_id.clone());
// Second time - should skip
let is_duplicate = processed_ids.contains(&event_id);
assert!(is_duplicate);
@@ -380,18 +297,18 @@ fn test_config_fields_for_catchup() {
// - sync_startup_delay_secs (default: 30)
// - sync_reconnect_delay_secs (default: 10)
// - sync_reconnect_lookback_days (default: 3)
// Environment variables:
// - NGIT_SYNC_STARTUP_DELAY_SECS
// - NGIT_SYNC_RECONNECT_DELAY_SECS
// - NGIT_SYNC_RECONNECT_LOOKBACK_DAYS
let expected_defaults = vec![
("startup_delay_secs", 30u64),
("reconnect_delay_secs", 10u64),
("reconnect_lookback_days", 3u64),
];
assert_eq!(expected_defaults.len(), 3);
assert_eq!(expected_defaults[0].1, 30);
assert_eq!(expected_defaults[1].1, 10);
@@ -405,9 +322,9 @@ fn test_catchup_respects_config() {
let custom_startup_delay: u64 = 60;
let custom_reconnect_delay: u64 = 20;
let custom_lookback_days: u64 = 7;
// All should be configurable to non-default values
assert_ne!(custom_startup_delay, 30);
assert_ne!(custom_reconnect_delay, 10);
assert_ne!(custom_lookback_days, 3);
}
}
+100 -187
View File
@@ -116,69 +116,6 @@ fn create_test_reply_event(keys: &Keys, event_id: &str) -> Event {
.expect("Failed to sign event")
}
// ============================================================================
// Kind Detection Tests
// ============================================================================
/// Test that announcement kinds are correctly identified
#[test]
fn test_is_announcement_kind_30617() {
assert!(SubscriptionManager::is_announcement_kind(30617));
}
/// Test that maintainer list kind is correctly identified
#[test]
fn test_is_announcement_kind_30618() {
assert!(SubscriptionManager::is_announcement_kind(30618));
}
/// Test that non-announcement kinds are not identified as announcements
#[test]
fn test_is_announcement_kind_negative() {
assert!(!SubscriptionManager::is_announcement_kind(1)); // Text note
assert!(!SubscriptionManager::is_announcement_kind(1617)); // PR
assert!(!SubscriptionManager::is_announcement_kind(1621)); // Issue
assert!(!SubscriptionManager::is_announcement_kind(0)); // Unknown
}
/// Test that PR/Issue kinds are correctly identified
#[test]
fn test_is_pr_issue_kind_1617() {
assert!(SubscriptionManager::is_pr_issue_kind(1617)); // Patch proposal
}
/// Test that PR kind 1618 is correctly identified
#[test]
fn test_is_pr_issue_kind_1618() {
assert!(SubscriptionManager::is_pr_issue_kind(1618)); // PR
}
/// Test that PR update kind is correctly identified
#[test]
fn test_is_pr_issue_kind_1619() {
assert!(SubscriptionManager::is_pr_issue_kind(1619)); // PR Update
}
/// Test that Issue kind is correctly identified
#[test]
fn test_is_pr_issue_kind_1621() {
assert!(SubscriptionManager::is_pr_issue_kind(1621)); // Issue
}
/// Test that Reply kind is correctly identified
#[test]
fn test_is_pr_issue_kind_1622() {
assert!(SubscriptionManager::is_pr_issue_kind(1622)); // Reply
}
/// Test that non-PR/Issue kinds are not identified
#[test]
fn test_is_pr_issue_kind_negative() {
assert!(!SubscriptionManager::is_pr_issue_kind(30617)); // Announcement
assert!(!SubscriptionManager::is_pr_issue_kind(1)); // Text note
assert!(!SubscriptionManager::is_pr_issue_kind(0)); // Unknown
}
// ============================================================================
// Filter Count Tests
// ============================================================================
@@ -188,19 +125,19 @@ fn test_is_pr_issue_kind_negative() {
fn test_initial_filter_count() {
// Create a minimal SubscriptionManager-like state for testing
// We test the logic without needing a full FilterService
// Initial state: 0 announcements, 0 events, not consolidated
// Filter count should be: 1 (Layer 1) + 0 + 0 = 1
let announcement_count = 0;
let event_count = 0;
let is_consolidated = false;
let filter_count = if is_consolidated {
1
} else {
1 + announcement_count + event_count
};
assert_eq!(filter_count, 1);
}
@@ -210,13 +147,13 @@ fn test_filter_count_with_announcements() {
let announcement_count = 5;
let event_count = 0;
let is_consolidated = false;
let filter_count = if is_consolidated {
1
} else {
1 + announcement_count + event_count
};
// 1 (Layer 1) + 5 (announcements) = 6
assert_eq!(filter_count, 6);
}
@@ -227,13 +164,13 @@ fn test_filter_count_with_events() {
let announcement_count = 0;
let event_count = 10;
let is_consolidated = false;
let filter_count = if is_consolidated {
1
} else {
1 + announcement_count + event_count
};
// 1 (Layer 1) + 10 (events) = 11
assert_eq!(filter_count, 11);
}
@@ -244,13 +181,13 @@ fn test_filter_count_mixed() {
let announcement_count = 50;
let event_count = 30;
let is_consolidated = false;
let filter_count = if is_consolidated {
1
} else {
1 + announcement_count + event_count
};
// 1 + 50 + 30 = 81
assert_eq!(filter_count, 81);
}
@@ -261,13 +198,13 @@ fn test_filter_count_consolidated() {
let announcement_count = 100; // These would be cleared on consolidation
let event_count = 100;
let is_consolidated = true;
let filter_count = if is_consolidated {
1
} else {
1 + announcement_count + event_count
};
assert_eq!(filter_count, 1);
}
@@ -280,9 +217,9 @@ fn test_filter_count_consolidated() {
fn test_should_consolidate_below_threshold() {
let filter_count = 100;
let is_consolidated = false;
let should_consolidate = !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD;
assert!(!should_consolidate);
}
@@ -291,9 +228,9 @@ fn test_should_consolidate_below_threshold() {
fn test_should_consolidate_at_threshold() {
let filter_count = 151; // > 150
let is_consolidated = false;
let should_consolidate = !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD;
assert!(should_consolidate);
}
@@ -302,9 +239,9 @@ fn test_should_consolidate_at_threshold() {
fn test_should_consolidate_above_threshold() {
let filter_count = 200;
let is_consolidated = false;
let should_consolidate = !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD;
assert!(should_consolidate);
}
@@ -313,9 +250,9 @@ fn test_should_consolidate_above_threshold() {
fn test_should_consolidate_already_consolidated() {
let filter_count = 200; // Would trigger, but already consolidated
let is_consolidated = true;
let should_consolidate = !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD;
assert!(!should_consolidate);
}
@@ -323,11 +260,11 @@ fn test_should_consolidate_already_consolidated() {
#[test]
fn test_consolidation_threshold_boundary() {
let is_consolidated = false;
// 150 should NOT trigger (> 150, not >= 150)
let should_consolidate_at_150 = !is_consolidated && 150 > CONSOLIDATION_THRESHOLD;
assert!(!should_consolidate_at_150);
// 151 should trigger
let should_consolidate_at_151 = !is_consolidated && 151 > CONSOLIDATION_THRESHOLD;
assert!(should_consolidate_at_151);
@@ -341,14 +278,14 @@ fn test_consolidation_threshold_boundary() {
#[test]
fn test_duplicate_announcement_prevention() {
let mut subscribed_announcements: HashSet<String> = HashSet::new();
let event_id = "abc123".to_string();
// First add should succeed
let is_new = !subscribed_announcements.contains(&event_id);
assert!(is_new);
subscribed_announcements.insert(event_id.clone());
// Second add should fail (duplicate)
let is_new_again = !subscribed_announcements.contains(&event_id);
assert!(!is_new_again);
@@ -358,14 +295,14 @@ fn test_duplicate_announcement_prevention() {
#[test]
fn test_duplicate_event_prevention() {
let mut subscribed_events: HashSet<String> = HashSet::new();
let event_id = "def456".to_string();
// First add should succeed
let is_new = !subscribed_events.contains(&event_id);
assert!(is_new);
subscribed_events.insert(event_id.clone());
// Second add should fail (duplicate)
let is_new_again = !subscribed_events.contains(&event_id);
assert!(!is_new_again);
@@ -375,14 +312,14 @@ fn test_duplicate_event_prevention() {
#[test]
fn test_multiple_unique_items_tracked() {
let mut subscribed_announcements: HashSet<String> = HashSet::new();
// Add multiple unique announcements
for i in 0..10 {
let id = format!("announcement_{}", i);
assert!(!subscribed_announcements.contains(&id));
subscribed_announcements.insert(id);
}
assert_eq!(subscribed_announcements.len(), 10);
}
@@ -395,12 +332,12 @@ fn test_multiple_unique_items_tracked() {
fn test_announcement_has_d_tag() {
let keys = Keys::generate();
let event = create_test_announcement(&keys, "my-repo");
let has_d_tag = event.tags.iter().any(|tag| {
let tag_vec = tag.clone().to_vec();
tag_vec.len() >= 2 && tag_vec[0] == "d"
});
assert!(has_d_tag);
}
@@ -409,7 +346,7 @@ fn test_announcement_has_d_tag() {
fn test_announcement_correct_kind() {
let keys = Keys::generate();
let event = create_test_announcement(&keys, "my-repo");
assert_eq!(event.kind.as_u16(), KIND_REPOSITORY_ANNOUNCEMENT);
}
@@ -418,7 +355,7 @@ fn test_announcement_correct_kind() {
fn test_maintainer_list_correct_kind() {
let keys = Keys::generate();
let event = create_test_maintainer_list(&keys, "maintainers");
assert_eq!(event.kind.as_u16(), KIND_MAINTAINER_LIST);
}
@@ -428,12 +365,12 @@ fn test_pr_event_has_a_tag() {
let keys = Keys::generate();
let coord = "30617:pubkey123:my-repo";
let event = create_test_pr_event(&keys, coord);
let has_a_tag = event.tags.iter().any(|tag| {
let tag_vec = tag.clone().to_vec();
tag_vec.len() >= 2 && tag_vec[0] == "a"
});
assert!(has_a_tag);
}
@@ -443,12 +380,12 @@ fn test_issue_event_has_a_tag() {
let keys = Keys::generate();
let coord = "30617:pubkey123:my-repo";
let event = create_test_issue_event(&keys, coord);
let has_a_tag = event.tags.iter().any(|tag| {
let tag_vec = tag.clone().to_vec();
tag_vec.len() >= 2 && tag_vec[0] == "a"
});
assert!(has_a_tag);
}
@@ -458,12 +395,12 @@ fn test_reply_event_has_e_tag() {
let keys = Keys::generate();
let event_id = "abc123def456";
let event = create_test_reply_event(&keys, event_id);
let has_e_tag = event.tags.iter().any(|tag| {
let tag_vec = tag.clone().to_vec();
tag_vec.len() >= 2 && tag_vec[0] == "e"
});
assert!(has_e_tag);
}
@@ -477,50 +414,55 @@ fn test_subscription_lifecycle() {
let mut subscribed_announcements: HashSet<String> = HashSet::new();
let mut subscribed_events: HashSet<String> = HashSet::new();
let mut is_consolidated = false;
// Initial state
let initial_count = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(initial_count, 1);
// Add some announcements
for i in 0..50 {
subscribed_announcements.insert(format!("ann_{}", i));
}
let after_announcements = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(after_announcements, 51);
// Add some events
for i in 0..50 {
subscribed_events.insert(format!("evt_{}", i));
}
let after_events = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(after_events, 101);
// Add more to exceed threshold
for i in 50..100 {
subscribed_announcements.insert(format!("ann_{}", i));
}
let before_consolidation = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(before_consolidation, 151);
// Should trigger consolidation
let should_consolidate = !is_consolidated && before_consolidation > CONSOLIDATION_THRESHOLD;
assert!(should_consolidate);
// Consolidate
subscribed_announcements.clear();
subscribed_events.clear();
is_consolidated = true;
// After consolidation
let after_consolidation = if is_consolidated { 1 } else { 1 + subscribed_announcements.len() + subscribed_events.len() };
let after_consolidation = if is_consolidated {
1
} else {
1 + subscribed_announcements.len() + subscribed_events.len()
};
assert_eq!(after_consolidation, 1);
// Should not trigger consolidation again
let should_consolidate_again = !is_consolidated && after_consolidation > CONSOLIDATION_THRESHOLD;
let should_consolidate_again =
!is_consolidated && after_consolidation > CONSOLIDATION_THRESHOLD;
assert!(!should_consolidate_again);
}
@@ -528,11 +470,11 @@ fn test_subscription_lifecycle() {
#[test]
fn test_consolidated_blocks_additions() {
let is_consolidated = true;
// When consolidated, add_announcement should return None (simulated)
// The logic is: if is_consolidated, return None
let should_add = !is_consolidated;
assert!(!should_add);
}
@@ -542,12 +484,12 @@ fn test_non_consolidated_allows_additions() {
let is_consolidated = false;
let mut subscribed_announcements: HashSet<String> = HashSet::new();
let event_id = "new_announcement";
// When not consolidated and event not in set, should add
let should_add = !is_consolidated && !subscribed_announcements.contains(event_id);
assert!(should_add);
subscribed_announcements.insert(event_id.to_string());
assert!(subscribed_announcements.contains(event_id));
}
@@ -562,7 +504,7 @@ fn test_announcement_coordinate_format() {
let keys = Keys::generate();
let identifier = "my-repo";
let event = create_test_announcement(&keys, identifier);
// Extract d tag
let d_tag = event.tags.iter().find_map(|tag| {
let tag_vec = tag.clone().to_vec();
@@ -572,13 +514,18 @@ fn test_announcement_coordinate_format() {
None
}
});
assert!(d_tag.is_some());
assert_eq!(d_tag.unwrap(), identifier);
// Build coordinate: kind:pubkey:identifier
let coord = format!("{}:{}:{}", KIND_REPOSITORY_ANNOUNCEMENT, event.pubkey.to_hex(), identifier);
let coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_ANNOUNCEMENT,
event.pubkey.to_hex(),
identifier
);
// Verify format
let parts: Vec<&str> = coord.split(':').collect();
assert_eq!(parts.len(), 3);
@@ -590,16 +537,21 @@ fn test_announcement_coordinate_format() {
#[test]
fn test_multiple_announcement_coordinates_unique() {
let keys = Keys::generate();
let identifiers = vec!["repo1", "repo2", "repo3"];
let mut coords: HashSet<String> = HashSet::new();
for id in identifiers {
let event = create_test_announcement(&keys, id);
let coord = format!("{}:{}:{}", KIND_REPOSITORY_ANNOUNCEMENT, event.pubkey.to_hex(), id);
let coord = format!(
"{}:{}:{}",
KIND_REPOSITORY_ANNOUNCEMENT,
event.pubkey.to_hex(),
id
);
coords.insert(coord);
}
assert_eq!(coords.len(), 3);
}
@@ -614,30 +566,34 @@ fn test_workflow_announcement_then_pr() {
let mut subscribed_announcements: HashSet<String> = HashSet::new();
let mut subscribed_events: HashSet<String> = HashSet::new();
let is_consolidated = false;
// Step 1: Receive announcement
let announcement = create_test_announcement(&keys, "my-repo");
let ann_id = announcement.id.to_hex();
// Should add to tracking (simulating add_announcement)
let should_add_ann = !is_consolidated && !subscribed_announcements.contains(&ann_id);
assert!(should_add_ann);
subscribed_announcements.insert(ann_id.clone());
// Filter count should increase
let filter_count = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(filter_count, 2);
// Step 2: Receive PR for that repo
let coord = format!("{}:{}:my-repo", KIND_REPOSITORY_ANNOUNCEMENT, keys.public_key().to_hex());
let coord = format!(
"{}:{}:my-repo",
KIND_REPOSITORY_ANNOUNCEMENT,
keys.public_key().to_hex()
);
let pr = create_test_pr_event(&keys, &coord);
let pr_id = pr.id.to_hex();
// Should add to tracking (simulating add_event)
let should_add_pr = !is_consolidated && !subscribed_events.contains(&pr_id);
assert!(should_add_pr);
subscribed_events.insert(pr_id.clone());
// Filter count should increase again
let filter_count = 1 + subscribed_announcements.len() + subscribed_events.len();
assert_eq!(filter_count, 3);
@@ -651,16 +607,16 @@ fn test_stress_many_items_triggers_consolidation() {
let mut subscribed_events: HashSet<String> = HashSet::new();
let mut is_consolidated = false;
let mut consolidation_triggered = false;
// Add 100 announcements
for i in 0..100 {
let event = create_test_announcement(&keys, &format!("repo-{}", i));
let event_id = event.id.to_hex();
if !is_consolidated && !subscribed_announcements.contains(&event_id) {
subscribed_announcements.insert(event_id);
}
// Check consolidation after each add
let filter_count = 1 + subscribed_announcements.len() + subscribed_events.len();
if !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD {
@@ -671,18 +627,18 @@ fn test_stress_many_items_triggers_consolidation() {
break;
}
}
// If we didn't consolidate yet, add events
if !consolidation_triggered {
for i in 0..100 {
let coord = format!("30617:pubkey:repo-{}", i);
let event = create_test_pr_event(&keys, &coord);
let event_id = event.id.to_hex();
if !is_consolidated && !subscribed_events.contains(&event_id) {
subscribed_events.insert(event_id);
}
// Check consolidation after each add
let filter_count = 1 + subscribed_announcements.len() + subscribed_events.len();
if !is_consolidated && filter_count > CONSOLIDATION_THRESHOLD {
@@ -694,55 +650,12 @@ fn test_stress_many_items_triggers_consolidation() {
}
}
}
// Consolidation should have been triggered
assert!(consolidation_triggered);
assert!(is_consolidated);
// After consolidation, counts should be reset
assert_eq!(subscribed_announcements.len(), 0);
assert_eq!(subscribed_events.len(), 0);
}
/// Test that all PR/Issue kinds are handled consistently
#[test]
fn test_all_pr_issue_kinds_handled() {
let keys = Keys::generate();
let coord = "30617:pubkey:repo";
// All these kinds should be identified as PR/Issue
let pr_kinds = vec![1617, 1618, 1619, 1621, 1622];
for kind in pr_kinds {
assert!(
SubscriptionManager::is_pr_issue_kind(kind),
"Kind {} should be identified as PR/Issue",
kind
);
}
}
/// Test that announcement and PR/Issue kinds are mutually exclusive
#[test]
fn test_kind_categories_mutually_exclusive() {
let announcement_kinds = vec![30617, 30618];
let pr_issue_kinds = vec![1617, 1618, 1619, 1621, 1622];
// No announcement kind should be a PR/Issue kind
for kind in &announcement_kinds {
assert!(
!SubscriptionManager::is_pr_issue_kind(*kind),
"Announcement kind {} should not be PR/Issue",
kind
);
}
// No PR/Issue kind should be an announcement kind
for kind in &pr_issue_kinds {
assert!(
!SubscriptionManager::is_announcement_kind(*kind),
"PR/Issue kind {} should not be announcement",
kind
);
}
}