cougr-core 1.0.0

Cougr - A Soroban-compatible ECS framework for on-chain gaming on Stellar
Documentation
use super::*;
use crate::resource::GameState as RuntimeGameState;
use soroban_sdk::{symbol_short, Bytes, Env};

struct TestPlugin;

impl Plugin for TestPlugin {
    fn name(&self) -> &'static str {
        "test_plugin"
    }

    fn build(&self, app: &mut GameApp) {
        app.add_system("test_system", test_system_fn);
    }
}

struct HookPlugin;

impl Plugin for HookPlugin {
    fn name(&self) -> &'static str {
        "hook_plugin"
    }

    fn build(&self, app: &mut GameApp) {
        app.add_hook_on_add(symbol_short!("pos"), noop_add_hook);
    }
}

fn test_system_fn(world: &mut SimpleWorld, env: &Env) {
    let e = world.spawn_entity();
    let data = Bytes::from_array(env, &[0xFF]);
    world.add_component(e, symbol_short!("marker"), data);
}

fn noop_add_hook(
    _entity_id: crate::simple_world::EntityId,
    _component_type: &Symbol,
    _data: &Bytes,
) {
}

#[test]
fn test_game_app_new() {
    let env = Env::default();
    let app = GameApp::new(&env);
    assert_eq!(app.plugin_count(), 0);
    assert_eq!(app.system_count(), 0);
}

#[test]
fn test_add_plugin() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(TestPlugin);

    assert_eq!(app.plugin_count(), 1);
    assert!(app.has_plugin("test_plugin"));
    assert_eq!(app.system_count(), 1);
}

#[test]
fn test_duplicate_plugin_skipped() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(TestPlugin);
    app.add_plugin(TestPlugin);

    assert_eq!(app.plugin_count(), 1);
    assert_eq!(app.system_count(), 1);
}

#[test]
fn test_plugin_configures_hooks() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(HookPlugin);

    assert_eq!(app.plugin_count(), 1);
    assert_eq!(app.hooks().add_hook_count(), 1);
}

#[test]
fn test_run_executes_systems() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(TestPlugin);
    app.run(&env).unwrap();

    assert!(app.world().has_component(1, &symbol_short!("marker")));
}

#[test]
fn test_into_world() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(TestPlugin);
    app.run(&env).unwrap();

    let world = app.into_world();
    assert!(world.has_component(1, &symbol_short!("marker")));
}

#[test]
fn test_with_world() {
    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!("pre"), data);

    let app = GameApp::with_world(world);
    assert!(app.world().has_component(e1, &symbol_short!("pre")));
}

#[test]
fn test_add_system_directly() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_system("direct", test_system_fn);

    assert_eq!(app.system_count(), 1);
    assert_eq!(app.plugin_count(), 0);
}

#[test]
fn test_multiple_plugins() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugin(TestPlugin);
    app.add_plugin(HookPlugin);

    assert_eq!(app.plugin_count(), 2);
    assert!(app.has_plugin("test_plugin"));
    assert!(app.has_plugin("hook_plugin"));
}

#[test]
fn test_startup_system_runs_once() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_startup_system("startup", test_system_fn);

    app.run(&env).unwrap();
    app.run(&env).unwrap();

    assert!(app.world().has_component(1, &symbol_short!("marker")));
    assert_eq!(app.world().component_count(&symbol_short!("marker")), 1);
}

#[test]
fn test_add_plugins_group_and_resources() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_plugins((TestPlugin, HookPlugin));

    let mut state = RuntimeGameState::new();
    state.increment_score(42);
    app.insert_resource(&env, &state);

    let loaded = app.get_resource::<RuntimeGameState>(&env).unwrap();
    assert_eq!(loaded.score, 42);
    assert_eq!(app.plugin_count(), 2);
    assert_eq!(app.resources().len(), 1);
}

#[test]
fn test_add_system_in_stage_and_context_variants() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_system_in_stage(ScheduleStage::PreUpdate, "spawn_a", test_system_fn);
    app.add_context_system_in_stage(ScheduleStage::PostUpdate, "spawn_b", |context| {
        let entity = context.world_mut().spawn_entity();
        let data = Bytes::from_array(context.env(), &[0x0A]);
        context
            .world_mut()
            .add_component(entity, symbol_short!("ctx"), data);
    });

    app.run(&env).unwrap();

    assert!(app.world().has_component(1, &symbol_short!("marker")));
    assert!(app.world().has_component(2, &symbol_short!("ctx")));
}

#[test]
fn test_add_simple_system_and_configure_system() {
    let env = Env::default();
    let mut app = GameApp::new(&env);
    app.add_simple_system(
        "spawn_ctx",
        crate::system::context_system(|context| {
            let entity = context.world_mut().spawn_entity();
            let data = Bytes::from_array(context.env(), &[0xAB]);
            context
                .world_mut()
                .add_component(entity, symbol_short!("simp"), data);
        }),
    );
    app.configure_system(
        "spawn_ctx",
        SystemConfig::new().in_stage(ScheduleStage::Cleanup),
    )
    .unwrap();

    app.run(&env).unwrap();

    assert!(app.world().has_component(1, &symbol_short!("simp")));
}