cougr-core 1.0.0

Cougr - A Soroban-compatible ECS framework for on-chain gaming on Stellar
Documentation
use super::SimpleWorld;
use crate::component::{ComponentStorage, Position};
use soroban_sdk::{symbol_short, Bytes, Env};

#[test]
fn test_simple_world_creation() {
    let env = Env::default();
    let world = SimpleWorld::new(&env);
    assert_eq!(world.next_entity_id, 1);
    assert_eq!(world.components.len(), 0);
    assert_eq!(world.version(), 0);
}

#[test]
fn test_spawn_entity() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let id1 = world.spawn_entity();
    let id2 = world.spawn_entity();
    assert_eq!(id1, 1);
    assert_eq!(id2, 2);
}

#[test]
fn test_add_and_get_component() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity_id = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1, 2, 3, 4]);
    world.add_component(entity_id, symbol_short!("test"), data.clone());

    let retrieved = world.get_component(entity_id, &symbol_short!("test"));
    assert_eq!(retrieved, Some(data));
}

#[test]
fn test_has_component() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity_id = world.spawn_entity();

    assert!(!world.has_component(entity_id, &symbol_short!("test")));

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(entity_id, symbol_short!("test"), data);

    assert!(world.has_component(entity_id, &symbol_short!("test")));
}

#[test]
fn test_remove_component() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity_id = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(entity_id, symbol_short!("test"), data);

    assert!(world.remove_component(entity_id, &symbol_short!("test")));
    assert!(!world.has_component(entity_id, &symbol_short!("test")));
    assert!(!world.remove_component(entity_id, &symbol_short!("test")));
}

#[test]
fn test_get_entities_with_component() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);

    let e1 = world.spawn_entity();
    let e2 = world.spawn_entity();
    let e3 = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(e1, symbol_short!("pos"), data.clone());
    world.add_component(e2, symbol_short!("pos"), data.clone());
    world.add_component(e3, symbol_short!("vel"), data);

    let entities = world.get_entities_with_component(&symbol_short!("pos"), &env);
    assert_eq!(entities.len(), 2);
    assert_eq!(world.table_component_count(&symbol_short!("pos")), 2);
    assert_eq!(world.component_count(&symbol_short!("pos")), 2);
}

#[test]
fn test_despawn_entity() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity_id = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(entity_id, symbol_short!("a"), data.clone());
    world.add_component(entity_id, symbol_short!("b"), data);

    world.despawn_entity(entity_id);
    assert!(!world.has_component(entity_id, &symbol_short!("a")));
    assert!(!world.has_component(entity_id, &symbol_short!("b")));
}

#[test]
fn test_version_increments_on_mutations() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    assert_eq!(world.version(), 0);

    let e1 = world.spawn_entity();
    let data = Bytes::from_array(&env, &[1]);
    world.add_component(e1, symbol_short!("test"), data);
    assert_eq!(world.version(), 1);

    world.remove_component(e1, &symbol_short!("test"));
    assert_eq!(world.version(), 2);

    let data2 = Bytes::from_array(&env, &[2]);
    world.add_component(e1, symbol_short!("a"), data2);
    assert_eq!(world.version(), 3);

    world.despawn_entity(e1);
    assert_eq!(world.version(), 4);
}

#[test]
fn test_sparse_component_storage() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e1 = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component_with_storage(
        e1,
        symbol_short!("marker"),
        data.clone(),
        ComponentStorage::Sparse,
    );

    assert!(world.has_component(e1, &symbol_short!("marker")));
    assert_eq!(
        world.get_component(e1, &symbol_short!("marker")),
        Some(data)
    );

    assert!(!world.components.contains_key((e1, symbol_short!("marker"))));
    assert!(world
        .sparse_components
        .contains_key((e1, symbol_short!("marker"))));
    assert_eq!(world.table_component_count(&symbol_short!("marker")), 0);
    assert_eq!(world.component_count(&symbol_short!("marker")), 1);
}

