emergence-mini-dilles/tests/test_tools.py
Jeuners ddf9598518 Emergence-Mini: minimaler Klon von Emergence-World
4 Agenten, 14 Landmarks, 15 Tools, 240x240 Grid, SQLite-Persistenz.
Round-Robin Turn-Manager mit Reactive Triggern, Town-Hall-Voting
(70%-Threshold) mit Live-Constitution-Amendment.

- engine/: db, world, agents, needs, tools, reasoning, governance, turn
- web/: Canvas-basierte Live-View mit WebSocket-Stream
- server.py: FastAPI + WebSocket auf 127.0.0.1:8080
- tests/: 70 Unit + Integration Tests (pytest), alle gruen
- smoke_test.py: 50+ End-to-End-Checks
- README: Quickstart, Architektur, Security, Tests, Lizenz
- .gitignore: DB, Cache, Logs

Basiert auf https://github.com/EmergenceAI/Emergence-World
(Lizenz: CC-BY-NC-4.0, Research-only)
2026-06-15 01:07:38 +02:00

220 lines
7.6 KiB
Python

"""Tool registry tests: all 15 tools, location-gating, handlers."""
import pytest
def test_registry_has_15_tools():
from engine import tools
tools.bootstrap() # idempotent
assert len(tools.all_tools()) == 15
def test_all_tool_names():
from engine import tools
tools.bootstrap()
names = {t.name for t in tools.all_tools()}
expected = {
"go_to_place", "go_home", "say_to_agent", "speak_to_all",
"show_emoticon", "idle", "recharge_energy", "add_to_longterm_memory",
"write_blog", "add_to_billboard", "read_billboard",
"submit_townhall_proposal", "vote_on_proposal", "list_agents",
"list_landmarks",
}
assert expected.issubset(names), f"missing: {expected - names}"
def test_go_to_place_moves_agent(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
res = tools.get("go_to_place").handler(spark, {"place": "library"}, {})
assert res["ok"]
spark2 = agents_mod.get("spark")
assert (spark2["x"], spark2["y"]) == (60, 60)
def test_go_to_place_unknown_place(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
res = tools.get("go_to_place").handler(spark, {"place": "atlantis"}, {})
assert not res["ok"]
assert "unknown" in res["error"]
def test_go_home(tmp_db):
from engine import tools, agents as agents_mod
# move spark away
tools.get("go_to_place").handler(agents_mod.get("spark"), {"place": "plaza"}, {})
res = tools.get("go_home").handler(agents_mod.get("spark"), {}, {})
assert res["ok"]
a = agents_mod.get("spark")
assert (a["x"], a["y"]) == (210, 210)
def test_say_to_agent_queues_event(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
ctx = {"speak_events": []}
res = tools.get("say_to_agent").handler(
spark, {"target": "flora", "text": "hello"}, ctx
)
assert res["ok"]
assert len(ctx["speak_events"]) == 1
assert ctx["speak_events"][0]["to"] == "flora"
def test_say_to_agent_empty_text(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
ctx = {"speak_events": []}
res = tools.get("say_to_agent").handler(
spark, {"target": "flora", "text": " "}, ctx
)
assert not res["ok"]
def test_say_to_agent_unknown_target(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
res = tools.get("say_to_agent").handler(
spark, {"target": "ghost", "text": "boo"}, {}
)
assert not res["ok"]
def test_speak_to_all(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
ctx = {"speak_events": []}
res = tools.get("speak_to_all").handler(spark, {"text": "Hello town!"}, ctx)
assert res["ok"]
assert res["broadcast"]
assert len(ctx["speak_events"]) == 1
def test_show_emoticon(tmp_db):
from engine import tools, agents as agents_mod
spark = agents_mod.get("spark")
ctx = {"speak_events": []}
res = tools.get("show_emoticon").handler(spark, {"emoticon": "👋"}, ctx)
assert res["ok"]
assert res["emoticon"] == "👋"
assert len(ctx["speak_events"]) == 1
def test_idle(tmp_db):
from engine import tools, agents as agents_mod
res = tools.get("idle").handler(agents_mod.get("anchor"), {}, {})
assert res["ok"]
assert res["idle"]
def test_recharge_costs_credit(tmp_db):
from engine import tools, agents as agents_mod
# Set energy low, credits ok
agents_mod.update_state("anchor", energy=20.0, credits=5.0)
# Move to cafe
tools.get("go_to_place").handler(agents_mod.get("anchor"), {"place": "cafe"}, {})
res = tools.get("recharge_energy").handler(agents_mod.get("anchor"), {}, {})
assert res["ok"]
a = agents_mod.get("anchor")
assert a["energy"] == 70.0 # 20 + 50
assert a["credits"] == 4.0 # 5 - 1
def test_recharge_fails_without_credits(tmp_db):
from engine import tools, agents as agents_mod
agents_mod.update_state("anchor", energy=20.0, credits=0.0)
tools.get("go_to_place").handler(agents_mod.get("anchor"), {"place": "cafe"}, {})
res = tools.get("recharge_energy").handler(agents_mod.get("anchor"), {}, {})
assert not res["ok"]
assert "credits" in res["error"]
def test_recharge_caps_at_100(tmp_db):
from engine import tools, agents as agents_mod
agents_mod.update_state("anchor", energy=80.0, credits=5.0)
tools.get("go_to_place").handler(agents_mod.get("anchor"), {"place": "cafe"}, {})
tools.get("recharge_energy").handler(agents_mod.get("anchor"), {}, {})
a = agents_mod.get("anchor")
assert a["energy"] == 100.0 # capped
def test_recharge_not_available_off_site(tmp_db):
from engine import tools, world, agents as agents_mod
spark = agents_mod.get("spark")
at_lm = world.landmark_at(spark["x"], spark["y"])
# spark spawns at home_spark, not cafe
assert not tools.get("recharge_energy").available_for(spark, at_lm)
def test_add_to_longterm_memory(tmp_db):
from engine import tools, agents as agents_mod
res = tools.get("add_to_longterm_memory").handler(
agents_mod.get("anchor"),
{"content": "today I learned something"},
{}
)
assert res["ok"]
def test_add_to_longterm_memory_empty(tmp_db):
from engine import tools, agents as agents_mod
res = tools.get("add_to_longterm_memory").handler(
agents_mod.get("anchor"), {"content": ""}, {}
)
assert not res["ok"]
def test_write_blog_boosts_knowledge(tmp_db):
from engine import tools, agents as agents_mod
agents_mod.update_state("anchor", knowledge=50.0)
res = tools.get("write_blog").handler(
agents_mod.get("anchor"),
{"title": "Hi", "body": "body text"},
{}
)
assert res["ok"]
a = agents_mod.get("anchor")
assert a["knowledge"] == 70.0 # 50 + 20
def test_add_to_billboard_gated(tmp_db):
from engine import tools, world, agents as agents_mod
spark = agents_mod.get("spark")
at_lm = world.landmark_at(spark["x"], spark["y"])
# spark at home_spark — not at billboard
assert not tools.get("add_to_billboard").available_for(spark, at_lm)
# Move to billboard
tools.get("go_to_place").handler(spark, {"place": "billboard"}, {})
spark = agents_mod.get("spark")
at_lm = world.landmark_at(spark["x"], spark["y"])
assert tools.get("add_to_billboard").available_for(spark, at_lm)
def test_submit_proposal_gated_to_town_hall(tmp_db):
from engine import tools, world, agents as agents_mod
a = agents_mod.get("anchor")
at_lm = world.landmark_at(a["x"], a["y"])
assert not tools.get("submit_townhall_proposal").available_for(a, at_lm)
tools.get("go_to_place").handler(a, {"place": "town_hall"}, {})
a = agents_mod.get("anchor")
at_lm = world.landmark_at(a["x"], a["y"])
assert tools.get("submit_townhall_proposal").available_for(a, at_lm)
def test_submit_proposal_requires_title_and_body(tmp_db):
from engine import tools, agents as agents_mod
tools.get("go_to_place").handler(agents_mod.get("anchor"), {"place": "town_hall"}, {})
res = tools.get("submit_townhall_proposal").handler(
agents_mod.get("anchor"), {"title": "", "body": "x"}, {}
)
assert not res["ok"]
def test_list_agents_and_landmarks(tmp_db):
from engine import tools, agents as agents_mod
res = tools.get("list_agents").handler(agents_mod.get("anchor"), {}, {})
assert res["ok"]
assert "Anchor" in res["agents"]
res = tools.get("list_landmarks").handler(agents_mod.get("anchor"), {}, {})
assert res["ok"]
assert "Town Hall" in res["landmarks"]