mirror of https://github.com/synctv-org/synctv
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.8 KiB
Rust
182 lines
6.8 KiB
Rust
//! Tests for local-only `NodeRegistry` mode (no Redis required)
|
|
//!
|
|
//! These tests verify that `NodeRegistry` can function in a degraded/local mode
|
|
//! when Redis is unavailable, supporting the "non-cluster mode can work without Redis" architecture.
|
|
|
|
#![allow(clippy::unwrap_used)]
|
|
use synctv_cluster::{ClusterMode, NodeInfo, NodeRegistry};
|
|
|
|
/// Test that `NodeRegistry` can be created in local-only mode without Redis.
|
|
#[tokio::test]
|
|
async fn test_new_local_only_creates_registry() {
|
|
// This should succeed without requiring a Redis connection
|
|
let registry = NodeRegistry::new_local_only("test-node-local".to_string(), 30, "localtest:");
|
|
|
|
assert!(
|
|
registry.is_ok(),
|
|
"new_local_only should succeed without Redis"
|
|
);
|
|
}
|
|
|
|
/// Test that local-only registry starts in Standalone mode.
|
|
#[tokio::test]
|
|
async fn test_local_only_starts_in_standalone_mode() {
|
|
let registry =
|
|
NodeRegistry::new_local_only("test-node-standalone".to_string(), 30, "standalone:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
assert_eq!(
|
|
registry.cluster_mode(),
|
|
ClusterMode::Standalone,
|
|
"Local-only registry should start in Standalone mode"
|
|
);
|
|
}
|
|
|
|
/// Test that local-only registry can discover nodes from local cache.
|
|
#[tokio::test]
|
|
async fn test_local_only_discover_nodes_from_local_cache() {
|
|
let registry = NodeRegistry::new_local_only("test-node-discover".to_string(), 30, "discover:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
// Insert a node into local cache
|
|
let node_info = NodeInfo::new("peer-node".to_string(), "localhost:8081".to_string());
|
|
registry.test_insert_local(node_info).await;
|
|
|
|
// get_all_nodes_local should return the cached node
|
|
let nodes = registry.get_all_nodes_local().await;
|
|
assert_eq!(nodes.len(), 1, "Should find one node in local cache");
|
|
assert_eq!(nodes[0].node_id, "peer-node");
|
|
|
|
// get_all_nodes should also work (returns local cache in Standalone mode)
|
|
let all_nodes = registry
|
|
.get_all_nodes()
|
|
.await
|
|
.expect("get_all_nodes should succeed");
|
|
assert_eq!(all_nodes.len(), 1, "Should find one node via get_all_nodes");
|
|
assert_eq!(all_nodes[0].node_id, "peer-node");
|
|
}
|
|
|
|
/// Test that local-only registry can get a specific node from local cache.
|
|
#[tokio::test]
|
|
async fn test_local_only_get_node_from_local_cache() {
|
|
let registry = NodeRegistry::new_local_only("test-node-get".to_string(), 30, "gettest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
// Insert a node into local cache
|
|
let node_info = NodeInfo::new("specific-node".to_string(), "localhost:8082".to_string());
|
|
registry.test_insert_local(node_info.clone()).await;
|
|
|
|
// get_node_local should find it
|
|
let found = registry.get_node_local("specific-node").await;
|
|
assert!(found.is_some(), "Should find the node");
|
|
let found = found.unwrap();
|
|
assert_eq!(found.cluster_address, "localhost:8082");
|
|
}
|
|
|
|
/// Test that local-only registry handles empty cache gracefully.
|
|
#[tokio::test]
|
|
async fn test_local_only_empty_cache_returns_empty() {
|
|
let registry = NodeRegistry::new_local_only("test-node-empty".to_string(), 30, "emptytest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
// Empty cache should return empty vector
|
|
let nodes = registry.get_all_nodes_local().await;
|
|
assert!(nodes.is_empty(), "Empty cache should return empty vector");
|
|
|
|
// get_all_nodes should also return empty (no Redis fallback needed)
|
|
let all_nodes = registry
|
|
.get_all_nodes()
|
|
.await
|
|
.expect("get_all_nodes should succeed");
|
|
assert!(all_nodes.is_empty(), "Should return empty for empty cache");
|
|
}
|
|
|
|
/// Test that `is_nodes_stale` returns true for local-only registry that never refreshed.
|
|
#[tokio::test]
|
|
async fn test_local_only_is_nodes_stale_when_never_refreshed() {
|
|
let registry = NodeRegistry::new_local_only("test-node-stale".to_string(), 30, "staletest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
// Since there's no Redis, is_nodes_stale should return true (never refreshed from Redis)
|
|
assert!(
|
|
registry.is_nodes_stale(),
|
|
"Local-only registry should report stale (never refreshed from Redis)"
|
|
);
|
|
}
|
|
|
|
/// Test that `merge_dns_peers` works in local-only mode.
|
|
#[tokio::test]
|
|
async fn test_local_only_merge_dns_peers() {
|
|
let registry = NodeRegistry::new_local_only("test-node-dns".to_string(), 30, "dnstest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
// Merge some DNS-discovered peers
|
|
let peers = vec![
|
|
NodeInfo::new("dns-peer-1".to_string(), "10.0.0.1:50051".to_string()),
|
|
NodeInfo::new("dns-peer-2".to_string(), "10.0.0.2:50051".to_string()),
|
|
];
|
|
registry.merge_dns_peers(peers).await;
|
|
|
|
// Should find both peers in local cache
|
|
let nodes = registry.get_all_nodes_local().await;
|
|
assert_eq!(nodes.len(), 2, "Should have 2 peers from DNS merge");
|
|
|
|
let node_ids: std::collections::HashSet<_> = nodes.iter().map(|n| n.node_id.as_str()).collect();
|
|
assert!(node_ids.contains("dns-peer-1"));
|
|
assert!(node_ids.contains("dns-peer-2"));
|
|
}
|
|
|
|
/// Test that local write paths invalidate the short-lived node view cache immediately.
|
|
#[tokio::test]
|
|
async fn test_local_only_writes_are_immediately_visible_through_get_all_nodes() {
|
|
let registry = NodeRegistry::new_local_only("test-node-cache".to_string(), 30, "cachetest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
let initial = registry
|
|
.get_all_nodes()
|
|
.await
|
|
.expect("initial get_all_nodes should succeed");
|
|
assert!(initial.is_empty(), "cache should start empty");
|
|
|
|
registry
|
|
.test_insert_local(NodeInfo::new(
|
|
"peer-node".to_string(),
|
|
"localhost:8088".to_string(),
|
|
))
|
|
.await;
|
|
|
|
let after_insert = registry
|
|
.get_all_nodes()
|
|
.await
|
|
.expect("get_all_nodes after local insert should succeed");
|
|
assert_eq!(
|
|
after_insert.len(),
|
|
1,
|
|
"local insert must invalidate cached node view immediately"
|
|
);
|
|
assert_eq!(after_insert[0].node_id, "peer-node");
|
|
|
|
registry.test_remove_local("peer-node").await;
|
|
|
|
let after_remove = registry
|
|
.get_all_nodes()
|
|
.await
|
|
.expect("get_all_nodes after local removal should succeed");
|
|
assert!(
|
|
after_remove.is_empty(),
|
|
"local removal must invalidate cached node view immediately"
|
|
);
|
|
}
|
|
|
|
/// Test that local-only registry provides valid fencing token.
|
|
#[tokio::test]
|
|
async fn test_local_only_fencing_token() {
|
|
let registry =
|
|
NodeRegistry::new_local_only("test-node-fencing".to_string(), 30, "fencingtest:")
|
|
.expect("new_local_only should succeed");
|
|
|
|
let token = registry.current_fencing_token();
|
|
assert_eq!(token.node_id, "test-node-fencing");
|
|
assert!(token.epoch >= 1, "Epoch should start at 1 or higher");
|
|
}
|