Added http_addr field to test configurations after it was added to NetworkConfig for REST API support. This fixes Nix build failures during test compilation. Files fixed: - chainfire integration tests (3 occurrences) - plasmavmc grpc smoke test (1 occurrence) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
175 lines
5.1 KiB
Rust
175 lines
5.1 KiB
Rust
//! Integration tests for Chainfire
|
|
//!
|
|
//! These tests verify that the server, client, and all components work together correctly.
|
|
|
|
use chainfire_client::Client;
|
|
use chainfire_server::{
|
|
config::{ClusterConfig, NetworkConfig, NodeConfig, RaftConfig, ServerConfig, StorageConfig},
|
|
server::Server,
|
|
};
|
|
use std::time::Duration;
|
|
use tokio::time::sleep;
|
|
|
|
/// Create a test server configuration
|
|
fn test_config(port: u16) -> (ServerConfig, tempfile::TempDir) {
|
|
use std::net::SocketAddr;
|
|
|
|
let api_addr: SocketAddr = format!("127.0.0.1:{}", port).parse().unwrap();
|
|
let raft_addr: SocketAddr = format!("127.0.0.1:{}", port + 100).parse().unwrap();
|
|
let gossip_addr: SocketAddr = format!("127.0.0.1:{}", port + 200).parse().unwrap();
|
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
|
|
|
let config = ServerConfig {
|
|
node: NodeConfig {
|
|
id: 1,
|
|
name: format!("test-node-{}", port),
|
|
role: "control_plane".to_string(),
|
|
},
|
|
cluster: ClusterConfig {
|
|
id: 1,
|
|
bootstrap: true,
|
|
initial_members: vec![],
|
|
},
|
|
network: NetworkConfig {
|
|
api_addr,
|
|
http_addr: "127.0.0.1:28081".parse().unwrap(),
|
|
raft_addr,
|
|
gossip_addr,
|
|
tls: None,
|
|
},
|
|
storage: StorageConfig {
|
|
data_dir: temp_dir.path().to_path_buf(),
|
|
},
|
|
raft: RaftConfig::default(),
|
|
};
|
|
|
|
(config, temp_dir)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_single_node_kv_operations() {
|
|
// Start server
|
|
let (config, _temp_dir) = test_config(23790);
|
|
let api_addr = config.network.api_addr;
|
|
let server = Server::new(config).await.unwrap();
|
|
|
|
// Run server in background
|
|
let server_handle = tokio::spawn(async move {
|
|
let _ = server.run().await;
|
|
});
|
|
|
|
// Wait for server to start and Raft leader election
|
|
// Increased from 500ms to 2000ms for CI/constrained environments
|
|
sleep(Duration::from_millis(2000)).await;
|
|
|
|
// Connect client
|
|
let mut client = Client::connect(format!("http://{}", api_addr))
|
|
.await
|
|
.unwrap();
|
|
|
|
// Test put with retry (leader election may still be in progress)
|
|
let mut rev = 0;
|
|
for attempt in 0..5 {
|
|
match client.put("test/key1", "value1").await {
|
|
Ok(r) => {
|
|
rev = r;
|
|
break;
|
|
}
|
|
Err(e) if attempt < 4 => {
|
|
eprintln!("Put attempt {} failed: {}, retrying...", attempt + 1, e);
|
|
sleep(Duration::from_millis(500)).await;
|
|
}
|
|
Err(e) => panic!("Put failed after 5 attempts: {}", e),
|
|
}
|
|
}
|
|
assert!(rev > 0);
|
|
|
|
// Test get
|
|
let value = client.get("test/key1").await.unwrap();
|
|
assert_eq!(value, Some(b"value1".to_vec()));
|
|
|
|
// Test put with different value
|
|
let rev2 = client.put("test/key1", "value2").await.unwrap();
|
|
assert!(rev2 > rev);
|
|
|
|
// Test get updated value
|
|
let value = client.get("test/key1").await.unwrap();
|
|
assert_eq!(value, Some(b"value2".to_vec()));
|
|
|
|
// Test get non-existent key
|
|
let value = client.get("test/nonexistent").await.unwrap();
|
|
assert!(value.is_none());
|
|
|
|
// Test delete
|
|
let deleted = client.delete("test/key1").await.unwrap();
|
|
assert!(deleted);
|
|
|
|
// Verify deletion
|
|
let value = client.get("test/key1").await.unwrap();
|
|
assert!(value.is_none());
|
|
|
|
// Test delete non-existent key
|
|
let deleted = client.delete("test/nonexistent").await.unwrap();
|
|
assert!(!deleted);
|
|
|
|
// Test prefix operations
|
|
client.put("prefix/a", "1").await.unwrap();
|
|
client.put("prefix/b", "2").await.unwrap();
|
|
client.put("prefix/c", "3").await.unwrap();
|
|
client.put("other/key", "other").await.unwrap();
|
|
|
|
let prefix_values = client.get_prefix("prefix/").await.unwrap();
|
|
assert_eq!(prefix_values.len(), 3);
|
|
|
|
// Cleanup
|
|
server_handle.abort();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_cluster_status() {
|
|
let (config, _temp_dir) = test_config(23800);
|
|
let api_addr = config.network.api_addr;
|
|
let server = Server::new(config).await.unwrap();
|
|
|
|
let server_handle = tokio::spawn(async move {
|
|
let _ = server.run().await;
|
|
});
|
|
|
|
sleep(Duration::from_millis(500)).await;
|
|
|
|
let mut client = Client::connect(format!("http://{}", api_addr))
|
|
.await
|
|
.unwrap();
|
|
|
|
let status = client.status().await.unwrap();
|
|
assert_eq!(status.leader, 1);
|
|
assert!(status.raft_term > 0);
|
|
|
|
server_handle.abort();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_string_convenience_methods() {
|
|
let (config, _temp_dir) = test_config(23810);
|
|
let api_addr = config.network.api_addr;
|
|
let server = Server::new(config).await.unwrap();
|
|
|
|
let server_handle = tokio::spawn(async move {
|
|
let _ = server.run().await;
|
|
});
|
|
|
|
sleep(Duration::from_millis(500)).await;
|
|
|
|
let mut client = Client::connect(format!("http://{}", api_addr))
|
|
.await
|
|
.unwrap();
|
|
|
|
// Test string methods
|
|
client.put_str("/config/name", "chainfire").await.unwrap();
|
|
|
|
let value = client.get_str("/config/name").await.unwrap();
|
|
assert_eq!(value, Some("chainfire".to_string()));
|
|
|
|
server_handle.abort();
|
|
}
|