Files
viandante/tests/test_mapper.py
T
truetype b20f3c0f31
ci / lint (ruff) (push) Has been cancelled
ci / pytest (py3.10) (push) Has been cancelled
ci / pytest (py3.11) (push) Has been cancelled
ci / pytest (py3.12) (push) Has been cancelled
ci(actions): add Gitea Actions ci+release workflows + fix ruff findings
- .gitea/workflows/ci.yml: ruff + matrix pytest on 3.10/3.11/3.12
  on push and PR to main; concurrency cancels superseded runs
- .gitea/workflows/release.yml: gate (ruff + pytest) + build sdist/wheel
  on tag 'v*', uploads via actions/upload-artifact@v4
- README.md: add ci and version badges right under H1
- src/viandante/cli.py: drop unused prompt_toolkit.patch_stdout import
- tests/test_deadsouls.py: add missing RoomBlock import (F821)
- tests/test_logger.py: drop unused local 'raw' in unicode test
- tests/test_mapper.py: rename unused locals to _a/_b/_c (F841) to
  preserve intent (B has 's' back-edge; C is orphan)
2026-07-07 17:15:54 +00:00

101 lines
3.1 KiB
Python

"""Tests for the room graph + ASCII map renderer."""
from __future__ import annotations
from viandante.mapper import Mapper
def test_visit_returns_stable_id() -> None:
m = Mapper()
a = m.visit("Cave Entrance", "", ["n", "s"])
b = m.visit("Cave Entrance", "", ["n", "s"])
assert a == b
def test_dynamic_description_does_not_break_id() -> None:
m = Mapper()
a = m.visit("Cave Entrance", "Dry and dusty.", ["n"])
b = m.visit("Cave Entrance", "Now dripping with water.", ["n"])
assert a == b
assert len(m.rooms) == 1
def test_render_empty() -> None:
assert Mapper().render() == "[mappa vuota]"
def test_render_single_room() -> None:
m = Mapper()
m.visit("Lobby", "Wide open space.", [])
out = m.render()
assert "Lobby" in out
assert "»" in out # current marker
assert "·" not in out # no neighbours
def test_render_grid_with_connectors() -> None:
m = Mapper()
a = m.visit("Hub", "central.", ["n", "e", "s", "w"])
b = m.visit("NorthRoom", "cold.", ["s"])
m.link_rooms(a, "n", b)
m.link_rooms(b, "s", a)
c = m.visit("EastRoom", "drafty.", ["w"])
m.link_rooms(a, "e", c)
m.link_rooms(c, "w", a)
out = m.render()
assert "Hub" in out
assert "NorthRoom" in out
assert "EastRoom" in out
assert "│" in out or "───" in out
# ----- multi-source BFS / orphan placement -------------------------------
def test_backtracking_visits_are_connected() -> None:
"""Walking A → B and never back should NOT leave B as orphan, because
B's exits include the back-edge to A.
"""
m = Mapper()
_a = m.visit("A", "", ["n"])
_b = m.visit("B", "", ["s"])
# No link_rooms() — but B has 's' which would point to A.
out = m.render(max_cols=20, max_rows=20)
assert "A" in out
assert "B" in out
# Both rooms must be visible in the rendered map.
rows = [r for r in out.splitlines() if r.strip()]
# Find the row containing B
assert any("B" in r for r in rows)
assert any("A" in r for r in rows)
def test_isolated_room_lands_somewhere_visible() -> None:
"""A room that has no edges at all should still end up in the rendered
map (stacked in the isolated-corner fallback).
"""
m = Mapper()
m.visit("Lobby", "", [])
m.visit("Void", "", []) # no edges to anyone
out = m.render(max_cols=20, max_rows=20)
assert "Lobby" in out
assert "Void" in out
def test_id_override_used_for_visit() -> None:
"""When GMCP supplies a vnum, ``id_override`` is used instead of name-hash."""
m = Mapper()
a = m.visit("Outer Courtyard", "", ["n"], id_override="vnum42")
b = m.visit("Outer Courtyard", "", ["n", "e"], id_override="vnum42")
assert a == b == "vnum42"
def test_multi_source_render_does_not_split_obviously_connected_rooms() -> None:
m = Mapper()
a = m.visit("A", "", ["n", "s"])
b = m.visit("B", "", ["s"])
m.link_rooms(a, "n", b)
_c = m.visit("C", "", []) # orphan: no exits
out = m.render(max_cols=10, max_rows=10)
assert "A" in out
assert "B" in out
assert "C" in out