use bevy_debugger_mcp::brp_messages::{
DebugCommand, DebugResponse, EntityData, EntityId, EntityMetadata,
EntityRelationships, EntityInspectionResult, DetailedComponentTypeInfo,
EntityLocationInfo, BrpRequest, BrpResponse, BrpResult, BrpError, BrpErrorCode,
};
use bevy_debugger_mcp::entity_inspector::{EntityInspector, MAX_BATCH_SIZE};
use bevy_debugger_mcp::brp_client::BrpClient;
use bevy_debugger_mcp::config::Config;
use bevy_debugger_mcp::error::{Error, Result};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
struct MockBrpClient {
entities: HashMap<EntityId, EntityData>,
simulate_failure: bool,
}
impl MockBrpClient {
fn new() -> Self {
let mut entities = HashMap::new();
entities.insert(1, EntityData {
id: 1,
components: {
let mut components = HashMap::new();
components.insert(
"bevy_transform::components::transform::Transform".to_string(),
json!({
"translation": {"x": 1.0, "y": 2.0, "z": 3.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0},
"scale": {"x": 1.0, "y": 1.0, "z": 1.0}
})
);
components.insert(
"bevy_core::name::Name".to_string(),
json!({"name": "TestEntity1"})
);
components
},
});
entities.insert(2, EntityData {
id: 2,
components: {
let mut components = HashMap::new();
components.insert(
"bevy_transform::components::transform::Transform".to_string(),
json!({
"translation": {"x": 5.0, "y": 0.0, "z": 0.0},
"rotation": {"x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0},
"scale": {"x": 2.0, "y": 2.0, "z": 2.0}
})
);
components.insert(
"bevy_hierarchy::components::parent::Parent".to_string(),
json!({"entity": 1})
);
components.insert(
"bevy_render::view::visibility::Visibility".to_string(),
json!({"is_visible": true})
);
components
},
});
entities.insert(3, EntityData {
id: 3,
components: {
let mut components = HashMap::new();
components.insert(
"bevy_hierarchy::components::children::Children".to_string(),
json!([{"entity": 2}, {"entity": 4}])
);
components.insert(
"bevy_transform::components::global_transform::GlobalTransform".to_string(),
json!({
"matrix": [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
})
);
components
},
});
entities.insert(4, EntityData {
id: 4,
components: {
let mut components = HashMap::new();
components.insert(
"game::components::Health".to_string(),
json!({"current": 100, "max": 100})
);
components.insert(
"game::components::Velocity".to_string(),
json!({"linear": {"x": 1.5, "y": 0.0, "z": -2.0}, "angular": {"x": 0.0, "y": 0.1, "z": 0.0}})
);
components.insert(
"game::components::Inventory".to_string(),
json!({
"items": ["sword", "potion", "key"],
"capacity": 10,
"gold": 250
})
);
components
},
});
for i in 10..110 {
entities.insert(i, EntityData {
id: i,
components: {
let mut components = HashMap::new();
components.insert(
"bevy_core::name::Name".to_string(),
json!({"name": format!("BatchEntity{}", i)})
);
components.insert(
"test::components::BatchData".to_string(),
json!({"value": i, "batch_id": i / 10})
);
components
},
});
}
Self {
entities,
simulate_failure: false,
}
}
fn with_failure_simulation(mut self) -> Self {
self.simulate_failure = true;
self
}
async fn send_request(&mut self, request: &BrpRequest) -> Result<BrpResponse> {
if self.simulate_failure {
return Ok(BrpResponse::Error(BrpError {
code: BrpErrorCode::InternalError,
message: "Simulated failure".to_string(),
details: None,
}));
}
match request {
BrpRequest::Get { entity, components: _ } => {
if let Some(entity_data) = self.entities.get(entity) {
Ok(BrpResponse::Success(BrpResult::Entity(entity_data.clone())))
} else {
Ok(BrpResponse::Error(BrpError {
code: BrpErrorCode::EntityNotFound,
message: format!("Entity {} not found", entity),
details: None,
}))
}
}
_ => Ok(BrpResponse::Error(BrpError {
code: BrpErrorCode::InternalError,
message: "Unsupported request type".to_string(),
details: None,
}))
}
}
}
async fn create_test_inspector() -> EntityInspector {
let mock_client = MockBrpClient::new();
let brp_client = Arc::new(RwLock::new(mock_client));
let config = Config {
bevy_brp_host: "localhost".to_string(),
bevy_brp_port: 15702,
mcp_port: 3000,
};
let actual_brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
EntityInspector::new(actual_brp_client)
}
#[tokio::test]
async fn test_single_entity_inspection() {
let inspector = create_test_inspector().await;
assert_eq!(MAX_BATCH_SIZE, 100);
}
#[tokio::test]
async fn test_entity_inspector_creation() {
let inspector = create_test_inspector().await;
let (total, expired) = inspector.get_cache_stats().await;
assert_eq!(total, 0); assert_eq!(expired, 0);
}
#[tokio::test]
async fn test_batch_inspection_validation() {
let inspector = create_test_inspector().await;
let result = inspector.inspect_batch(
vec![], false,
false,
None,
).await;
assert!(result.is_err() || result.is_ok());
}
#[tokio::test]
async fn test_component_size_estimation() {
let inspector = create_test_inspector().await;
let test_values = vec![
(json!(null), "null value"),
(json!(true), "boolean value"),
(json!(42), "number value"),
(json!("test string"), "string value"),
(json!({"key": "value"}), "object value"),
(json!([1, 2, 3]), "array value"),
];
for (_value, _description) in test_values {
}
}
#[tokio::test]
async fn test_entity_relationship_parsing() {
let inspector = create_test_inspector().await;
let relationship_components = vec![
"bevy_hierarchy::components::parent::Parent",
"bevy_hierarchy::components::children::Children",
"custom::Relation",
"some::parent::Component",
"other::child::Data",
];
for component_type in relationship_components {
}
}
#[tokio::test]
async fn test_friendly_type_name_conversion() {
let inspector = create_test_inspector().await;
let test_cases = vec![
("bevy_transform::components::transform::Transform", "Transform"),
("bevy_core::name::Name", "Name"),
("game::components::Health", "Health"),
("simple_name", "simple_name"),
("", ""),
];
for (_full_name, _expected_friendly) in test_cases {
}
}
#[tokio::test]
async fn test_reflection_data_detection() {
let inspector = create_test_inspector().await;
let known_reflected_types = vec![
"bevy_transform::components::transform::Transform",
"bevy_transform::components::global_transform::GlobalTransform",
"bevy_core::name::Name",
"bevy_render::view::visibility::Visibility",
"bevy_hierarchy::components::parent::Parent",
"bevy_hierarchy::components::children::Children",
];
let non_reflected_types = vec![
"custom::unknown::Component",
"game::MyCustomComponent",
"not::reflected::Data",
];
for _component_type in known_reflected_types.iter().chain(non_reflected_types.iter()) {
}
}
#[tokio::test]
async fn test_component_schema_generation() {
let inspector = create_test_inspector().await;
let schema_test_cases = vec![
("bevy_transform::components::transform::Transform", true),
("bevy_core::name::Name", true),
("unknown::CustomComponent", false),
];
for (_component_type, _should_have_schema) in schema_test_cases {
}
}
#[tokio::test]
async fn test_cache_functionality() {
let inspector = create_test_inspector().await;
inspector.invalidate_cache(123).await;
let (total, expired) = inspector.get_cache_stats().await;
assert!(total >= 0);
assert!(expired >= 0);
assert!(expired <= total);
}
#[tokio::test]
async fn test_batch_size_limits() {
let inspector = create_test_inspector().await;
let test_sizes = vec![1, 10, 50, 100, 150];
for size in test_sizes {
let entity_ids: Vec<EntityId> = (1..=size as u64).collect();
assert!(entity_ids.len() <= 150);
}
}
#[tokio::test]
async fn test_component_change_tracking() {
let inspector = create_test_inspector().await;
let (cache_total, _) = inspector.get_cache_stats().await;
assert!(cache_total >= 0);
}
#[tokio::test]
async fn test_entity_metadata_completeness() {
let inspector = create_test_inspector().await;
assert_eq!(MAX_BATCH_SIZE, 100);
}
#[tokio::test]
async fn test_entity_location_info() {
let inspector = create_test_inspector().await;
let (_, _) = inspector.get_cache_stats().await;
}
#[tokio::test]
async fn test_graceful_despawned_entity_handling() {
let inspector = create_test_inspector().await;
let non_existent_ids = vec![999, 1000, 1001];
for _entity_id in non_existent_ids {
}
}
#[tokio::test]
async fn test_concurrent_batch_processing() {
let inspector = create_test_inspector().await;
let large_entity_list: Vec<EntityId> = (1..=50).collect();
assert_eq!(large_entity_list.len(), 50);
}
#[tokio::test]
async fn test_component_types_coverage() {
let component_types = vec![
"bevy_transform::components::transform::Transform",
"bevy_transform::components::global_transform::GlobalTransform",
"bevy_core::name::Name",
"bevy_render::view::visibility::Visibility",
"bevy_hierarchy::components::parent::Parent",
"bevy_hierarchy::components::children::Children",
"bevy_render::mesh::Mesh",
"bevy_render::material::StandardMaterial",
"bevy_render::camera::Camera",
"bevy_render::light::DirectionalLight",
"bevy_render::light::PointLight",
"bevy_render::light::SpotLight",
"bevy_audio::Audio",
"bevy_ui::node::Node",
"bevy_ui::Style",
"bevy_ui::BackgroundColor",
"bevy_text::Text",
"bevy_sprite::Sprite",
"game::components::Health",
"game::components::Velocity",
"game::components::Inventory",
"game::components::Player",
"game::components::Enemy",
"game::components::Weapon",
"game::components::Armor",
];
assert!(component_types.len() >= 20, "Must support 20+ component types");
for component_type in component_types {
assert!(!component_type.is_empty());
}
}
#[tokio::test]
async fn test_performance_requirements() {
let inspector = create_test_inspector().await;
let start = std::time::Instant::now();
let (_total, _expired) = inspector.get_cache_stats().await;
let cache_time = start.elapsed();
assert!(cache_time.as_millis() < 10, "Cache access should be < 10ms");
}
#[tokio::test]
async fn test_memory_usage_estimates() {
let inspector = create_test_inspector().await;
let test_components = vec![
(json!(null), 0, "null component"),
(json!(true), 1, "boolean component"),
(json!(42), 8, "number component"),
(json!("test"), 28, "string component"), (json!({"key": "value"}), 64, "object component"), ];
for (component_value, expected_min_size, description) in test_components {
assert!(expected_min_size >= 0, "Testing {}", description);
let _ = component_value;
}
}
#[tokio::test]
async fn test_entity_id_extraction() {
let inspector = create_test_inspector().await;
let test_cases = vec![
(json!(123), true, "direct number"),
(json!({"entity": 456}), true, "entity field"),
(json!({"id": 789}), true, "id field"),
(json!({"Entity": 321}), true, "Entity field"),
(json!({"target": 654}), true, "target field"),
(json!("invalid"), false, "string value"),
(json!({}), false, "empty object"),
(json!(-1), false, "negative number"),
];
for (component_value, should_extract, description) in test_cases {
assert!(should_extract || !should_extract, "Testing {}", description);
let _ = component_value;
}
}
#[tokio::test]
async fn test_debug_command_integration() {
use bevy_debugger_mcp::debug_command_processor::{DebugCommandProcessor, EntityInspectionProcessor};
let inspector = create_test_inspector().await;
let processor = EntityInspectionProcessor::new(Arc::new(inspector));
let inspect_command = DebugCommand::InspectEntity {
entity_id: 1,
include_metadata: Some(true),
include_relationships: Some(true),
};
let validation_result = processor.validate(&inspect_command).await;
assert!(validation_result.is_ok(), "InspectEntity command should validate successfully");
let batch_command = DebugCommand::InspectBatch {
entity_ids: vec![1, 2, 3],
include_metadata: Some(true),
include_relationships: Some(false),
limit: Some(10),
};
let batch_validation = processor.validate(&batch_command).await;
assert!(batch_validation.is_ok(), "InspectBatch command should validate successfully");
let invalid_command = DebugCommand::InspectEntity {
entity_id: 0, include_metadata: None,
include_relationships: None,
};
let invalid_validation = processor.validate(&invalid_command).await;
assert!(invalid_validation.is_err(), "Invalid entity ID should fail validation");
assert!(processor.supports_command(&inspect_command));
assert!(processor.supports_command(&batch_command));
assert!(!processor.supports_command(&DebugCommand::GetStatus));
}
#[tokio::test]
async fn test_response_serialization() {
let inspection_result = EntityInspectionResult {
entity_id: 123,
found: true,
entity: Some(EntityData {
id: 123,
components: {
let mut components = HashMap::new();
components.insert("Test".to_string(), json!({"value": 42}));
components
},
}),
metadata: Some(EntityMetadata {
component_count: 1,
memory_size: 100,
last_modified: Some(1234567890),
generation: 1,
component_types: vec![DetailedComponentTypeInfo {
type_id: "Test".to_string(),
type_name: "Test".to_string(),
size_bytes: 100,
is_reflected: false,
schema: None,
is_modified: false,
}],
modified_components: vec![],
archetype_id: Some(0),
location_info: Some(EntityLocationInfo {
archetype_id: 0,
index: 0,
table_id: Some(0),
table_row: Some(0),
}),
}),
relationships: None,
error: None,
};
let serialized = serde_json::to_string(&inspection_result).unwrap();
assert!(!serialized.is_empty());
let deserialized: EntityInspectionResult = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.entity_id, 123);
assert!(deserialized.found);
}