From 9c30162967dbe057b29904643db05a4848e7be4c Mon Sep 17 00:00:00 2001 From: frankie-powers Date: Thu, 2 Oct 2025 23:27:22 +0100 Subject: [PATCH 1/4] lib & new modules --- .../distributed-energy-resource-manager/src/lib.rs | 2 +- .../distributed-energy-resource-manager/src/tests/mod.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 soroban/contracts/distributed-energy-resource-manager/src/tests/mod.rs diff --git a/soroban/contracts/distributed-energy-resource-manager/src/lib.rs b/soroban/contracts/distributed-energy-resource-manager/src/lib.rs index 519de8b..37d5b22 100644 --- a/soroban/contracts/distributed-energy-resource-manager/src/lib.rs +++ b/soroban/contracts/distributed-energy-resource-manager/src/lib.rs @@ -9,7 +9,7 @@ mod optimization; mod utils; #[cfg(test)] -mod test; +mod tests; use resource::*; use optimization::*; diff --git a/soroban/contracts/distributed-energy-resource-manager/src/tests/mod.rs b/soroban/contracts/distributed-energy-resource-manager/src/tests/mod.rs new file mode 100644 index 0000000..826abbb --- /dev/null +++ b/soroban/contracts/distributed-energy-resource-manager/src/tests/mod.rs @@ -0,0 +1,6 @@ +#![cfg(test)] + +pub mod optimization; +pub mod registration; +pub mod status; +pub mod utils; From 53b69b6e37eeca84f71d55320c2ee004d5c53a45 Mon Sep 17 00:00:00 2001 From: frankie-powers Date: Thu, 2 Oct 2025 23:27:54 +0100 Subject: [PATCH 2/4] utils.rs --- .../src/tests/utils.rs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 soroban/contracts/distributed-energy-resource-manager/src/tests/utils.rs diff --git a/soroban/contracts/distributed-energy-resource-manager/src/tests/utils.rs b/soroban/contracts/distributed-energy-resource-manager/src/tests/utils.rs new file mode 100644 index 0000000..d524db7 --- /dev/null +++ b/soroban/contracts/distributed-energy-resource-manager/src/tests/utils.rs @@ -0,0 +1,108 @@ +#![cfg(test)] + +use soroban_sdk::{ + testutils::{Address as _}, + Address, Env, String, +}; + +use crate::{ + DistributedEnergyResourceManager, DistributedEnergyResourceManagerClient, ResourceType, + DERStatus +}; + +// ============ TEST CONTEXT ============ + +pub struct TestContext { + pub env: Env, + pub client: DistributedEnergyResourceManagerClient<'static>, + pub admin: Address, +} + +// ============ SETUP FUNCTIONS ============ + +/// Creates a basic test environment with initialized contract +#[allow(dead_code)] +pub fn setup_test_env() -> TestContext { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + let admin = Address::generate(&env); + + client.initialize(&admin); + + TestContext { + env, + client, + admin, + } +} + +/// Creates test environment with admin and grid operator +#[allow(dead_code)] +pub fn setup_with_operator() -> (TestContext, Address) { + let ctx = setup_test_env(); + let operator = Address::generate(&ctx.env); + let operator_name = String::from_str(&ctx.env, "Grid Operator"); + + ctx.client.add_grid_operator(&ctx.admin, &operator, &operator_name, &5); + + (ctx, operator) +} + +// ============ HELPER FUNCTIONS ============ + +/// Registers a basic DER for testing +#[allow(dead_code)] +pub fn register_test_der( + ctx: &TestContext, + owner: &Address, + der_id: &str, + resource_type: ResourceType, + capacity: u32, +) -> String { + let der_id_str = String::from_str(&ctx.env, der_id); + let location = String::from_str(&ctx.env, "Test Location"); + + ctx.client.register_der(owner, &der_id_str, &resource_type, &capacity, &location); + der_id_str +} + +/// Registers multiple DERs for testing (up to 10) +#[allow(dead_code)] +pub fn register_multiple_ders( + ctx: &TestContext, + owner: &Address, + count: u32, +) { + let location = String::from_str(&ctx.env, "Test Location"); + let der_ids = ["DER_00", "DER_01", "DER_02", "DER_03", "DER_04", + "DER_05", "DER_06", "DER_07", "DER_08", "DER_09"]; + + for i in 0..(count.min(10) as usize) { + let der_id = String::from_str(&ctx.env, der_ids[i]); + ctx.client.register_der(owner, &der_id, &ResourceType::Solar, &100, &location); + } +} + +// ============ ASSERTION HELPERS ============ + +/// Asserts DER has expected status +#[allow(dead_code)] +pub fn assert_der_status(ctx: &TestContext, der_id: &String, expected: DERStatus) { + let der_info = ctx.client.get_der_info(der_id); + assert_eq!(der_info.status, expected, "DER status should match expected"); +} + +/// Asserts stats match expected values +#[allow(dead_code)] +pub fn assert_stats( + ctx: &TestContext, + total: u32, + online: u32, + capacity: u32, +) { + let stats = ctx.client.get_stats(); + assert_eq!(stats.total_ders, total, "Total DERs mismatch"); + assert_eq!(stats.online_ders, online, "Online DERs mismatch"); + assert_eq!(stats.total_capacity, capacity, "Total capacity mismatch"); +} From b7478f378ea6c063190a11417c13bd868c56b519 Mon Sep 17 00:00:00 2001 From: frankie-powers Date: Thu, 2 Oct 2025 23:33:35 +0100 Subject: [PATCH 3/4] optimization tests --- .../src/tests/optimization.rs | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 soroban/contracts/distributed-energy-resource-manager/src/tests/optimization.rs diff --git a/soroban/contracts/distributed-energy-resource-manager/src/tests/optimization.rs b/soroban/contracts/distributed-energy-resource-manager/src/tests/optimization.rs new file mode 100644 index 0000000..0ab05d6 --- /dev/null +++ b/soroban/contracts/distributed-energy-resource-manager/src/tests/optimization.rs @@ -0,0 +1,283 @@ +#![cfg(test)] + +use soroban_sdk::{ + testutils::{Address as _}, + Address, Env, String, +}; + +use crate::{ + DistributedEnergyResourceManager, DistributedEnergyResourceManagerClient, ResourceType, + DERStatus +}; + +// ============ RESOURCE OPTIMIZATION TESTS ============ + +#[test] +fn test_optimize_resources() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + // Register some DERs + let solar_der = String::from_str(&env, "SOLAR_001"); + let battery_der = String::from_str(&env, "BATTERY_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &solar_der, &ResourceType::Solar, &1000, &location); + client.register_der(&der_owner, &battery_der, &ResourceType::Battery, &500, &location); + + // Optimize resources + let schedules = client.optimize_resources(&operator); + + // Verify optimization schedules were created + assert_eq!(schedules.len(), 2); + + // Check that we can retrieve the schedules + let retrieved_schedules = client.get_optimization_schedules(); + assert_eq!(retrieved_schedules.len(), 2); +} + +#[test] +fn test_emergency_allocation() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + // Register a DER + let der_id = String::from_str(&env, "BATTERY_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &der_id, &ResourceType::Battery, &500, &location); + + // Emergency allocation + let result = client.emergency_allocation( + &operator, + &der_id, + &300, // 300 kW required + &3600 // 1 hour duration + ); + + assert!(result); + + // Verify DER status changed to emergency + let der_info = client.get_der_info(&der_id); + assert_eq!(der_info.status, DERStatus::Emergency); +} + +// ============ EDGE CASE TESTS ============ + +#[test] +#[should_panic(expected = "DER not found")] +fn test_emergency_allocation_non_existent_der() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + let der_id = String::from_str(&env, "NON_EXISTENT_DER"); + + // Attempt emergency allocation on non-existent DER - should panic + client.emergency_allocation(&operator, &der_id, &300, &3600); +} + +#[test] +#[should_panic(expected = "DER is not available for emergency allocation")] +fn test_emergency_allocation_offline_der() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + // Register a DER + let der_id = String::from_str(&env, "BATTERY_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &der_id, &ResourceType::Battery, &500, &location); + + // Set DER to offline + client.update_status(&der_owner, &der_id, &DERStatus::Offline); + + // Attempt emergency allocation on offline DER - should panic + client.emergency_allocation(&operator, &der_id, &300, &3600); +} + +#[test] +#[should_panic(expected = "Required power exceeds DER capacity")] +fn test_emergency_allocation_exceeds_capacity() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + // Register a DER with 500 capacity + let der_id = String::from_str(&env, "BATTERY_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &der_id, &ResourceType::Battery, &500, &location); + + // Attempt to allocate more than capacity - should panic + client.emergency_allocation(&operator, &der_id, &600, &3600); +} + +#[test] +fn test_optimize_with_mixed_status_ders() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + let location = String::from_str(&env, "Test Location"); + + // Register DERs with different statuses + let solar_der = String::from_str(&env, "SOLAR_001"); + let wind_der = String::from_str(&env, "WIND_001"); + let battery_der = String::from_str(&env, "BATTERY_001"); + + client.register_der(&der_owner, &solar_der, &ResourceType::Solar, &1000, &location); + client.register_der(&der_owner, &wind_der, &ResourceType::Wind, &500, &location); + client.register_der(&der_owner, &battery_der, &ResourceType::Battery, &200, &location); + + // Set one DER to offline + client.update_status(&der_owner, &battery_der, &DERStatus::Offline); + + // Optimize resources - should only include online/optimized DERs + let schedules = client.optimize_resources(&operator); + + // Only solar and wind should be in optimization (battery is offline) + assert_eq!(schedules.len(), 2); +} + +#[test] +fn test_optimize_empty_der_list() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + // Optimize with no DERs registered + let schedules = client.optimize_resources(&operator); + + // Should return empty schedule + assert_eq!(schedules.len(), 0); +} + +#[test] +fn test_optimization_all_resource_types() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + // Add grid operator + let operator_name = String::from_str(&env, "Grid Operator 1"); + client.add_grid_operator(&admin, &operator, &operator_name, &5); + + let location = String::from_str(&env, "Test Location"); + + // Register all resource types + let resource_types = [ + (ResourceType::Solar, "SOLAR_001", 1000), + (ResourceType::Wind, "WIND_001", 500), + (ResourceType::Battery, "BATTERY_001", 200), + (ResourceType::Hydro, "HYDRO_001", 800), + (ResourceType::Geothermal, "GEO_001", 600), + (ResourceType::FuelCell, "FUEL_001", 100), + ]; + + for (resource_type, der_id, capacity) in resource_types { + let der_id_str = String::from_str(&env, der_id); + client.register_der(&der_owner, &der_id_str, &resource_type, &capacity, &location); + } + + // Optimize all resources + let schedules = client.optimize_resources(&operator); + + // Should create schedules for all 6 resource types + assert_eq!(schedules.len(), 6); + + // Verify schedule priorities are set (different for each type) + // Battery should have highest priority (9), Geothermal lowest (4) + let mut has_battery_priority = false; + let mut has_geothermal_priority = false; + + for schedule in schedules.iter() { + if schedule.priority == 9 { + has_battery_priority = true; + } + if schedule.priority == 4 { + has_geothermal_priority = true; + } + } + + assert!(has_battery_priority, "Battery priority (9) should be present"); + assert!(has_geothermal_priority, "Geothermal priority (4) should be present"); +} + From 2b24587373e3ccf2d5ffba9dfe8b1ce3ce7f1e5d Mon Sep 17 00:00:00 2001 From: frankie-powers Date: Thu, 2 Oct 2025 23:33:52 +0100 Subject: [PATCH 4/4] registration and status tests --- .../src/tests/registration.rs | 250 ++++++++++++++++++ .../src/tests/status.rs | 188 +++++++++++++ 2 files changed, 438 insertions(+) create mode 100644 soroban/contracts/distributed-energy-resource-manager/src/tests/registration.rs create mode 100644 soroban/contracts/distributed-energy-resource-manager/src/tests/status.rs diff --git a/soroban/contracts/distributed-energy-resource-manager/src/tests/registration.rs b/soroban/contracts/distributed-energy-resource-manager/src/tests/registration.rs new file mode 100644 index 0000000..0649d6d --- /dev/null +++ b/soroban/contracts/distributed-energy-resource-manager/src/tests/registration.rs @@ -0,0 +1,250 @@ +#![cfg(test)] + +use soroban_sdk::{ + testutils::{Address as _}, + Address, Env, String, +}; + +use crate::{ + DistributedEnergyResourceManager, DistributedEnergyResourceManagerClient, ResourceType, + DERStatus +}; + +// ============ INITIALIZATION TESTS ============ + +#[test] +fn test_initialize() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + + client.initialize(&admin); + + // Verify contract is initialized + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 0); + assert_eq!(stats.online_ders, 0); + assert_eq!(stats.total_capacity, 0); +} + +// ============ DER REGISTRATION TESTS ============ + +#[test] +fn test_register_der() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + // Initialize contract + client.initialize(&admin); + + // Register a solar DER + let der_id = String::from_str(&env, "SOLAR_001"); + let location = String::from_str(&env, "San Francisco, CA"); + + let result = client.register_der( + &der_owner, + &der_id, + &ResourceType::Solar, + &1000, // 1 MW + &location + ); + + assert!(result); + + // Verify DER was registered + let der_info = client.get_der_info(&der_id); + assert_eq!(der_info.owner, der_owner); + assert_eq!(der_info.resource_type, ResourceType::Solar); + assert_eq!(der_info.capacity, 1000); + assert_eq!(der_info.status, DERStatus::Online); + assert_eq!(der_info.location, location); +} + +#[test] +fn test_register_multiple_ders() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + // Register multiple DERs + let solar_der = String::from_str(&env, "SOLAR_001"); + let wind_der = String::from_str(&env, "WIND_001"); + let battery_der = String::from_str(&env, "BATTERY_001"); + + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &solar_der, &ResourceType::Solar, &1000, &location); + client.register_der(&der_owner, &wind_der, &ResourceType::Wind, &500, &location); + client.register_der(&der_owner, &battery_der, &ResourceType::Battery, &200, &location); + + // Verify all DERs are registered + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 3); + assert_eq!(stats.online_ders, 3); + assert_eq!(stats.total_capacity, 1700); + + // Verify owner can see all their DERs + let owner_ders = client.get_owner_ders(&der_owner); + assert_eq!(owner_ders.len(), 3); +} + +#[test] +fn test_different_resource_types() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let location = String::from_str(&env, "Test Location"); + + // Test all resource types + let resource_types = [ + (ResourceType::Solar, "SOLAR_001", 1000), + (ResourceType::Wind, "WIND_001", 500), + (ResourceType::Battery, "BATTERY_001", 200), + (ResourceType::Hydro, "HYDRO_001", 800), + (ResourceType::Geothermal, "GEO_001", 600), + (ResourceType::FuelCell, "FUEL_001", 100), + ]; + + for (resource_type, der_id, capacity) in resource_types { + let der_id_str = String::from_str(&env, der_id); + + let result = client.register_der( + &der_owner, + &der_id_str, + &resource_type, + &capacity, + &location + ); + assert!(result); + + // Verify DER was registered correctly + let der_info = client.get_der_info(&der_id_str); + assert_eq!(der_info.resource_type, resource_type); + assert_eq!(der_info.capacity, capacity); + } + + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 6); + assert_eq!(stats.total_capacity, 3200); +} + +// ============ GRID OPERATOR TESTS ============ + +#[test] +fn test_add_grid_operator() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let operator = Address::generate(&env); + + client.initialize(&admin); + + let operator_name = String::from_str(&env, "Grid Operator 1"); + + let result = client.add_grid_operator( + &admin, + &operator, + &operator_name, + &5 // Authority level 5 + ); + + assert!(result); +} + +// ============ EDGE CASE TESTS ============ + +#[test] +#[should_panic(expected = "DER already exists")] +fn test_duplicate_der_registration() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let der_id = String::from_str(&env, "SOLAR_001"); + let location = String::from_str(&env, "Test Location"); + + // Register DER first time + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &1000, &location); + + // Attempt to register same DER again - should panic + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &1000, &location); +} + +#[test] +#[should_panic(expected = "Contract already initialized")] +fn test_duplicate_initialization() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + + // Initialize first time + client.initialize(&admin); + + // Attempt to initialize again - should panic + client.initialize(&admin); +} + +#[test] +fn test_high_volume_der_registrations() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let location = String::from_str(&env, "Test Location"); + + // Register 50 DERs to test scalability + let der_ids = [ + "DER_000", "DER_001", "DER_002", "DER_003", "DER_004", "DER_005", "DER_006", "DER_007", "DER_008", "DER_009", + "DER_010", "DER_011", "DER_012", "DER_013", "DER_014", "DER_015", "DER_016", "DER_017", "DER_018", "DER_019", + "DER_020", "DER_021", "DER_022", "DER_023", "DER_024", "DER_025", "DER_026", "DER_027", "DER_028", "DER_029", + "DER_030", "DER_031", "DER_032", "DER_033", "DER_034", "DER_035", "DER_036", "DER_037", "DER_038", "DER_039", + "DER_040", "DER_041", "DER_042", "DER_043", "DER_044", "DER_045", "DER_046", "DER_047", "DER_048", "DER_049", + ]; + + for der_id_str in der_ids { + let der_id = String::from_str(&env, der_id_str); + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &100, &location); + } + + // Verify all DERs registered + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 50); + assert_eq!(stats.online_ders, 50); + assert_eq!(stats.total_capacity, 5000); + + // Verify owner can see all their DERs + let owner_ders = client.get_owner_ders(&der_owner); + assert_eq!(owner_ders.len(), 50); +} + diff --git a/soroban/contracts/distributed-energy-resource-manager/src/tests/status.rs b/soroban/contracts/distributed-energy-resource-manager/src/tests/status.rs new file mode 100644 index 0000000..34726dd --- /dev/null +++ b/soroban/contracts/distributed-energy-resource-manager/src/tests/status.rs @@ -0,0 +1,188 @@ +#![cfg(test)] + +use soroban_sdk::{ + testutils::{Address as _}, + Address, Env, String, +}; + +use crate::{ + DistributedEnergyResourceManager, DistributedEnergyResourceManagerClient, ResourceType, + DERStatus +}; + +// ============ STATUS UPDATE TESTS ============ + +#[test] +fn test_update_status() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let der_id = String::from_str(&env, "SOLAR_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &1000, &location); + + // Update status to maintenance + let result = client.update_status(&der_owner, &der_id, &DERStatus::Maintenance); + assert!(result); + + // Verify status was updated + let der_info = client.get_der_info(&der_id); + assert_eq!(der_info.status, DERStatus::Maintenance); +} + +#[test] +fn test_get_stats() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + // Register multiple DERs with different statuses + let solar_der = String::from_str(&env, "SOLAR_001"); + let wind_der = String::from_str(&env, "WIND_001"); + let battery_der = String::from_str(&env, "BATTERY_001"); + let location = String::from_str(&env, "Test Location"); + + // Register solar DER + client.register_der(&der_owner, &solar_der, &ResourceType::Solar, &1000, &location); + + // Register wind DER + client.register_der(&der_owner, &wind_der, &ResourceType::Wind, &500, &location); + + // Register battery DER + client.register_der(&der_owner, &battery_der, &ResourceType::Battery, &200, &location); + + // Put one DER in maintenance + client.update_status(&der_owner, &battery_der, &DERStatus::Maintenance); + + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 3); + assert_eq!(stats.online_ders, 2); // Only solar and wind are online + assert_eq!(stats.total_capacity, 1700); + assert_eq!(stats.utilization_rate, 66); // 2 out of 3 DERs online +} + +// ============ EDGE CASE TESTS ============ + +#[test] +#[should_panic(expected = "DER not found")] +fn test_update_status_non_existent_der() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let der_id = String::from_str(&env, "NON_EXISTENT_DER"); + + // Attempt to update status of non-existent DER - should panic + client.update_status(&der_owner, &der_id, &DERStatus::Maintenance); +} + +#[test] +#[should_panic(expected = "DER not found")] +fn test_get_info_non_existent_der() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + + client.initialize(&admin); + + let der_id = String::from_str(&env, "NON_EXISTENT_DER"); + + // Attempt to get info of non-existent DER - should panic + client.get_der_info(&der_id); +} + +#[test] +fn test_status_transitions_all_states() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let der_id = String::from_str(&env, "SOLAR_001"); + let location = String::from_str(&env, "Test Location"); + + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &1000, &location); + + // Test all status transitions + let statuses = [ + DERStatus::Online, + DERStatus::Maintenance, + DERStatus::Offline, + DERStatus::Online, + DERStatus::Optimized, + ]; + + for status in statuses { + client.update_status(&der_owner, &der_id, &status); + let der_info = client.get_der_info(&der_id); + assert_eq!(der_info.status, status); + } +} + +#[test] +fn test_multiple_ders_different_statuses() { + let env = Env::default(); + let contract_id = env.register(DistributedEnergyResourceManager, ()); + let client = DistributedEnergyResourceManagerClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let der_owner = Address::generate(&env); + + client.initialize(&admin); + + let location = String::from_str(&env, "Test Location"); + + // Register 10 DERs and set different statuses + let der_configs = [ + ("DER_0", DERStatus::Online), + ("DER_1", DERStatus::Maintenance), + ("DER_2", DERStatus::Offline), + ("DER_3", DERStatus::Optimized), + ("DER_4", DERStatus::Online), + ("DER_5", DERStatus::Maintenance), + ("DER_6", DERStatus::Offline), + ("DER_7", DERStatus::Optimized), + ("DER_8", DERStatus::Online), + ("DER_9", DERStatus::Maintenance), + ]; + + for (der_id_str, status) in der_configs { + let der_id = String::from_str(&env, der_id_str); + client.register_der(&der_owner, &der_id, &ResourceType::Solar, &100, &location); + + if status != DERStatus::Online { + client.update_status(&der_owner, &der_id, &status); + } + } + + // Verify stats reflect mixed statuses + let stats = client.get_stats(); + assert_eq!(stats.total_ders, 10); + // Online (3) + Optimized (2) = 5 + assert_eq!(stats.online_ders, 5); + assert_eq!(stats.total_capacity, 1000); +} +