@@ -390,3 +390,97 @@ func test_observer_with_multiple_entities():
390390
391391 # Should have detected all 5 changes
392392 assert_int (observer .changed_count ).is_equal (5 )
393+
394+
395+ ## Test that removing an entity via world.remove_entity triggers on_component_removed
396+ func test_observer_on_remove_entity_triggers_component_removed ():
397+ var observer = O_ObserverTest .new ()
398+ world .add_observer (observer )
399+
400+ # Create entity with the watched component
401+ var entity = Entity .new ()
402+ var component = C_ObserverTest .new (42 , "test" )
403+ entity .add_component (component )
404+ world .add_entity (entity )
405+
406+ assert_int (observer .added_count ).is_equal (1 )
407+ observer .reset ()
408+
409+ # Remove the entire entity from the world
410+ world .remove_entity (entity )
411+
412+ # Observer should have been notified about the component removal
413+ assert_int (observer .removed_count ).is_equal (1 )
414+ assert_object (observer .last_removed_entity ).is_equal (entity )
415+
416+
417+ ## Test that removing an entity with multiple components notifies all relevant observers
418+ func test_observer_on_remove_entity_multiple_components ():
419+ var observer = O_ObserverTest .new ()
420+ var health_observer = O_HealthObserver .new ()
421+ world .add_observer (observer )
422+ world .add_observer (health_observer )
423+
424+ # Create entity with both watched components
425+ var entity = Entity .new ()
426+ entity .add_component (C_ObserverTest .new ())
427+ entity .add_component (C_ObserverHealth .new (100 ))
428+ world .add_entity (entity )
429+
430+ assert_int (observer .added_count ).is_equal (1 )
431+ assert_int (health_observer .health_added_count ).is_equal (1 )
432+ observer .reset ()
433+ health_observer .reset ()
434+
435+ # Remove the entire entity
436+ world .remove_entity (entity )
437+
438+ # Both observers should have been notified
439+ assert_int (observer .removed_count ).is_equal (1 )
440+ assert_int (health_observer .health_removed_count ).is_equal (1 )
441+
442+
443+ ## Test that removing multiple entities notifies observer for each
444+ func test_observer_on_remove_multiple_entities ():
445+ var observer = O_ObserverTest .new ()
446+ world .add_observer (observer )
447+
448+ # Create and add 3 entities
449+ var entities_list : Array [Entity ] = []
450+ for i in range (3 ):
451+ var entity = Entity .new ()
452+ entity .add_component (C_ObserverTest .new (i ))
453+ world .add_entity (entity )
454+ entities_list .append (entity )
455+
456+ assert_int (observer .added_count ).is_equal (3 )
457+ observer .reset ()
458+
459+ # Remove all entities
460+ for entity in entities_list :
461+ world .remove_entity (entity )
462+
463+ # Observer should have been notified for each removal
464+ assert_int (observer .removed_count ).is_equal (3 )
465+
466+
467+ ## Regression test: observer side-effects during remove_entity must not cause double notification.
468+ ## O_TestCleanupSideEffect removes C_ObserverHealth when it sees C_ObserverTest removed.
469+ ## If entity signals are still connected during the loop, health_observer fires twice (bug).
470+ ## If signals are disconnected before the loop, health_observer fires exactly once (correct).
471+ func test_observer_no_double_notification_on_remove_entity ():
472+ var cleanup_observer = O_TestCleanupSideEffectObserver .new ()
473+ var health_observer = O_HealthObserver .new ()
474+ world .add_observer (cleanup_observer )
475+ world .add_observer (health_observer )
476+
477+ var entity = Entity .new ()
478+ entity .add_component (C_ObserverTest .new ())
479+ entity .add_component (C_ObserverHealth .new (100 ))
480+ world .add_entity (entity )
481+
482+ health_observer .reset ()
483+
484+ world .remove_entity (entity )
485+
486+ assert_int (health_observer .health_removed_count ).is_equal (1 )
0 commit comments