#[test]
fn test_indices_follow_storage_migration() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity = world.spawn_entity();
    let data = Bytes::from_array(&env, &[7]);

    world.add_component(entity, symbol_short!("state"), data.clone());
    assert_eq!(world.table_component_count(&symbol_short!("state")), 1);
    assert_eq!(world.component_count(&symbol_short!("state")), 1);

    world.add_component_with_storage(
        entity,
        symbol_short!("state"),
        data,
        ComponentStorage::Sparse,
    );
    assert_eq!(world.table_component_count(&symbol_short!("state")), 0);
    assert_eq!(world.component_count(&symbol_short!("state")), 1);
}

#[test]
fn test_sparse_vs_table_queries() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);

    let e1 = world.spawn_entity();
    let e2 = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(e1, symbol_short!("pos"), data.clone());
    world.add_component_with_storage(
        e1,
        symbol_short!("tag"),
        data.clone(),
        ComponentStorage::Sparse,
    );
    world.add_component_with_storage(e2, symbol_short!("tag"), data, ComponentStorage::Sparse);

    let table_only = world.get_table_entities_with_component(&symbol_short!("pos"), &env);
    assert_eq!(table_only.len(), 1);

    let all_pos = world.get_entities_with_component(&symbol_short!("pos"), &env);
    assert_eq!(all_pos.len(), 1);

    let all_tag = world.get_all_entities_with_component(&symbol_short!("tag"), &env);
    assert_eq!(all_tag.len(), 2);

    let table_tag = world.get_table_entities_with_component(&symbol_short!("tag"), &env);
    assert_eq!(table_tag.len(), 0);
}

#[test]
fn test_remove_sparse_component() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e1 = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component_with_storage(e1, symbol_short!("tag"), data, ComponentStorage::Sparse);
    assert!(world.has_component(e1, &symbol_short!("tag")));

    assert!(world.remove_component(e1, &symbol_short!("tag")));
    assert!(!world.has_component(e1, &symbol_short!("tag")));
}

#[test]
fn test_despawn_clears_both_maps() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e1 = world.spawn_entity();

    let data = Bytes::from_array(&env, &[1]);
    world.add_component(e1, symbol_short!("pos"), data.clone());
    world.add_component_with_storage(e1, symbol_short!("tag"), data, ComponentStorage::Sparse);

    world.despawn_entity(e1);
    assert!(!world.has_component(e1, &symbol_short!("pos")));
    assert!(!world.has_component(e1, &symbol_short!("tag")));
}

#[test]
fn test_add_component_replaces_existing() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let entity_id = world.spawn_entity();

    let data1 = Bytes::from_array(&env, &[1]);
    let data2 = Bytes::from_array(&env, &[2]);

    world.add_component(entity_id, symbol_short!("test"), data1);
    world.add_component(entity_id, symbol_short!("test"), data2.clone());

    let retrieved = world.get_component(entity_id, &symbol_short!("test"));
    assert_eq!(retrieved, Some(data2));
}

#[test]
fn test_set_and_get_typed() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e = world.spawn_entity();

    let pos = Position::new(10, 20);
    world.set_typed(&env, e, &pos);

    let retrieved: Option<Position> = world.get_typed(&env, e);
    assert!(retrieved.is_some());
    let r = retrieved.unwrap();
    assert_eq!(r.x, 10);
    assert_eq!(r.y, 20);
}

#[test]
fn test_has_typed() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e = world.spawn_entity();

    assert!(!world.has_typed::<Position>(e));
    world.set_typed(&env, e, &Position::new(1, 2));
    assert!(world.has_typed::<Position>(e));
}

#[test]
fn test_remove_typed() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e = world.spawn_entity();

    world.set_typed(&env, e, &Position::new(1, 2));
    assert!(world.remove_typed::<Position>(e));
    assert!(!world.has_typed::<Position>(e));
    assert!(!world.remove_typed::<Position>(e));
}

#[test]
fn test_get_typed_nonexistent() {
    let env = Env::default();
    let world = SimpleWorld::new(&env);
    let result: Option<Position> = world.get_typed(&env, 999);
    assert!(result.is_none());
}

#[test]
fn test_set_typed_overwrites() {
    let env = Env::default();
    let mut world = SimpleWorld::new(&env);
    let e = world.spawn_entity();

    world.set_typed(&env, e, &Position::new(1, 2));
    world.set_typed(&env, e, &Position::new(50, 60));

    let pos: Position = world.get_typed(&env, e).unwrap();
    assert_eq!(pos.x, 50);
    assert_eq!(pos.y, 60);
}