243 lines
7.5 KiB
Rust
243 lines
7.5 KiB
Rust
use std::fs;
|
|
use serde_json;
|
|
|
|
// Since this is an integration test, we need to include the modules directly
|
|
// or create a simple test that doesn't rely on the full crate structure
|
|
|
|
// Let's create a simple JSON parsing test using serde_json directly
|
|
use serde::{Deserialize, Serialize};
|
|
use rust_decimal::Decimal;
|
|
use chrono::{DateTime, Utc};
|
|
// Copy the essential structs for testing
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UserPersistentData {
|
|
pub user_email: String,
|
|
#[serde(default)]
|
|
pub wallet_balance: Decimal,
|
|
#[serde(default)]
|
|
pub nodes: Vec<FarmNode>,
|
|
#[serde(default)]
|
|
pub slas: Vec<ServiceLevelAgreement>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FarmNode {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub location: String,
|
|
#[serde(deserialize_with = "deserialize_node_status")]
|
|
pub status: NodeStatus,
|
|
pub capacity: NodeCapacity,
|
|
pub used_capacity: NodeCapacity,
|
|
pub uptime_percentage: f32,
|
|
#[serde(deserialize_with = "deserialize_earnings", alias = "earnings_today_usd")]
|
|
pub earnings_today: Decimal,
|
|
#[serde(deserialize_with = "deserialize_datetime")]
|
|
pub last_seen: DateTime<Utc>,
|
|
pub health_score: f32,
|
|
pub region: String,
|
|
pub node_type: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NodeCapacity {
|
|
pub cpu_cores: i32,
|
|
pub memory_gb: i32,
|
|
pub storage_gb: i32,
|
|
pub bandwidth_mbps: i32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum NodeStatus {
|
|
Online,
|
|
Offline,
|
|
Maintenance,
|
|
Error,
|
|
Standby,
|
|
}
|
|
|
|
impl std::fmt::Display for NodeStatus {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
NodeStatus::Online => write!(f, "Online"),
|
|
NodeStatus::Offline => write!(f, "Offline"),
|
|
NodeStatus::Maintenance => write!(f, "Maintenance"),
|
|
NodeStatus::Error => write!(f, "Error"),
|
|
NodeStatus::Standby => write!(f, "Standby"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ServiceLevelAgreement {
|
|
pub id: String,
|
|
#[serde(alias = "client_name")]
|
|
pub name: String,
|
|
#[serde(alias = "service_type")]
|
|
pub description: String,
|
|
#[serde(default)]
|
|
pub service_id: Option<String>,
|
|
pub response_time_hours: i32,
|
|
pub resolution_time_hours: i32,
|
|
#[serde(alias = "uptime_guarantee")]
|
|
pub availability_percentage: f32,
|
|
#[serde(default = "default_support_hours")]
|
|
pub support_hours: String,
|
|
#[serde(default = "default_escalation_procedure")]
|
|
pub escalation_procedure: String,
|
|
#[serde(default)]
|
|
pub penalties: Vec<SLAPenalty>,
|
|
#[serde(alias = "created_date")]
|
|
pub created_at: String,
|
|
pub status: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SLAPenalty {
|
|
pub breach_type: String,
|
|
pub threshold: String,
|
|
pub penalty_amount: i32,
|
|
pub penalty_type: String,
|
|
}
|
|
|
|
fn default_support_hours() -> String {
|
|
"Business Hours".to_string()
|
|
}
|
|
|
|
fn default_escalation_procedure() -> String {
|
|
"Standard escalation procedure".to_string()
|
|
}
|
|
|
|
// Custom deserializers
|
|
fn deserialize_node_status<'de, D>(deserializer: D) -> Result<NodeStatus, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let s = String::deserialize(deserializer)?;
|
|
match s.as_str() {
|
|
"Online" => Ok(NodeStatus::Online),
|
|
"Offline" => Ok(NodeStatus::Offline),
|
|
"Maintenance" => Ok(NodeStatus::Maintenance),
|
|
"Error" => Ok(NodeStatus::Error),
|
|
"Standby" => Ok(NodeStatus::Standby),
|
|
_ => Ok(NodeStatus::Online),
|
|
}
|
|
}
|
|
|
|
fn deserialize_earnings<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
use serde::de::Error;
|
|
use std::str::FromStr;
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
enum EarningsValue {
|
|
Number(f64),
|
|
String(String),
|
|
}
|
|
|
|
let value = EarningsValue::deserialize(deserializer)?;
|
|
match value {
|
|
EarningsValue::Number(n) => {
|
|
Decimal::from_str(&n.to_string()).map_err(D::Error::custom)
|
|
}
|
|
EarningsValue::String(s) => {
|
|
Decimal::from_str(&s).map_err(D::Error::custom)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn deserialize_datetime<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
use serde::de::Error;
|
|
let s = String::deserialize(deserializer)?;
|
|
DateTime::parse_from_rfc3339(&s)
|
|
.map(|dt| dt.with_timezone(&Utc))
|
|
.map_err(D::Error::custom)
|
|
}
|
|
|
|
#[test]
|
|
fn test_user1_json_parsing() {
|
|
println!("Testing JSON parsing fixes for user1...");
|
|
|
|
// Test parsing user1 data
|
|
let user1_path = "./user_data/user1_at_example_com.json";
|
|
|
|
let json_content = fs::read_to_string(user1_path)
|
|
.expect("Should be able to read user1 JSON file");
|
|
|
|
println!("✓ Successfully read user1 JSON file");
|
|
|
|
let user_data: UserPersistentData = serde_json::from_str(&json_content)
|
|
.expect("Should be able to parse user1 JSON without errors");
|
|
|
|
println!("✅ SUCCESS: JSON parsing completed without errors!");
|
|
println!(" - User email: {}", user_data.user_email);
|
|
println!(" - Number of nodes: {}", user_data.nodes.len());
|
|
println!(" - Number of SLAs: {}", user_data.slas.len());
|
|
|
|
// Verify we have the expected data
|
|
assert_eq!(user_data.user_email, "user1@example.com");
|
|
assert_eq!(user_data.nodes.len(), 2, "Should have 2 farm nodes");
|
|
assert!(user_data.slas.len() >= 1, "Should have at least 1 SLA");
|
|
let has_expected_sla = user_data
|
|
.slas
|
|
.iter()
|
|
.any(|s| s.id == "SLA-1001" && s.name == "RetailCorp" && s.description == "E-commerce Development");
|
|
assert!(
|
|
has_expected_sla,
|
|
"Expected SLA 'SLA-1001' (RetailCorp - E-commerce Development) not found"
|
|
);
|
|
|
|
// Check first node details
|
|
let first_node = &user_data.nodes[0];
|
|
assert_eq!(first_node.id, "TF-US-001");
|
|
assert_eq!(first_node.name, "New York Data Center");
|
|
println!(" - First node: {} ({})", first_node.name, first_node.status);
|
|
println!(" - Earnings today: {}", first_node.earnings_today);
|
|
|
|
// Check expected SLA details (by ID)
|
|
let expected_sla = user_data
|
|
.slas
|
|
.iter()
|
|
.find(|s| s.id == "SLA-1001")
|
|
.expect("Expected SLA 'SLA-1001' to be present");
|
|
assert_eq!(expected_sla.name, "RetailCorp");
|
|
assert_eq!(expected_sla.description, "E-commerce Development");
|
|
println!(" - Expected SLA: {} ({})", expected_sla.name, expected_sla.status);
|
|
|
|
println!("\n🎉 All assertions passed! The JSON parsing fixes are working correctly.");
|
|
}
|
|
|
|
#[test]
|
|
fn test_all_user_json_files() {
|
|
println!("Testing JSON parsing for all user files...");
|
|
|
|
let user_files = [
|
|
"user1_at_example_com.json",
|
|
"user2_at_example_com.json",
|
|
"user3_at_example_com.json",
|
|
"user4_at_example_com.json",
|
|
"user5_at_example_com.json",
|
|
"user6_at_example_com.json",
|
|
];
|
|
|
|
for file in &user_files {
|
|
let path = format!("./user_data/{}", file);
|
|
println!("Testing {}", file);
|
|
|
|
let json_content = fs::read_to_string(&path)
|
|
.unwrap_or_else(|_| panic!("Should be able to read {}", file));
|
|
|
|
let _user_data: UserPersistentData = serde_json::from_str(&json_content)
|
|
.unwrap_or_else(|e| panic!("Should be able to parse {} without errors: {}", file, e));
|
|
|
|
println!("✓ {} parsed successfully", file);
|
|
}
|
|
|
|
println!("🎉 All user JSON files parsed successfully!");
|
|
} |