quilltale / tests /test_npc_memory.py
aeesh1's picture
added tests for npc memory
abed043
"""
Tests for NPC episodic memory system.
"""
from src.world.state import WorldState, MemoryEntry
import json
def load_test_state() -> WorldState:
with open("data/worlds/default.json") as f:
return WorldState.from_json(f.read())
def test_memory_written_on_interaction():
state = load_test_state()
changes = state.apply_update({
"npc_memory": {
"barkeep": {
"description": "Player asked about the locked chest upstairs",
"emotional_tone": "suspicious",
"significance": 2,
}
}
})
barkeep = state.npcs["barkeep"]
assert len(barkeep.memories) == 1
assert barkeep.memories[0].emotional_tone == "suspicious"
assert barkeep.memories[0].significance == 2
assert any("Marta" in c for c in changes)
def test_multiple_memories_accumulate():
state = load_test_state()
state.apply_update({"npc_memory": {
"barkeep": {"description": "Player ordered ale", "emotional_tone": "neutral", "significance": 1}
}})
state.apply_update({"npc_memory": {
"barkeep": {"description": "Player stole the dagger", "emotional_tone": "angry", "significance": 3}
}})
state.apply_update({"npc_memory": {
"barkeep": {"description": "Player apologised and returned the dagger", "emotional_tone": "wary", "significance": 2}
}})
barkeep = state.npcs["barkeep"]
assert len(barkeep.memories) == 3
def test_relevant_memories_prioritises_significance():
state = load_test_state()
barkeep = state.npcs["barkeep"]
# Add memories with different significance levels
barkeep.memories = [
MemoryEntry(turn=1, description="minor thing", emotional_tone="neutral", significance=1),
MemoryEntry(turn=2, description="very significant thing", emotional_tone="angry", significance=3),
MemoryEntry(turn=3, description="notable thing", emotional_tone="wary", significance=2),
]
relevant = barkeep.relevant_memories(max_memories=2)
# Should get the significance=3 memory first
assert relevant[0].significance == 3
assert relevant[1].significance == 2
def test_memory_appears_in_context_summary():
state = load_test_state()
state.apply_update({"npc_memory": {
"barkeep": {
"description": "Player threatened her with the dagger",
"emotional_tone": "fearful",
"significance": 3,
}
}})
summary = state.to_context_summary()
# Memory should appear in the context the GM sees
assert "threatened" in summary
assert "fearful" in summary
def test_memory_survives_serialisation_roundtrip():
state = load_test_state()
state.apply_update({"npc_memory": {
"barkeep": {
"description": "Player left a large tip",
"emotional_tone": "grateful",
"significance": 2,
}
}})
# Serialise and restore
restored = WorldState.from_json(state.to_json())
barkeep = restored.npcs["barkeep"]
assert len(barkeep.memories) == 1
assert barkeep.memories[0].emotional_tone == "grateful"
assert barkeep.memories[0].description == "Player left a large tip"
def test_dead_npc_memory_not_injected_in_summary():
state = load_test_state()
# Give barkeep a memory then kill her
state.apply_update({"npc_memory": {
"barkeep": {"description": "Player was rude", "emotional_tone": "angry", "significance": 1}
}})
state.apply_update({"npc_state": {"barkeep": {"alive": False}}})
summary = state.to_context_summary()
# Dead NPCs should not inject memories
assert "Player was rude" not in summary
def test_memory_summary_format():
state = load_test_state()
barkeep = state.npcs["barkeep"]
barkeep.memories = [
MemoryEntry(turn=5, description="Gave player information about the chest",
emotional_tone="neutral", significance=1),
]
summary = barkeep.memory_summary()
assert "Marta" in summary
assert "Gave player information" in summary
assert "neutral" in summary