Files
gamedev-the-steward/tests/unit/test_world_event_store.gd
T
2026-08-12 20:06:51 +02:00

173 lines
5.7 KiB
GDScript

extends GutTest
func test_value_objects_round_trip_and_reject_invalid_data() -> void:
var entity_ref := WorldEntityRef.create(&"npc", &"7")
var address := SpatialAddress.create(&"jajce", &"village_square", Vector3(2.0, 3.0, 4.0))
assert_not_null(entity_ref)
assert_eq(
WorldEntityRef.from_dictionary(entity_ref.to_dictionary()).to_dictionary(),
entity_ref.to_dictionary()
)
assert_not_null(address)
assert_eq(
SpatialAddress.from_dictionary(address.to_dictionary()).to_dictionary(),
address.to_dictionary()
)
assert_null(WorldEntityRef.create(&"npc", &""))
assert_null(
(
SpatialAddress
. from_dictionary(
{
"schema_version": SpatialAddress.SCHEMA_VERSION,
"world_id": "jajce",
"location_id": "square",
"position": [0.0, 1.0],
}
)
)
)
func test_record_serialization_is_deterministic_and_defensive() -> void:
var actor := WorldEntityRef.create(&"npc", &"7")
var resource := WorldEntityRef.create(&"resource", &"berry_patch_a")
var participants := {
&"source": resource,
&"actor": actor,
}
var payload := {
&"zeta": 2,
&"alpha": &"food",
&"nested": {&"beta": 2, &"alpha": 1},
}
var event := WorldEventRecord.create(
42,
&"resource_extracted",
12,
participants,
SpatialAddress.create(&"jajce", &"forest_edge", Vector3(4.0, 0.0, 8.0)),
payload
)
participants.clear()
payload[&"zeta"] = 99
var serialized := event.to_dictionary()
var restored := WorldEventRecord.from_dictionary(serialized)
assert_not_null(restored)
assert_eq(restored.to_dictionary(), serialized)
assert_eq(JSON.stringify(restored.to_dictionary()), JSON.stringify(serialized))
assert_eq(restored.get_participant_roles(), [&"actor", &"source"])
assert_eq(restored.get_payload()["zeta"], 2)
assert_eq(restored.get_payload()["alpha"], "food")
serialized["payload"]["zeta"] = 100
assert_eq(restored.get_payload()["zeta"], 2)
func test_store_indexes_id_type_participant_location_and_tick() -> void:
var store := WorldEventStore.new()
var actor_a := WorldEntityRef.create(&"npc", &"7")
var actor_b := WorldEntityRef.create(&"npc", &"8")
var pantry := WorldEntityRef.create(&"storage", &"village_pantry")
var square := SpatialAddress.create(&"jajce", &"village_square", Vector3.ZERO)
var forest := SpatialAddress.create(&"jajce", &"forest_edge", Vector3(12.0, 0.0, 4.0))
var later_event := WorldEventRecord.create(
20,
&"storage_deposited",
9,
{&"actor": actor_a, &"destination": pantry},
square,
{"item_id": "food", "amount": 2.0}
)
var earlier_event := WorldEventRecord.create(
7, &"resource_extracted", 4, {&"actor": actor_a}, forest, {"item_id": "food", "amount": 2.0}
)
var same_tick_event := WorldEventRecord.create(
9,
&"storage_deposited",
9,
{&"actor": actor_b, &"source": pantry},
square,
{"item_id": "food", "amount": 1.0}
)
assert_true(store.append(later_event))
assert_true(store.append(earlier_event))
assert_true(store.append(same_tick_event))
assert_false(store.append(later_event))
assert_same(store.get_by_id(20), store.get_by_id(20))
assert_eq(_event_ids(store.get_all()), [7, 9, 20])
assert_eq(_event_ids(store.get_for_type(&"storage_deposited")), [9, 20])
assert_eq(_event_ids(store.get_for_participant(actor_a)), [7, 20])
assert_eq(_event_ids(store.get_for_participant(pantry)), [9, 20])
assert_eq(_event_ids(store.get_for_location(square)), [9, 20])
assert_eq(_event_ids(store.get_for_tick(9)), [9, 20])
assert_eq(_event_ids(store.get_between_ticks(4, 9)), [7, 9, 20])
assert_true(store.get_for_type(&"unknown").is_empty())
func test_store_round_trip_rebuilds_all_indexes_transactionally() -> void:
var original := WorldEventStore.new()
var actor := WorldEntityRef.create(&"npc", &"3")
var address := SpatialAddress.create(&"jajce", &"pasture", Vector3(1.0, 0.0, 2.0))
assert_true(
original.append(
WorldEventRecord.create(
5, &"animal_fed", 14, {&"actor": actor}, address, {"amount": 1.0}
)
)
)
var serialized := original.to_dictionary()
var restored := WorldEventStore.from_dictionary(serialized)
assert_not_null(restored)
assert_eq(restored.to_dictionary(), serialized)
assert_eq(_event_ids(restored.get_for_type(&"animal_fed")), [5])
assert_eq(_event_ids(restored.get_for_participant(actor)), [5])
assert_eq(_event_ids(restored.get_for_location(address)), [5])
assert_eq(_event_ids(restored.get_for_tick(14)), [5])
var invalid_restore := {
"schema_version": WorldEventStore.SCHEMA_VERSION,
"events": [serialized["events"][0], serialized["events"][0]],
}
assert_false(restored.restore_from_dictionary(invalid_restore))
assert_eq(restored.to_dictionary(), serialized)
func test_economic_event_compatibility_conversion_preserves_contract() -> void:
var economic := EconomicEventRecord.create(
12,
&"storage_deposited",
30,
4,
&"npc_inventory_4",
&"village_pantry",
&"food",
2.5,
Vector3(3.0, 0.0, 7.0)
)
economic.data["custom_fact"] = {"reason": "harvest"}
var world_event := WorldEventRecord.from_economic_event(economic, &"jajce", &"village_pantry")
var round_tripped := world_event.to_economic_event()
assert_not_null(world_event)
assert_eq(world_event.get_event_id(), 12)
assert_eq(world_event.get_participant(&"actor").get_entity_id(), &"4")
assert_eq(world_event.get_participant(&"source").get_entity_id(), &"npc_inventory_4")
assert_eq(world_event.get_participant(&"destination").get_entity_id(), &"village_pantry")
assert_eq(round_tripped.to_dictionary(), economic.to_dictionary())
var store := WorldEventStore.new()
assert_not_null(store.append_economic(economic, &"jajce", &"village_pantry"))
assert_eq(store.get_by_id(12).to_economic_event().to_dictionary(), economic.to_dictionary())
func _event_ids(events: Array[WorldEventRecord]) -> Array[int]:
var ids: Array[int] = []
for event in events:
ids.append(event.get_event_id())
return ids