diff --git a/CLAUDE.md b/CLAUDE.md index f746a9f..dd65c69 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -175,7 +175,7 @@ as a grouped list does. Filling a page in is editing its `.md`; no Python follow ### Titles -- Sentence case, always ("Trace — YUL-Poly", "Nodes", "Path width", "Admin login"). +- Sentence case, always ("Trace — Lakeside", "Nodes", "Path width", "Admin login"). - **No emoji in any screen or dialog title** — icons live in menu/list rows. (A terminal that draws an emoji narrower than Rich measures leaves a content-sized dialog's border short.) diff --git a/docs/survey-navigation.md b/docs/survey-navigation.md index 2b3a8bf..2ffe57c 100644 --- a/docs/survey-navigation.md +++ b/docs/survey-navigation.md @@ -275,8 +275,8 @@ Four screens carry a live find-as-you-type filter and draw it through the one | **Select list** | **leaves the screen, filter and all** ([`select.py:773-774`](../meshterm/ui/tui/select.py#L773)) | deletes a char | | **Path composer** | **leaves the screen, filter and all** ([`path_composer.py:717-718`](../meshterm/ui/path_composer.py#L717)) | deletes a char, then pops a hop | -So on the map, typing `yul` then Esc shows you the map again. On a contact list, typing -`yul` then Esc drops you back to the menu. Same affordance, same glyphs, opposite outcome — +So on the map, typing `hub` then Esc shows you the map again. On a contact list, typing +`hub` then Esc drops you back to the menu. Same affordance, same glyphs, opposite outcome — and the select list is by far the most-used of the four. Chat is a fifth member of the family with a different peelable thing diff --git a/meshterm/ui/livefeed_screen.py b/meshterm/ui/livefeed_screen.py index d8fc47c..fc63f0f 100644 --- a/meshterm/ui/livefeed_screen.py +++ b/meshterm/ui/livefeed_screen.py @@ -108,7 +108,7 @@ _FEED_SEED_FLOOR = datetime.min.replace(tzinfo=timezone.utc) #: The feed's fixed subject-lane width; anything longer ellipsizes so the columns hold. #: Sized for a node name, which is what most classes put here — and wide enough that the -#: two endpoints of an addressed frame still read as ``a1 → YUL-Cartierv…`` rather than +#: two endpoints of an addressed frame still read as ``a1 → Hilltop-Repe…`` rather than #: collapsing to bare hashes. Widening it would push the row past the 72-column screen #: the lanes are budgeted for, so the cells the lane can't hold are the viewer's to spend. _FEED_SUBJECT_WIDTH = 18 diff --git a/meshterm/ui/message_paths_screen.py b/meshterm/ui/message_paths_screen.py index 0d1d6d9..299370d 100644 --- a/meshterm/ui/message_paths_screen.py +++ b/meshterm/ui/message_paths_screen.py @@ -17,7 +17,7 @@ detail read together: and introduced by nothing, the **route** as a path line (:mod:`~meshterm.ui.pathline`): the origin, every relay, and us — the same two endpoints the graph draws between, so the row and the picture start and finish in the same places. Each node is a chip in its - own hue, named where we know it (``YUL-Poly``) and standing in its own hash at the + own hue, named where we know it (``Lakeside``) and standing in its own hash at the device's path-hash width where we don't (``e839f2``, keyless grey); our own end is the app-wide ``★``. No hash is repeated after a name — the graph above is where the hash bytes live, and a chip and its label share the node's colour, so the two halves diff --git a/meshterm/ui/path_composer.py b/meshterm/ui/path_composer.py index 361a643..b0826d5 100644 --- a/meshterm/ui/path_composer.py +++ b/meshterm/ui/path_composer.py @@ -455,7 +455,7 @@ class PathComposerScreen(Screen): knows no name for it. The owning screen's resolver (:func:`~meshterm.services.trace_runner.make_node_resolver`) is the same first-match lookup the trace screens name their walked hops by, so falling back - to it means a node the trace window shows as *YUL-Poly* is *YUL-Poly* here too, + to it means a node the trace window shows as *Lakeside* is *Lakeside* here too, instead of a bare ``3d`` the reader has to decode. Nothing is invented: an unmatched hash comes back unchanged and renders as a hash. diff --git a/meshterm/ui/specimen.py b/meshterm/ui/specimen.py index 4b81f3a..8c1d020 100644 --- a/meshterm/ui/specimen.py +++ b/meshterm/ui/specimen.py @@ -23,7 +23,7 @@ from .widgets import _recency_style, channel_glyph, name_chip #: Demo identities for the name-colour row: ``(name, key)``. The keys are synthetic, and #: chosen so their first bytes land in three different sectors of the hue wheel — on the #: console that means three different palette slots (see ``theme._NODE_SLOT_HEXES``). -_DEMO_NAMES = (("Alice", "a1b2"), ("YUL-Poly", "3d63"), ("Lounge", "cc10")) +_DEMO_NAMES = (("Alice", "a1b2"), ("Lakeside", "3d63"), ("Lounge", "cc10")) def _palette_row() -> Text: diff --git a/meshterm/ui/trace_screen.py b/meshterm/ui/trace_screen.py index 7b6225a..1eb16f2 100644 --- a/meshterm/ui/trace_screen.py +++ b/meshterm/ui/trace_screen.py @@ -274,7 +274,7 @@ class TracingDialog(Screen): """Build the dialog. Args: - title: Heading for the dialog border (e.g. ``Tracing — YUL-Poly``). + title: Heading for the dialog border (e.g. ``Tracing — Lakeside``). spinner: The spinner to animate (shared with the owner's ticker). on_abort: Invoked when the user asks to abort (idempotent expected). """ diff --git a/meshterm/ui/tui/render.py b/meshterm/ui/tui/render.py index 20047d2..2f71761 100644 --- a/meshterm/ui/tui/render.py +++ b/meshterm/ui/tui/render.py @@ -247,7 +247,7 @@ def render_lines(renderable: RenderableType, width: int, *, no_wrap: bool = Fals def query_line(query: str, width: int) -> str: - """The live find/filter query echoed as one body line: ``/yul``, warn-styled. + """The live find/filter query echoed as one body line: ``/hub``, warn-styled. THE way a find-as-you-type screen shows what has been typed. Every such screen draws this same line above whatever the query narrows — the select list above its rows, the diff --git a/meshterm/ui/walk_screen.py b/meshterm/ui/walk_screen.py index ed5f704..4399d82 100644 --- a/meshterm/ui/walk_screen.py +++ b/meshterm/ui/walk_screen.py @@ -36,7 +36,7 @@ only its immediate neighbourhood: **Enter walks**: the highlighted neighbour becomes the new focus, the breadcrumb trail across the top grows (a :mod:`~meshterm.ui.pathline` path line — powerline chips where the -terminal draws them, ``you › YUL-Cartierville › …`` where it doesn't, each name in its +terminal draws them, ``you › Hilltop-Repeater › …`` where it doesn't, each name in its node's own hue), and **⌫ steps back** along it. Walking to a node already on the trail truncates the stack to its first appearance — the loop you walked to get back there is dropped rather than recorded. The trail never wraps: it is one line read through a @@ -271,7 +271,7 @@ class WalkScreen(Screen): """ super().__init__() # The screen's name, and nothing else (JP, 2026-08-30). It carried the focus and - # the graph's size — ``Mesh walk — YUL-Cartierville · 42 nodes · 61 links`` — which + # the graph's size — ``Mesh walk — Hilltop-Repeater · 42 nodes · 61 links`` — which # ran past a 53-column title bar, and every atom of it was already on the screen # below: the trail ends on the focus, the line under it names that node in colour # with its hash and its ring, and how big the neighbourhood is, is what the canvas diff --git a/tests/test_admin_login.py b/tests/test_admin_login.py index 6b6656e..df6bc1e 100644 --- a/tests/test_admin_login.py +++ b/tests/test_admin_login.py @@ -367,7 +367,7 @@ def test_the_reply_budget_is_sized_to_the_route_the_login_has_to_walk() -> None: trace_runner.trace_timeout = spy try: two_hops = Contact( - name="YUL-Far", public_key="a1b2c3d4" * 8, route_hops=("3d", "f2") + name="Hub-Far", public_key="a1b2c3d4" * 8, route_hops=("3d", "f2") ) asyncio.run(_device(_FakeMeshCore()).admin_login(two_hops, "hunter2")) asyncio.run(_device(_FakeMeshCore()).admin_login(_NODE, "hunter2")) @@ -401,7 +401,7 @@ def test_a_known_short_route_never_buys_less_patience_than_no_route_at_all() -> trace_runner.trace_timeout = spy try: - near = Contact(name="YUL-Near", public_key="a1b2c3d4" * 8, route_hops=("3d",)) + near = Contact(name="Hub-Near", public_key="a1b2c3d4" * 8, route_hops=("3d",)) asyncio.run(_device(_FakeMeshCore()).admin_login(near, "hunter2")) finally: trace_runner.trace_timeout = real diff --git a/tests/test_courier.py b/tests/test_courier.py index e537888..2aafda3 100644 --- a/tests/test_courier.py +++ b/tests/test_courier.py @@ -25,7 +25,7 @@ from meshterm.ui.courier_screen import ( ) NODE = "3d" * 6 -CONTACT = Contact(name="YUL", public_key="3d" * 32) +CONTACT = Contact(name="Hub", public_key="3d" * 32) class _StubChat: @@ -73,7 +73,7 @@ def test_store_queue_round_trips_and_persists(tmp_path: Path) -> None: path = tmp_path / "courier.json" store = CourierStore(path) when = utcnow() + timedelta(hours=8) - queued = store.queue(NODE, "YUL", "hello there", not_before=when) + queued = store.queue(NODE, "Hub", "hello there", not_before=when) assert queued.ident == 1 and queued.status == QUEUED again = CourierStore(path) @@ -86,8 +86,8 @@ def test_store_queue_round_trips_and_persists(tmp_path: Path) -> None: def test_store_lifecycle_attempts_finish_cancel_clear(tmp_path: Path) -> None: """Attempt marks, delivery, cancellation, and clearing all behave.""" store = CourierStore(tmp_path / "courier.json") - a = store.queue(NODE, "YUL", "one") - b = store.queue(NODE, "YUL", "two") + a = store.queue(NODE, "Hub", "one") + b = store.queue(NODE, "Hub", "two") store.note_attempt(a.ident) assert store.get(a.ident).attempts == 1 store.mark_delivered(a.ident) @@ -102,9 +102,9 @@ def test_store_lifecycle_attempts_finish_cancel_clear(tmp_path: Path) -> None: def test_store_caps_the_finished_history(tmp_path: Path) -> None: """Old finished entries fall off; the waiting queue is never trimmed.""" store = CourierStore(tmp_path / "courier.json") - keeper = store.queue(NODE, "YUL", "still waiting") + keeper = store.queue(NODE, "Hub", "still waiting") for i in range(DONE_CAP + 5): - entry = store.queue(NODE, "YUL", f"m{i}") + entry = store.queue(NODE, "Hub", f"m{i}") store.mark_delivered(entry.ident) done = [m for m in store.entries() if m.status != QUEUED] assert len(done) == DONE_CAP @@ -120,14 +120,14 @@ def test_eligibility_waits_for_freshness_and_schedule(tmp_path: Path) -> None: store = service._ctx.courier_store now = utcnow() - plain = store.queue(NODE, "YUL", "hi") + plain = store.queue(NODE, "Hub", "hi") assert not service.eligible(plain, now) # never heard this session service._heard[NODE] = now - timedelta(seconds=FRESH_S + 1) assert not service.eligible(plain, now) # heard, but too long ago service._heard[NODE] = now assert service.eligible(plain, now) - scheduled = store.queue(NODE, "YUL", "later", not_before=now + timedelta(hours=1)) + scheduled = store.queue(NODE, "Hub", "later", not_before=now + timedelta(hours=1)) assert not service.eligible(scheduled, now) # the schedule holds it # Past its time, a scheduled entry's *first* shot fires even unheard-of. service._heard.clear() @@ -139,7 +139,7 @@ def test_eligibility_backs_off_after_failures(tmp_path: Path) -> None: service = _service(tmp_path, []) store = service._ctx.courier_store now = utcnow() - entry = store.queue(NODE, "YUL", "hi") + entry = store.queue(NODE, "Hub", "hi") service._heard[NODE] = now store.note_attempt(entry.ident, when=now) entry = store.get(entry.ident) @@ -155,11 +155,11 @@ async def test_attempt_delivers_and_raises_the_good_news(tmp_path: Path) -> None """An acknowledged send settles the entry and lights the Watchtower badge.""" service = _service(tmp_path, [True]) ctx = service._ctx - entry = ctx.courier_store.queue(NODE, "YUL", "hello") + entry = ctx.courier_store.queue(NODE, "Hub", "hello") service._heard[NODE] = utcnow() await service._pass() - assert ctx.chat.sent == [("YUL", "hello")] + assert ctx.chat.sent == [("Hub", "hello")] settled = ctx.courier_store.get(entry.ident) assert settled.status == "delivered" and settled.attempts == 1 alerts = ctx.watch_store.alerts() @@ -171,7 +171,7 @@ async def test_attempt_gives_up_after_the_budget(tmp_path: Path) -> None: """Unacknowledged attempts exhaust the budget and say so, exactly once.""" service = _service(tmp_path, [False] * MAX_ATTEMPTS) ctx = service._ctx - entry = ctx.courier_store.queue(NODE, "YUL", "hello") + entry = ctx.courier_store.queue(NODE, "Hub", "hello") for _ in range(MAX_ATTEMPTS): message = ctx.courier_store.get(entry.ident) await service._attempt(message) @@ -185,8 +185,8 @@ async def test_pass_attempts_at_most_one_entry(tmp_path: Path) -> None: """A backlog drains one message per pass — the courier never bursts.""" service = _service(tmp_path, [True, True]) ctx = service._ctx - ctx.courier_store.queue(NODE, "YUL", "first") - ctx.courier_store.queue(NODE, "YUL", "second") + ctx.courier_store.queue(NODE, "Hub", "first") + ctx.courier_store.queue(NODE, "Hub", "second") service._heard[NODE] = utcnow() await service._pass() assert len(ctx.chat.sent) == 1 @@ -212,7 +212,7 @@ async def test_attempt_now_forces_a_send(tmp_path: Path) -> None: service = _service(tmp_path, [True]) ctx = service._ctx entry = ctx.courier_store.queue( - NODE, "YUL", "hello", not_before=utcnow() + timedelta(hours=8) + NODE, "Hub", "hello", not_before=utcnow() + timedelta(hours=8) ) assert await service.attempt_now(entry.ident) == "delivered" assert await service.attempt_now(entry.ident) == "gone" # already settled @@ -265,8 +265,8 @@ async def test_cli_list_renders_waiting_and_finished(tmp_path: Path) -> None: from meshterm.tools.courier import CourierTool ctx = _ToolCtx(tmp_path) - ctx.courier_store.queue(NODE, "YUL", "still waiting") - done = ctx.courier_store.queue(NODE, "YUL", "landed") + ctx.courier_store.queue(NODE, "Hub", "still waiting") + done = ctx.courier_store.queue(NODE, "Hub", "landed") ctx.courier_store.mark_delivered(done.ident) result = await CourierTool().run(ctx, {"cli_action": "list"}) @@ -289,7 +289,7 @@ async def test_cli_cancel_removes_a_waiting_entry(tmp_path: Path) -> None: from meshterm.tools.courier import CourierTool ctx = _ToolCtx(tmp_path) - entry = ctx.courier_store.queue(NODE, "YUL", "nope") + entry = ctx.courier_store.queue(NODE, "Hub", "nope") tool = CourierTool() result = await tool.run(ctx, {"cli_action": "cancel", "id": entry.ident}) @@ -305,7 +305,7 @@ async def test_cli_send_forces_one_attempt(tmp_path: Path) -> None: from meshterm.tools.courier import CourierTool ctx = _ToolCtx(tmp_path, outcome="delivered") - entry = ctx.courier_store.queue(NODE, "YUL", "hello") + entry = ctx.courier_store.queue(NODE, "Hub", "hello") result = await CourierTool().run(ctx, {"cli_action": "send", "id": entry.ident}) assert result.summary == {"id": entry.ident, "outcome": "delivered"} @@ -325,8 +325,8 @@ async def test_cli_clear_drops_finished_only(tmp_path: Path) -> None: from meshterm.tools.courier import CourierTool ctx = _ToolCtx(tmp_path) - ctx.courier_store.queue(NODE, "YUL", "still here") - done = ctx.courier_store.queue(NODE, "YUL", "gone soon") + ctx.courier_store.queue(NODE, "Hub", "still here") + done = ctx.courier_store.queue(NODE, "Hub", "gone soon") ctx.courier_store.mark_delivered(done.ident) result = await CourierTool().run(ctx, {"cli_action": "clear"}) @@ -377,7 +377,7 @@ async def test_pick_schedule_when_next_heard_returns_no_schedule() -> None: returns on Esc, so choosing it read as a cancel and the message was never queued. """ ctx = _ScheduleCtx(_StubScheduleSession(lambda items: items[0].value)) - result = await _pick_schedule(ctx, "YUL") + result = await _pick_schedule(ctx, "Hub") assert result is None assert result is not CANCEL_SCHEDULE assert WHEN_HEARD is not None # the row's own sentinel, distinct from Esc's None @@ -386,13 +386,13 @@ async def test_pick_schedule_when_next_heard_returns_no_schedule() -> None: async def test_pick_schedule_esc_cancels_the_queueing() -> None: """Esc on the picker (``select`` returns ``None``) backs out without queueing anything.""" ctx = _ScheduleCtx(_StubScheduleSession(lambda items: None)) - assert await _pick_schedule(ctx, "YUL") is CANCEL_SCHEDULE + assert await _pick_schedule(ctx, "Hub") is CANCEL_SCHEDULE async def test_pick_schedule_a_fixed_delay_holds_until_its_time() -> None: """A concrete offset row (e.g. In 1 h) comes back as its aware future datetime.""" ctx = _ScheduleCtx(_StubScheduleSession(lambda items: items[1].value)) # In 1 h - result = await _pick_schedule(ctx, "YUL") + result = await _pick_schedule(ctx, "Hub") assert result is not None and result is not CANCEL_SCHEDULE assert result > utcnow() @@ -417,15 +417,15 @@ def test_outbox_refresh_moves_a_delivered_entry_without_a_keypress(tmp_path: Pat from meshterm.ui.courier_screen import CourierOutboxScreen ctx = _outbox_ctx(tmp_path) - entry = ctx.courier_store.queue(NODE, "YUL", "hold this") + entry = ctx.courier_store.queue(NODE, "Hub", "hold this") screen = CourierOutboxScreen(ctx) before = _outbox_plain(screen) - assert "⏳ YUL" in before and "Finished" not in before + assert "⏳ Hub" in before and "Finished" not in before ctx.courier_store.mark_delivered(entry.ident) screen.refresh() after = _outbox_plain(screen) - assert "Finished" in after and "✓ YUL" in after and "⏳" not in after + assert "Finished" in after and "✓ Hub" in after and "⏳" not in after def test_outbox_refresh_keeps_the_highlight_on_its_entry(tmp_path: Path) -> None: @@ -433,8 +433,8 @@ def test_outbox_refresh_keeps_the_highlight_on_its_entry(tmp_path: Path) -> None from meshterm.ui.courier_screen import CourierOutboxScreen ctx = _outbox_ctx(tmp_path) - first = ctx.courier_store.queue(NODE, "YUL", "first") - second = ctx.courier_store.queue(NODE, "YUL", "second") + first = ctx.courier_store.queue(NODE, "Hub", "first") + second = ctx.courier_store.queue(NODE, "Hub", "second") screen = CourierOutboxScreen(ctx, default=("msg", second.ident)) assert screen._current_choice().value == ("msg", second.ident) @@ -448,7 +448,7 @@ def test_outbox_refresh_without_change_recomposes_nothing(tmp_path: Path) -> Non from meshterm.ui.courier_screen import CourierOutboxScreen ctx = _outbox_ctx(tmp_path) - ctx.courier_store.queue(NODE, "YUL", "steady") + ctx.courier_store.queue(NODE, "Hub", "steady") screen = CourierOutboxScreen(ctx) items = screen._items screen.refresh() @@ -460,7 +460,7 @@ def test_outbox_rows_recompute_live_state_per_repaint(tmp_path: Path) -> None: from meshterm.ui.courier_screen import CourierOutboxScreen ctx = _outbox_ctx(tmp_path) - entry = ctx.courier_store.queue(NODE, "YUL", "patience") + entry = ctx.courier_store.queue(NODE, "Hub", "patience") screen = CourierOutboxScreen(ctx) assert "waiting to hear the contact" in _outbox_plain(screen) diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index e27c5ad..8268c76 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -29,7 +29,7 @@ class _FakeSession: def _screen(window=None, histogram=None, kinds=None) -> DashboardScreen: screen = DashboardScreen( session=_FakeSession(), - resolve=lambda h: {"a1b2": "Alice", "3d63": "YUL"}.get(h, ""), + resolve=lambda h: {"a1b2": "Alice", "3d63": "Hub"}.get(h, ""), window=list(window or []), activity=lambda: tuple(histogram or (0,) * ACTIVITY_BUCKETS), activity_flags=lambda: (True,) * ACTIVITY_BUCKETS, diff --git a/tests/test_gallery.py b/tests/test_gallery.py index b0e65ec..b5e9f42 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -138,7 +138,7 @@ class _Entry: def _dashboard(cols: int, rows: int) -> Screen: return DashboardScreen( session=_GallerySession(cols, rows), - resolve=lambda h: {"a1b2c3d4": "Alice", "3d63c642": "YUL-Cartierville"}.get(h, ""), + resolve=lambda h: {"a1b2c3d4": "Alice", "3d63c642": "Hilltop-Repeater"}.get(h, ""), window=[ Observation(node="a1b2c3d4", name="a1b2c3d4", kind="advert", snr=5.0, rssi=-90.0, observed_at=utcnow()), @@ -182,7 +182,7 @@ def _purge_ranked(): # noqa: ANN201 dict(heard_age_days=95.0, packets=3, known_days=200.0, hops=2.0)), ("hop-9", "9a" * 32, dict(heard_age_days=400.0, packets=1, known_days=420.0, hops=4.0)), - ("YUL-Poly", "3d" * 32, dict(heard_age_days=30.0, packets=22, known_days=250.0, + ("Lakeside", "3d" * 32, dict(heard_age_days=30.0, packets=22, known_days=250.0, hops=1.0)), ("sensor-2", "7c" * 32, dict(heard_age_days=210.0, packets=2, known_days=260.0)), ] @@ -245,14 +245,14 @@ def _node_detail_header() -> Text: # a specimen of what the platform draws, so a stub colour would hide a palette bug. glyph, glyph_style = _NODE_GLYPHS[NODE_TYPE_REPEATER] header = Text(f"{glyph} ", style=glyph_style) - header.append("YUL-Cartierville", style=name_style("YUL-Cartierville", _HUB_KEY)) + header.append("Hilltop-Repeater", style=name_style("Hilltop-Repeater", _HUB_KEY)) header.append(" repeater", style="muted") return header def _node_detail(cols: int, rows: int) -> Screen: return NodeDetailScreen( - title="Node — YUL-Cartierville", + title="Node — Hilltop-Repeater", header=_node_detail_header(), info_rows=[ ("key", highlighted_hash(_HUB_KEY, 1)), @@ -291,7 +291,7 @@ def _map(cols: int, rows: int) -> Screen: session = _GallerySession(cols, rows) markers = [ MapMarker("Homestead", 45.50, -73.60, is_self=True), - MapMarker("YUL-Cartierville", 45.40, -73.50, is_repeater=True), + MapMarker("Hilltop-Repeater", 45.40, -73.50, is_repeater=True), MapMarker("A Rather Long Node Name For Width", 45.55, -73.65), ] return MapScreen(session, markers, _StubTileSource(), 14) @@ -323,7 +323,7 @@ def _livefeed(cols: int, rows: int) -> Screen: def _walk_topo() -> tuple[MeshTopology, dict[str, Contact]]: - hub = Contact(name="YUL-Cartierville", public_key=_HUB_KEY, key_prefix="3d63c6429436") + hub = Contact(name="Hilltop-Repeater", public_key=_HUB_KEY, key_prefix="3d63c6429436") far = Contact(name="Alice", public_key=_FAR_KEY, key_prefix="f2c24f54551e") topo = MeshTopology("aa" * 6, contacts=[hub, far]) hub_id, far_id = topo.canonical(hub.public_key), topo.canonical(far.public_key) @@ -342,10 +342,10 @@ def _walk(cols: int, rows: int) -> Screen: def _timemachine(cols: int, rows: int) -> Screen: def build(window, width): # noqa: ANN001 - return [Text("YUL-Cartierville 5m ago advert"), Text("Alice 12m ago packet")] + return [Text("Hilltop-Repeater 5m ago advert"), Text("Alice 12m ago packet")] return TimeMachineScreen( - session=_GallerySession(cols, rows), label="YUL-Cartierville", build=build, + session=_GallerySession(cols, rows), label="Hilltop-Repeater", build=build, ) @@ -364,14 +364,14 @@ def _message_paths(cols: int, rows: int) -> Screen: def _remote_cli(cols: int, rows: int) -> Screen: return RemoteCliScreen( - node_label="YUL-Cartierville", - history=["get name", "get name -> YUL-Cartierville"], + node_label="Hilltop-Repeater", + history=["get name", "get name -> Hilltop-Repeater"], send=lambda c: None, session=_GallerySession(cols, rows), ) def _path_composer(cols: int, rows: int) -> Screen: - hub = Contact(name="YUL-Cartierville", public_key=_HUB_KEY, key_prefix="3d63c6429436") + hub = Contact(name="Hilltop-Repeater", public_key=_HUB_KEY, key_prefix="3d63c6429436") far = Contact(name="Alice", public_key=_FAR_KEY, key_prefix="f2c24f54551e") topo = build_topology( self_id="aaaaaaaaaaaa" + "0" * 52, contacts=[hub, far], @@ -415,7 +415,7 @@ def _courier_outbox(cols: int, rows: int) -> Screen: config_dir = Path(tempfile.mkdtemp(prefix="meshterm-gallery-courier-")) ctx = _CourierStubContext(config_dir) ctx.courier = CourierService(ctx) - ctx.courier_store.queue("aa" * 6, "YUL-Cartierville", "battery reading requested please") + ctx.courier_store.queue("aa" * 6, "Hilltop-Repeater", "battery reading requested please") ctx.courier_store.queue("bb" * 6, "A Rather Long Contact Name For Width", "hello there") return CourierOutboxScreen(ctx) @@ -439,7 +439,7 @@ def _record_dialog(cols: int, rows: int) -> Screen: record = _record() return RecordDialog( record, CATEGORY_BY_ID[record.category], 1, - resolve=lambda h: {"3d63c6429436": "YUL-Cartierville", "f2c24f54551e": "Alice"}.get(h, h), + resolve=lambda h: {"3d63c6429436": "Hilltop-Repeater", "f2c24f54551e": "Alice"}.get(h, h), device_label="Homestead", device_hash=None, ) @@ -450,7 +450,7 @@ def _packet_viewer(cols: int, rows: int) -> Screen: raw={"payload_typename": "GRP_TXT", "route_typename": "FLOOD"}, ) return PacketViewer( - [entry], 0, resolve=lambda h: {"3d63c6429436": "YUL-Cartierville"}.get(h, h), + [entry], 0, resolve=lambda h: {"3d63c6429436": "Hilltop-Repeater"}.get(h, h), ) @@ -479,7 +479,7 @@ def _trace(cols: int, rows: int) -> Screen: def _tx_sweep(cols: int, rows: int) -> Screen: screen = TxSweepScreen( - admin_label="YUL-Cartierville", target_label="Alice", device_label="Homestead", + admin_label="Hilltop-Repeater", target_label="Alice", device_label="Homestead", device_hash="00" * 32, resolve=lambda h: h, session=_GallerySession(cols, rows), tx_min=12, tx_max=28, step=3, samples=3, run_sweep=lambda: None, apply_winner=lambda: None, diff --git a/tests/test_live_screens.py b/tests/test_live_screens.py index 0ef6ae0..1e2aa15 100644 --- a/tests/test_live_screens.py +++ b/tests/test_live_screens.py @@ -706,7 +706,7 @@ def test_route_lane_wraps_at_hop_boundaries_under_its_own_column() -> None: line it continues from ends on the ``→`` cue, and every name survives whole — the lane names nodes and leaves the hex to the ``path`` lane below it. """ - names = {"3d": "YUL-Cartierville", "f2": "Mile-End-Rooftop", "27": "Beaubien-Sud"} + names = {"3d": "Hilltop-Repeater", "f2": "Mile-End-Rooftop", "27": "Beaubien-Sud"} screen, _ = _trace_screen(mode="path") screen._resolve = lambda h: names.get(h, h) screen._path_spec = "3d,f2,27" @@ -1194,7 +1194,7 @@ def test_scenario_path_leads_and_ends_with_us_and_the_target() -> None: label="observed path", hops=("3d63c6429436",), source="observed", score=1.0, ) text = _scenario_path( - scenario, topo, "f2c24f54551e", device_label="YUL-Me", width_bytes=1 + scenario, topo, "f2c24f54551e", device_label="Hub-Me", width_bytes=1 ) assert text.plain == "★ → Hub → Far" @@ -1204,7 +1204,7 @@ def test_scenario_path_direct_scenario_still_names_both_endpoints() -> None: topo = _scenario_topo() scenario = PathScenario(label="direct", hops=(), source="direct", score=0.0) text = _scenario_path( - scenario, topo, "f2c24f54551e", device_label="YUL-Me", width_bytes=1 + scenario, topo, "f2c24f54551e", device_label="Hub-Me", width_bytes=1 ) assert text.plain == "★ → Far" @@ -1223,10 +1223,10 @@ def test_scenario_path_cuts_a_long_candidate_rather_than_eliding_its_middle() -> label="observed path", hops=("3d63c6429436",), source="observed", score=1.0, ) full = _scenario_path( - scenario, topo, "f2c24f54551e", device_label="YUL-Me", width_bytes=1 + scenario, topo, "f2c24f54551e", device_label="Hub-Me", width_bytes=1 ) narrow = _scenario_path( - scenario, topo, "f2c24f54551e", device_label="YUL-Me", width_bytes=1, + scenario, topo, "f2c24f54551e", device_label="Hub-Me", width_bytes=1, width=full.cell_len - 3, ) assert narrow.cell_len <= full.cell_len - 3 diff --git a/tests/test_livefeed.py b/tests/test_livefeed.py index f76aa7b..8ae30fa 100644 --- a/tests/test_livefeed.py +++ b/tests/test_livefeed.py @@ -50,7 +50,7 @@ class _Fut: #: Nodes the feed's resolver can name, by the hash a frame addresses them with (one byte) #: and by the wider prefixes an advert carries. -_KNOWN = {"a1b2": "Alice", "3d63": "YUL", "a1": "Alice", "3d": "YUL", "c0": "Us"} +_KNOWN = {"a1b2": "Alice", "3d63": "Hub", "a1": "Alice", "3d": "Hub", "c0": "Us"} def _screen(seed=None, **kwargs) -> LiveFeedScreen: @@ -127,7 +127,7 @@ def test_livefeed_rows_leave_the_relay_path_to_the_viewer() -> None: MeshEvent.observation_event(_obs(kind="packet", path="3d63,a1b2", snr=1.0)) ) row = _rows(screen, 72)[0] - assert "via" not in row and "YUL" not in row # no route, not even a stub of one + assert "via" not in row and "Hub" not in row # no route, not even a stub of one assert "📦 packet" in row # the class still reads in words at 72 assert "+1.0 dB" in row and "-90 dBm" in row # …as does the reception it was heard at assert len(row.rstrip()) <= 72 @@ -222,14 +222,14 @@ def test_livefeed_addressed_frame_is_about_its_two_ends() -> None: # A pair too wide for the lane spends its cells on the addressee: the sender drops to # the hash it was named by rather than the recipient being the half cut off. wide = _screen() - wide._resolve = lambda h: {"a1": "Alice-With-A-Long-Name", "3d": "YUL-Cartierville"}.get(h, "") + wide._resolve = lambda h: {"a1": "Alice-With-A-Long-Name", "3d": "Hilltop-Repeater"}.get(h, "") wide.on_event( MeshEvent.observation_event( _obs(node="", kind="packet", raw={"payload_typename": "REQ", "dest_hash": "3d", "src_hash": "a1"}) ) ) - assert "a1 → YUL-Cartierv" in _rows(wide, 100)[0] + assert "a1 → Hilltop-Repe" in _rows(wide, 100)[0] def test_livefeed_tokened_classes_are_about_their_token() -> None: @@ -265,7 +265,7 @@ def test_livefeed_column_header_sits_over_the_lanes_it_names() -> None: assert stamp is not None assert _col(header, "TIME") == _col(row, stamp.group(0)) assert _col(header, "CLASS") == _col(row, "telemetry") - _ICON_LANE - assert _col(header, "SUBJECT") == _col(row, "YUL") + assert _col(header, "SUBJECT") == _col(row, "Hub") # The two readings right-align their number, so their labels end where the digits do. assert _col(header, "SNR") + 3 == _col(row, "+12.8") + 5 assert _col(header, "RSSI") + 4 == _col(row, "-105") + 4 diff --git a/tests/test_map.py b/tests/test_map.py index 908b9b4..113dcf8 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -514,7 +514,7 @@ def test_street_name_is_drawn_only_once() -> None: layers = decode_tile(_FIXTURE.read_bytes(), layers=DRAWN_LAYERS) vp = Viewport(45.5019, -73.5674, 16, 53 * 2, 22 * 4) out = _plain(render_map(vp, {(14, 4843, 5861): layers}, - [MapMarker("YUL", 45.5040, -73.5700, is_repeater=True)])) + [MapMarker("Hub", 45.5040, -73.5700, is_repeater=True)])) # René-Lévesque arrives as several segments and used to be drawn twice on one screen. assert out.count("René-Lévesque") <= 1, "a street was named more than once" @@ -1413,7 +1413,7 @@ def test_map_locate_recenters_on_our_own_node_at_the_current_zoom() -> None: from meshterm.ui.map_screen import MapScreen markers = [ - MapMarker("YUL-Cartierville", 45.53, -73.71, is_repeater=True), + MapMarker("Hilltop-Repeater", 45.53, -73.71, is_repeater=True), MapMarker("Homestead", 45.40, -73.50, is_self=True), ] screen = MapScreen(_StubSession(80, 24), markers, _StubSource(), 14) @@ -1442,32 +1442,32 @@ def test_map_echoes_the_find_query_in_the_body_only_where_the_footer_is_gone() - from meshterm.ui.map_render import MapMarker from meshterm.ui.map_screen import MapScreen - markers = [MapMarker("YUL-Cartierville", 45.53, -73.71, is_repeater=True)] + markers = [MapMarker("Hilltop-Repeater", 45.53, -73.71, is_repeater=True)] try: set_platform(REGULAR) desktop = MapScreen(_StubSession(72, 20), markers, _StubSource(), 14) assert len(desktop.render_body(72)) == 20 - for ch in "yul": + for ch in "hil": desktop.handle("text", ch) # Unchanged: the footer already shows it, so the canvas keeps the whole body. - assert "/yul" not in _plain(desktop.render_body(72)) + assert "/hil" not in _plain(desktop.render_body(72)) assert len(desktop.render_body(72)) == 20 - assert "find: yul" in desktop.footer_hint + assert "find: hil" in desktop.footer_hint set_platform(PICOCALC) device = MapScreen(_StubSession(53, 23), markers, _StubSource(), 14) assert len(device.render_body(53)) == 23 - for ch in "yul": + for ch in "hil": device.handle("text", ch) lines = device.render_body(53) # The echo overlays the canvas's *last* row — directly above the F-key lane — # so the ground never shifts: same body height, same viewport, top anchor held. - assert _plain(lines[-1]).strip() == "/yul" - assert "/yul" not in _plain(lines[0]) + assert _plain(lines[-1]).strip() == "/hil" + assert "/hil" not in _plain(lines[0]) assert len(lines) == 23 # Clearing the find hands the row back to the canvas, still without a reflow. - for _ in "yul": + for _ in "hil": device.handle("backspace") assert len(device.render_body(53)) == 23 assert "/" not in _plain(device.render_body(53)) @@ -1505,7 +1505,7 @@ def test_map_frame_chip_lights_only_with_matches_to_frame() -> None: from meshterm.ui.map_screen import MapScreen markers = [ - MapMarker("YUL-Cartierville", 45.53, -73.71, is_repeater=True), + MapMarker("Hilltop-Repeater", 45.53, -73.71, is_repeater=True), MapMarker("Alice", 45.40, -73.50), ] screen = MapScreen(_StubSession(80, 24), markers, _StubSource(), 14) @@ -1533,31 +1533,31 @@ def test_map_screen_find_filters_frames_and_clears() -> None: from meshterm.ui.map_screen import MapScreen markers = [ - MapMarker("YUL-Cartierville", 45.53, -73.71, is_repeater=True), + MapMarker("Hilltop-Repeater", 45.53, -73.71, is_repeater=True), MapMarker("Alice", 45.40, -73.50), MapMarker("Yagi-North", 45.60, -73.65), ] screen = MapScreen(_StubSession(80, 24), markers, _StubSource(), 14) screen.render_body(80) - for ch in "yu": + for ch in "hi": screen.handle("text", ch) - assert screen._filter == "yu" - assert [m.label for m in screen._matches()] == ["YUL-Cartierville"] + assert screen._filter == "hi" + assert [m.label for m in screen._matches()] == ["Hilltop-Repeater"] screen.render_body(80) assert "1 of 3 match" in screen.title - assert "find: yu" in screen.footer_hint + assert "find: hi" in screen.footer_hint # Only the match keeps a label; the others dim to bare context glyphs. body = _plain(screen.render_body(80)) - assert "YUL-Cartierville" in body + assert "Hilltop-Repeater" in body assert "Alice" not in body and "Yagi-North" not in body - # ^Enter frames the matches: the view centres on YUL, and the query survives it. + # ^Enter frames the matches: the view centres on the repeater, and the query survives. screen.handle("ctrl_enter") assert screen._viewport.center_lat == pytest.approx(45.53, abs=0.05) assert screen._viewport.center_lon == pytest.approx(-73.71, abs=0.05) - assert screen._filter == "yu" + assert screen._filter == "hi" # Plain Enter is the other way out: the query goes, the view stays exactly where the # frame left it. @@ -1567,10 +1567,10 @@ def test_map_screen_find_filters_frames_and_clears() -> None: assert (screen._viewport.center_lat, screen._viewport.center_lon) == centre # Backspace edits; Esc clears the filter first and only then dismisses. - for ch in "yu": + for ch in "hi": screen.handle("text", ch) screen.handle("backspace") - assert screen._filter == "y" + assert screen._filter == "h" async def drive() -> object: screen.future = asyncio.get_running_loop().create_future() @@ -1588,14 +1588,14 @@ def test_map_screen_frame_zooms_in_on_a_single_match() -> None: from meshterm.ui.map_screen import _FIND_ZOOM, MapScreen markers = [ - MapMarker("YUL-Cartierville", 45.53, -73.71, is_repeater=True), + MapMarker("Hilltop-Repeater", 45.53, -73.71, is_repeater=True), MapMarker("Alice", 45.40, -73.50), ] screen = MapScreen(_StubSession(80, 24), markers, _StubSource(), 19) # source max 19 screen.render_body(80) - for ch in "yul": + for ch in "hil": screen.handle("text", ch) - assert [m.label for m in screen._matches()] == ["YUL-Cartierville"] + assert [m.label for m in screen._matches()] == ["Hilltop-Repeater"] screen.handle("ctrl_enter") assert screen._viewport.center_lat == pytest.approx(45.53, abs=0.01) assert screen._viewport.center_lon == pytest.approx(-73.71, abs=0.01) diff --git a/tests/test_message_paths_screen.py b/tests/test_message_paths_screen.py index 1b6fd63..a865919 100644 --- a/tests/test_message_paths_screen.py +++ b/tests/test_message_paths_screen.py @@ -17,7 +17,7 @@ from meshterm.ui.pathline import CRACK_HEAD, CRACK_TAIL def _resolve(hop: str) -> str: - return {"3d63": "YUL-Cartierville", "a1b2": "Waymarker"}.get(hop, hop) + return {"3d63": "Hilltop-Repeater", "a1b2": "Waymarker"}.get(hop, hop) from tests.conftest import plain as _plain # THE strip-and-join screen reader @@ -53,7 +53,7 @@ def test_paths_screen_renders_graph_rows_and_cursor() -> None: assert "via" not in body # the route lane holds nothing but the route rows = body.split("sensor\n\n")[1].split("\n") assert len(rows) == 2 * len(_arrivals()) - assert rows[0].startswith("❯ ") and "YUL-Cartierville" in rows[0] # the picked route + assert rows[0].startswith("❯ ") and "Hilltop-Repeater" in rows[0] # the picked route # …with its length, time and SNR tucked beneath it, the hop count leading assert rows[1].strip().startswith("1 hop ") assert _arrivals()[0].when.astimezone().strftime("%H:%M") in rows[1] @@ -77,7 +77,7 @@ def test_paths_screen_warns_once_for_a_path_that_revisits_a_hop() -> None: ]) body = _plain(screen.render_body(76)) assert body.count("⚠") == 1 - assert "YUL-Cartierville repeats — a loop, or two nodes sharing one hash" in body + assert "Hilltop-Repeater repeats — a loop, or two nodes sharing one hash" in body graph = body.split("origin →")[0] assert graph.count("3d") == 2 # both visits marked assert graph.count("a1") == 1 # the relay the two paths *share* stays one marker @@ -97,8 +97,8 @@ def test_paths_screen_labels_every_relay_with_its_hash_byte() -> None: graph = body.split("origin →")[0] for byte in ("3d", "a1", "77"): # every relay labelled, selected or not assert byte in graph - assert "YUL-Cartier" not in graph and "Waymarker" not in graph - assert "YUL-Cartierville" in body and "Waymarker" in body + assert "Hilltop-Rep" not in graph and "Waymarker" not in graph + assert "Hilltop-Repeater" in body and "Waymarker" in body assert "(3d)" not in body and "(a1)" not in body # the hex lives on the graph @@ -222,7 +222,7 @@ def test_paths_screen_route_runs_origin_to_us_not_relay_to_relay() -> None: """ screen = _screen(_arrivals()) rows = _plain(screen.render_body(76)).split("sensor\n\n")[1].split("\n") - assert rows[0] == "❯ Alice → YUL-Cartierville → ★" + assert rows[0] == "❯ Alice → Hilltop-Repeater → ★" assert rows[2] == " Alice → Waymarker → 77 → ★" # an unnamed relay still stands in its hash @@ -232,9 +232,9 @@ def test_paths_screen_origin_is_a_star_for_us_and_a_question_for_nobody() -> Non now = utcnow() one = [Arrival(when=now, hops=("3d63",), snr=1.0)] ours = _plain(_screen(one, source="Homestead").render_body(76)) - assert "❯ ★ → YUL-Cartierville → ★" in ours + assert "❯ ★ → Hilltop-Repeater → ★" in ours nameless = _plain(_screen(one, source=None).render_body(76)) - assert "❯ ? → YUL-Cartierville → ★" in nameless + assert "❯ ? → Hilltop-Repeater → ★" in nameless def test_paths_screen_cuts_unselected_rows_the_same_way(monkeypatch) -> None: # noqa: ANN001 @@ -293,7 +293,7 @@ def test_an_outgoing_message_ends_on_its_recipient_not_on_us() -> None: summary="heard once", source="Homestead", destination="Bob", ) body = _plain(screen.render_body(72)) - assert "YUL-Cartierville" in body + assert "Hilltop-Repeater" in body assert body.count("Homestead") == 1, "our name belongs at one end of a send, not both" assert "Bob" in body, "the far end is the recipient" # The caption names the same far end the line ends on. diff --git a/tests/test_navigation.py b/tests/test_navigation.py index 8b7e57e..953b5bb 100644 --- a/tests/test_navigation.py +++ b/tests/test_navigation.py @@ -146,8 +146,8 @@ async def test_the_trace_target_picker_stays_pushed_under_the_live_screen(monkey from meshterm.ui.surface import TuiUi contacts = [ - Contact(name="YUL-Poly", public_key="a1" + "0" * 62, key_prefix="a1"), - Contact(name="YUL-Cartierville", public_key="3d" + "0" * 62, key_prefix="3d"), + Contact(name="Lakeside", public_key="a1" + "0" * 62, key_prefix="a1"), + Contact(name="Hilltop-Repeater", public_key="3d" + "0" * 62, key_prefix="3d"), ] class _Repo: @@ -221,10 +221,10 @@ async def test_the_tx_optimize_pickers_stay_pushed_under_the_sweep(monkeypatch) contacts = [ Contact( - name="YUL-Cartierville", public_key="3d" + "0" * 62, key_prefix="3d", + name="Hilltop-Repeater", public_key="3d" + "0" * 62, key_prefix="3d", node_type=NODE_TYPE_REPEATER, ), - Contact(name="YUL-Poly", public_key="a1" + "0" * 62, key_prefix="a1"), + Contact(name="Lakeside", public_key="a1" + "0" * 62, key_prefix="a1"), ] class _Ctx: @@ -248,9 +248,9 @@ async def test_the_tx_optimize_pickers_stay_pushed_under_the_sweep(monkeypatch) async def main() -> None: run = asyncio.ensure_future(TxOptimizeTool()._run_live(ctx)) node_list = await _screen_at(session, 1) - node_list.resolve("YUL-Cartierville") + node_list.resolve("Hilltop-Repeater") target_list = await _screen_at(session, 2) - target_list.resolve("YUL-Poly") + target_list.resolve("Lakeside") while not swept: await asyncio.sleep(0) # Esc from the sweep leaves the target list up; Esc there, the node list. @@ -261,7 +261,7 @@ async def test_the_tx_optimize_pickers_stay_pushed_under_the_sweep(monkeypatch) await asyncio.wait_for(session.run(main()), timeout=5) - assert swept == [("YUL-Cartierville", "YUL-Poly", 2)], "both pickers under the sweep" + assert swept == [("Hilltop-Repeater", "Lakeside", 2)], "both pickers under the sweep" assert session._stack == [], "and both visits pop on the way out" diff --git a/tests/test_packet_viewer.py b/tests/test_packet_viewer.py index b3552c5..2ee31a0 100644 --- a/tests/test_packet_viewer.py +++ b/tests/test_packet_viewer.py @@ -319,16 +319,16 @@ def test_packet_viewer_from_row_leads_with_the_node_type_mark() -> None: return next(l for l in _stripped(viewer.render_body(80)) if l.startswith("from")) advertised = PacketEntry( - when=utcnow(), kind="advert", node="3d63", name="YUL", node_type=NODE_TYPE_REPEATER + when=utcnow(), kind="advert", node="3d63", name="Hub", node_type=NODE_TYPE_REPEATER ) - assert "▲ YUL" in from_row(_viewer(advertised)) # the type the packet itself carried + assert "▲ Hub" in from_row(_viewer(advertised)) # the type the packet itself carried - bare = PacketEntry(when=utcnow(), kind="packet", node="3d63", name="YUL") - assert "○ YUL" in from_row(_viewer(bare)) # nothing can type it: the unknown ring + bare = PacketEntry(when=utcnow(), kind="packet", node="3d63", name="Hub") + assert "○ Hub" in from_row(_viewer(bare)) # nothing can type it: the unknown ring typed = PacketViewer( [bare], 0, resolve=lambda h: "", type_of=lambda h: NODE_TYPE_REPEATER ) - assert "▲ YUL" in from_row(typed) # …until the contacts can + assert "▲ Hub" in from_row(typed) # …until the contacts can mine = PacketEntry(when=utcnow(), kind="advert", node="3d63", name="Waymarker") ours = PacketViewer([mine], 0, resolve=lambda h: "", self_name="Waymarker") diff --git a/tests/test_pathgraph.py b/tests/test_pathgraph.py index 07eebce..e4b4acf 100644 --- a/tests/test_pathgraph.py +++ b/tests/test_pathgraph.py @@ -413,7 +413,7 @@ def test_detour_nests_outside_its_sibling_when_rows_allow() -> None: ``cc`` inserts a relay before the ``bb`` its sibling also converges through. With rows to spare the detour *nests*: ``cc`` takes the lane just outside ``bb``'s on the same flank, so the sibling's straight SRC → bb run never passes over ``cc``'s marker and the detour reads - as the wider arc through ``cc`` it really is. This is the real YUL-Poly shape: + as the wider arc through ``cc`` it really is. This is the real Lakeside shape: CDN-FENDALL1 → UpperSalaberry. """ layers = [ @@ -431,7 +431,7 @@ def test_detour_nests_outside_its_sibling_when_rows_allow() -> None: def test_detour_folds_onto_its_siblings_lane_when_rows_are_tight() -> None: """The nested lane is given up — cc rides bb's lane — only when the rows can't afford it. - The same YUL-Poly shape at a row budget too short for the nested third lane falls back to + The same Lakeside shape at a row budget too short for the nested third lane falls back to the single-lane picture: ``cc`` is re-owned onto ``bb``'s lane as a waypoint the branch dips through, and the band stays two lanes tall. The last resort, not the default. """ @@ -649,7 +649,7 @@ def test_subset_route_bypasses_the_relay_it_skips() -> None: The spine runs SRC → c1 → c2 → us with a balanced flank each side, so the endpoints ride the spine's own lane and the subset route's SRC → c2 edge is a level run straight through - ``c1``'s marker — the Furthur → YUL-Cartierville shape, where the selected route lit the + ``c1``'s marker — the Furthur → Hilltop-Repeater shape, where the selected route lit the spine's run through C14903 it never rides. The edge must bend around ``c1`` through a virtual waypoint in its column — and on the flank ``x1`` does *not* occupy, since ``x1`` shares ``c1``'s column and its lane there is taken. diff --git a/tests/test_pathline.py b/tests/test_pathline.py index 6f511e6..34305c3 100644 --- a/tests/test_pathline.py +++ b/tests/test_pathline.py @@ -66,11 +66,11 @@ def test_plain_mode_annotation_dim_and_custom_separator() -> None: """The trace ``(3d)`` note rides muted; a dim hop (and the arrow into it) fades; a surface's own separator — the mesh walk trail's ``›`` — passes straight through.""" line = PathLine( - [PathHop("YUL", key="3d", annotation="3d"), PathHop("home", you=True, dim=True)], + [PathHop("Hub", key="3d", annotation="3d"), PathHop("home", you=True, dim=True)], mode="plain", ) text = line.text() - assert text.plain == "YUL (3d) → home" + assert text.plain == "Hub (3d) → home" styles = _styles(text) assert styles[" (3d)"] == "muted" # the annotation note, space included assert styles["home"] == "faint" @@ -107,7 +107,7 @@ def test_chips_are_joined_by_one_interlocked_chevron() -> None: def test_chips_keep_the_same_words_and_honour_style_overrides() -> None: """Chips change colours and separators, never the words; an explicit style override (hex or theme name) becomes the chip fill.""" - hops = [PathHop("YUL", key="3d", annotation="3d"), PathHop("you", you=True)] + hops = [PathHop("Hub", key="3d", annotation="3d"), PathHop("you", you=True)] plain = PathLine(hops, mode="plain").text().plain chips = PathLine(hops, mode="powerline").text().plain assert plain.replace(" → ", " ") == chips.replace(POWERLINE_SEP, "").replace(" ", " ").strip() @@ -378,7 +378,7 @@ def test_path_line_factory_matches_path_text_character_for_character() -> None: """The migration bridge: the trace flavour — device endpoints with annotated hashes, a named hop, a prefix-lit unnamed hop, a dimmed tail — renders through ``path_line`` exactly as ``path_text`` renders it, character and style alike.""" - names = {"aa11bb": "Alice", "3d63ab": "YUL"} + names = {"aa11bb": "Alice", "3d63ab": "Hub"} hops = [None, "aa11bb", "77ccddee", "3d63ab", None] kwargs = dict( prefix_bytes=2, self_name="Me", show_hash=True, hash_bytes=3, @@ -392,7 +392,7 @@ def test_path_line_factory_matches_path_text_character_for_character() -> None: def test_path_line_factory_matches_path_text_hash_as_name_flavour() -> None: """The message-paths flavour: an unnamed hop standing as its own muted identity hash, annotated with its addressed byte — same parity guarantee.""" - names = {"3d63abcdef00": "YUL"} + names = {"3d63abcdef00": "Hub"} hops = ["3d63abcdef00", "e839f2aabb11"] kwargs = dict(prefix_bytes=3, show_hash=True, hash_bytes=1, hash_as_name=True) old = path_text(hops, lambda h: names.get(h, h), **kwargs) diff --git a/tests/test_picocalc_chrome.py b/tests/test_picocalc_chrome.py index d4e6e12..eb2e4ae 100644 --- a/tests/test_picocalc_chrome.py +++ b/tests/test_picocalc_chrome.py @@ -125,15 +125,15 @@ def test_the_esc_hint_gives_way_before_it_crowds_the_title() -> None: Two rungs down, in order — a long title keeps the key without its verb, a longer one takes the cells back altogether. The title is what the reader came for. """ - verbless = _bar("Trace — YUL-Cartierville over a spec", "↑↓ move · Esc back") + verbless = _bar("Trace — Hilltop-Repeater over a spec", "↑↓ move · Esc back") assert verbless.rstrip().endswith(" Esc") # the verb went, the key stayed assert "Esc back" not in verbless - crowded = _bar("Trace — YUL-Cartierville over a longer spec", "↑↓ move · Esc back") + crowded = _bar("Trace — Hilltop-Repeater over a longer spec", "↑↓ move · Esc back") assert "Esc" not in crowded for bar in (verbless, crowded): - assert "YUL-Cartierville" in bar and cell_len(bar) <= 53 + assert "Hilltop-Repeater" in bar and cell_len(bar) <= 53 def test_bordered_frame_is_unchanged_on_regular() -> None: diff --git a/tests/test_records_screen.py b/tests/test_records_screen.py index b787c5b..8849c72 100644 --- a/tests/test_records_screen.py +++ b/tests/test_records_screen.py @@ -50,7 +50,7 @@ from tests.conftest import plain as _plain # THE strip-and-join screen reader def _resolve(hop: str) -> str: - return {HUB_ID: "YUL-Cartierville", FAR_ID: "Far"}.get(hop, hop) + return {HUB_ID: "Hilltop-Repeater", FAR_ID: "Far"}.get(hop, hop) def _record(**kw) -> DiscoveredPath: @@ -79,7 +79,7 @@ def test_record_dialog_shows_stats_route_and_the_trace_action() -> None: """Score in the category's unit, the resolved route bracketed by us, and actions.""" body = _plain(_dialog(_record()).render_body(60)) assert "2 nodes" in body # the score, in the discipline's own unit - assert "YUL-Cartierville" in body # a resolved relay on the route + assert "Hilltop-Repeater" in body # a resolved relay on the route assert "Homestead" in body # us, bracketing the walked route assert "Trace this path" in body # the renamed action (was "Walk again") assert "Walk again" not in body @@ -126,7 +126,7 @@ def test_record_dialog_route_runs_unlabelled_across_the_whole_card() -> None: assert route[0].startswith("★") # our end opens the walk on the app-wide star… assert route[-1].endswith("★") # …and closes it on the same assert "Homestead" not in "".join(route) # never our name, and never our key - assert "YUL-Cartierville" in "".join(route) # the hops themselves are named in full + assert "Hilltop-Repeater" in "".join(route) # the hops themselves are named in full assert len(route) > 1 # it folded rather than truncating… # …every fold hanging under the step, and the labelled lanes resume at the spec. assert all(line.startswith(" ") for line in route[1:]) @@ -202,7 +202,7 @@ def test_record_dialog_names_the_link_the_longest_leg_spanned() -> None: "km_complete": True, "leg_km": 12.4, "leg_link": [HUB_ID, FAR_ID]}, ) body = _plain(_dialog(record).render_body(60)) - assert re.search(r"longest leg\s+12\.4 km\s+YUL-Cartierville → Far", body) + assert re.search(r"longest leg\s+12\.4 km\s+Hilltop-Repeater → Far", body) def test_record_dialog_stars_our_own_end_of_the_longest_leg() -> None: @@ -216,7 +216,7 @@ def test_record_dialog_stars_our_own_end_of_the_longest_leg() -> None: line for line in _plain(_dialog(record).render_body(60)).splitlines() if "longest leg" in line ) - assert "★ → YUL-Cartierville" in lane and "Homestead" not in lane + assert "★ → Hilltop-Repeater" in lane and "Homestead" not in lane def test_record_dialog_skips_the_leg_lane_for_a_record_stored_without_one() -> None: diff --git a/tests/test_repeater_admin.py b/tests/test_repeater_admin.py index 89c2278..515e1b7 100644 --- a/tests/test_repeater_admin.py +++ b/tests/test_repeater_admin.py @@ -193,11 +193,11 @@ def test_picker_rows_hue_each_name_by_its_own_key(tui_ctx) -> None: from meshterm.ui.widgets import _NODE_GLYPHS contacts = [ - Contact(name="YUL-North", public_key="a1" * 32, key_prefix="a1b2c3d4", node_type=2), - Contact(name="YUL-South", public_key="d4" * 32, key_prefix="d4c3b2a1", node_type=2), + Contact(name="Hub-North", public_key="a1" * 32, key_prefix="a1b2c3d4", node_type=2), + Contact(name="Hub-South", public_key="d4" * 32, key_prefix="d4c3b2a1", node_type=2), ] rows, candidates = admin_picker_rows(tui_ctx, contacts) - assert [c.name for c in candidates] == ["YUL-North", "YUL-South"] + assert [c.name for c in candidates] == ["Hub-North", "Hub-South"] glyph, glyph_style = _NODE_GLYPHS[2] titles = { diff --git a/tests/test_selection_colour.py b/tests/test_selection_colour.py index 750d7b6..2de443a 100644 --- a/tests/test_selection_colour.py +++ b/tests/test_selection_colour.py @@ -48,7 +48,7 @@ def _row(*, selected: bool) -> Text: """A list row in the shape every screen builds: pointer, name, age, key lane.""" row = Text() row.append("❯ " if selected else " ", style="cursor" if selected else "") - row.append("YUL-Poly", style=name_style("YUL-Poly", _KEY)) + row.append("Lakeside", style=name_style("Lakeside", _KEY)) row.append(" ") row.append("5m", style="heat.minutes") row.append(" ") @@ -72,7 +72,7 @@ def _hues(ansi: str) -> set[str]: def test_a_selected_row_draws_its_node_name_in_the_cursor_white() -> None: """THE ask: the row you are on says "this one", not "this one is mauve".""" - hue = name_style("YUL-Poly", _KEY).removeprefix("bold ") + hue = name_style("Lakeside", _KEY).removeprefix("bold ") hues = _hues(render_to_ansi(_row(selected=True), 60)) @@ -82,7 +82,7 @@ def test_a_selected_row_draws_its_node_name_in_the_cursor_white() -> None: def test_an_unselected_row_keeps_every_name_in_its_own_hue() -> None: """The fold is the *highlight's* doing; the column below it still reads as identities.""" - hue = name_style("YUL-Poly", _KEY).removeprefix("bold ") + hue = name_style("Lakeside", _KEY).removeprefix("bold ") assert hue in _hues(render_to_ansi(_row(selected=False), 60)) @@ -140,7 +140,7 @@ def test_a_path_line_s_chips_keep_their_fills_on_the_cursor_row() -> None: """ fill = node_style(_KEY).removeprefix("bold ") row = Text(" ") - row.append(" YUL-Poly ", style=f"#0f172a on {fill}") + row.append(" Lakeside ", style=f"#0f172a on {fill}") row.style = "cursor" assert f"48;2;{int(fill[1:3], 16)};" in render_to_ansi(row, 40) @@ -155,7 +155,7 @@ def test_a_path_line_keeps_every_hop_s_hue_on_the_cursor_row() -> None: different things on the two platforms. The widget stamps its own extent and the fold spares what lies inside it. """ - hops = [PathHop("YUL-Poly", key=_KEY), PathHop("Waymarker", key="77" * 6)] + hops = [PathHop("Lakeside", key=_KEY), PathHop("Waymarker", key="77" * 6)] row = Text("❯ ", style="cursor") row.append_text(PathLine(hops, mode="plain").text()) row.style = "cursor" @@ -169,14 +169,14 @@ def test_a_path_line_keeps_every_hop_s_hue_on_the_cursor_row() -> None: def test_a_name_beside_a_path_line_still_folds() -> None: """The exception is the route, not the row: a name lane next to one is still a name.""" row = Text(" ") - row.append("YUL-Poly", style=name_style("YUL-Poly", _KEY)) + row.append("Lakeside", style=name_style("Lakeside", _KEY)) row.append(" ") row.append_text(PathLine([PathHop("Waymarker", key="77" * 6)], mode="plain").text()) row.style = "cursor" hues = _hues(render_to_ansi(row, 60)) - assert name_style("YUL-Poly", _KEY).removeprefix("bold ") not in hues # the lane folded + assert name_style("Lakeside", _KEY).removeprefix("bold ") not in hues # the lane folded assert node_style("77").removeprefix("bold ") in hues # …the route did not @@ -203,7 +203,7 @@ def test_the_message_paths_dialog_colours_its_picked_route_like_its_graph(platfo ChatMessage(text="on my way", is_channel=True, created_at=now), arrivals, matched=True, - resolve=lambda hop: {"3d63": "YUL-Cartierville", "a1b2": "Waymarker"}.get(hop, hop), + resolve=lambda hop: {"3d63": "Hilltop-Repeater", "a1b2": "Waymarker"}.get(hop, hop), prefix_bytes=1, self_name="Homestead", summary="heard twice", source="Alice", ) picked = next(line for line in screen.render_body(72) if "❯" in line) @@ -245,18 +245,18 @@ def test_the_fold_speaks_whichever_hue_vocabulary_is_bound(platform) -> None: # """PicoCalc quantizes the hue to a palette slot; the rule above it is the same rule.""" set_platform(platform) row = Text(" ") - row.append("YUL-Poly", style=name_style("YUL-Poly", _KEY)) + row.append("Lakeside", style=name_style("Lakeside", _KEY)) row.style = "cursor" lit = render_to_ansi(row, 30) - unlit = render_to_ansi(Text(" YUL-Poly"), 30) + unlit = render_to_ansi(Text(" Lakeside"), 30) assert lit != unlit - assert "YUL-Poly" in re.sub(r"\x1b\[[\d;]*m", "", lit) + assert "Lakeside" in re.sub(r"\x1b\[[\d;]*m", "", lit) if platform is PICOCALC: - assert "\x1b[1;97mYUL-Poly" in lit, lit # slot 15, the console's white + assert "\x1b[1;97mLakeside" in lit, lit # slot 15, the console's white else: - assert "38;2;255;255;255mYUL-Poly" in lit, lit + assert "38;2;255;255;255mLakeside" in lit, lit # -- the vocabulary itself ------------------------------------------------------------ diff --git a/tests/test_theme16.py b/tests/test_theme16.py index 1c7bff5..94d4fd0 100644 --- a/tests/test_theme16.py +++ b/tests/test_theme16.py @@ -222,12 +222,12 @@ def test_names_colour_by_key_on_both_platforms() -> None: """One rule everywhere: the key picks the hue, and only its first byte does.""" for platform in (REGULAR, PICOCALC): set_platform(platform) - hue = name_style("YUL-Cartierville", "3d63c6429436") + hue = name_style("Hilltop-Repeater", "3d63c6429436") assert hue.startswith("bold #"), platform.name # Any prefix of the key agrees, a rename does not move the colour, and a sender # we could not place falls to the unknown-node grey — colour is reserved for # keyed identities. - assert name_style("YUL-Cartierville", "3d") == hue + assert name_style("Hilltop-Repeater", "3d") == hue assert name_style("renamed", "3d63c6429436") == hue assert name_style("nameless", None) == "node.unknown" diff --git a/tests/test_timemachine.py b/tests/test_timemachine.py index 5dee91e..6536de1 100644 --- a/tests/test_timemachine.py +++ b/tests/test_timemachine.py @@ -48,7 +48,7 @@ def _seeded_repo(tmp_path: Path) -> Repository: repo.record_observation( run, Observation( - node=NODE, name="YUL", kind="advert", + node=NODE, name="Hub", kind="advert", snr=8.0 if hours_ago > 24 else -2.0, rssi=-95.0, observed_at=now - timedelta(hours=hours_ago), ), @@ -107,7 +107,7 @@ def test_daily_activity_counts_packets_and_nodes(tmp_path: Path) -> None: repo = _seeded_repo(tmp_path) days = repo.daily_activity() assert sum(d[1] for d in days) == 26 # 24 adverts + newcomer + the packet row - assert max(d[2] for d in days) == 2 # at most YUL + Newcomer on one day + assert max(d[2] for d in days) == 2 # at most Hub + Newcomer on one day assert days == sorted(days) # oldest first repo.close() @@ -413,7 +413,7 @@ def test_node_page_renders_all_sections(tmp_path: Path) -> None: """Volume, SNR band, rhythm, and the record roll-up all render.""" repo = _seeded_repo(tmp_path) ctx = SimpleNamespace(repo=repo) - body = _plain(_node_sections(ctx, NODE, "YUL", None, 90)) + body = _plain(_node_sections(ctx, NODE, "Hub", None, 90)) assert "Volume" in body and "24 receptions" in body assert "SNR" in body and "-2.0" in body and "+8.0" in body assert "Rhythm" in body and "10-min" in body # the finest slice 90 cols affords @@ -426,7 +426,7 @@ def test_node_page_rhythm_spans_the_day_and_shares_the_volume_gutter(tmp_path: P """The node rhythm matches the mesh's: a full-day sweep, gutter-aligned to Volume.""" repo = _seeded_repo(tmp_path) ctx = SimpleNamespace(repo=repo) - lines = _plain(_node_sections(ctx, NODE, "YUL", None, 90), width=90).split("\n") + lines = _plain(_node_sections(ctx, NODE, "Hub", None, 90), width=90).split("\n") def border_col(heading: str) -> int: start = next(i for i, line in enumerate(lines) if heading in line) @@ -929,7 +929,7 @@ def test_screen_cycles_windows_and_caches(tmp_path: Path) -> None: return [Text("page")] - screen = TimeMachineScreen(session=_FakeSession(), label="YUL", build=build) + screen = TimeMachineScreen(session=_FakeSession(), label="Hub", build=build) assert "7 d" in screen.title screen.render_body(80) screen.render_body(80) @@ -958,7 +958,7 @@ def test_picocalc_window_ring_stops_at_30_days() -> None: set_platform(PICOCALC) screen = TimeMachineScreen( - session=_FakeSession(), label="YUL", build=lambda window, width: [] + session=_FakeSession(), label="Hub", build=lambda window, width: [] ) assert "7 d" in screen.title # opens on 7 d, as everywhere screen.handle("text", "w") @@ -993,10 +993,10 @@ def _activity_repo(tmp_path: Path) -> Repository: path_hash_bytes=1, timestamp=now - timedelta(minutes=mins), ) - repo.record_trace(trun, trace("YUL-A", True, [5.0, 8.0], 40)) # min 5.0, 2 hops - repo.record_trace(trun, trace("YUL-A", True, [2.0], 30)) # min 2.0, 1 hop - repo.record_trace(trun, trace("YUL-B", True, [-3.0, 1.0, 4.0], 20)) # min -3.0, 3 hops - repo.record_trace(trun, trace("YUL-B", False, [], 15)) # timed out, no SNR + repo.record_trace(trun, trace("Hub-A", True, [5.0, 8.0], 40)) # min 5.0, 2 hops + repo.record_trace(trun, trace("Hub-A", True, [2.0], 30)) # min 2.0, 1 hop + repo.record_trace(trun, trace("Hub-B", True, [-3.0, 1.0, 4.0], 20)) # min -3.0, 3 hops + repo.record_trace(trun, trace("Hub-B", False, [], 15)) # timed out, no SNR repo.record_trace(trun, trace(PATH_TRACE_TARGET, True, [6.0], 10)) # path walk: no target crun = repo.start_run("chat", {}, None) @@ -1019,7 +1019,7 @@ def _activity_repo(tmp_path: Path) -> Repository: "INSERT INTO tx_samples " "(run_id, target, tx_power, median_min_snr, success_rate, samples, created_at) " "VALUES (?, ?, ?, ?, ?, ?, ?)", - (trun, "YUL-A", 20, 3.0, 0.8, 5, (now - timedelta(minutes=20)).isoformat()), + (trun, "Hub-A", 20, 3.0, 0.8, 5, (now - timedelta(minutes=20)).isoformat()), ) repo._conn.commit() return repo @@ -1046,7 +1046,7 @@ def test_self_trace_reach_carries_outcomes(tmp_path: Path) -> None: timed_out = [r for r in reach if not r[1]] assert len(homed) == 4 and len(timed_out) == 1 assert timed_out[0][2] is None # nothing came back to measure - assert min(r[2] for r in homed) == -3.0 # the YUL-B path's bottleneck + assert min(r[2] for r in homed) == -3.0 # the Hub-B path's bottleneck repo.close() @@ -1055,7 +1055,7 @@ def test_self_activity_ledger_tallies(tmp_path: Path) -> None: repo = _activity_repo(tmp_path) led = repo.self_activity_ledger() assert led.trace_total == 5 and led.trace_ok == 4 - assert led.trace_targets == 2 # YUL-A + YUL-B; the (path) walk names no target + assert led.trace_targets == 2 # Hub-A + Hub-B; the (path) walk names no target assert led.msg_channel == 2 and led.msg_dm == 3 assert led.dm_acked == 1 and led.dm_ackable == 2 # 2 dm sends had a verdict; 1 acked assert led.dm_peers == 2 # aa + bb @@ -1087,11 +1087,11 @@ def test_self_row_leads_the_node_list_as_a_lane() -> None: from meshterm.ui.timemachine_screen import SELF named = _lane( - ContactRow(value=SELF, name="YUL-Johputer", key="3d" * 32, you=True), + ContactRow(value=SELF, name="Homestead", key="3d" * 32, you=True), name_w=30, prefix_bytes=1, hash_w=30, ).plain assert named.startswith("★") - assert "YUL-Johputer" in named and "(you)" in named + assert "Homestead" in named and "(you)" in named assert "—" in named # heard and packets have nothing to show for us assert "3d3d" in named # our key hash, its routing prefix lit # No reachable device: a bare "you" name and a "?" hash, no "(you)" tag. diff --git a/tests/test_tui.py b/tests/test_tui.py index ab5912d..a67f236 100644 --- a/tests/test_tui.py +++ b/tests/test_tui.py @@ -2342,8 +2342,8 @@ def test_default_text_prompt_is_the_full_frame_base() -> None: assert not session._has_float() # no float — the prompt is the background assert session._base_screen() is session.top - session.top.resolve("YUL") - assert await task == "YUL" + session.top.resolve("Hub") + assert await task == "Hub" asyncio.run(main()) diff --git a/tests/test_walk.py b/tests/test_walk.py index a031a91..9d48bc9 100644 --- a/tests/test_walk.py +++ b/tests/test_walk.py @@ -18,7 +18,7 @@ from meshterm.ui.tui.render import render_to_ansi from meshterm.ui.walk_screen import _HSCROLL_STEP, WalkScreen US = "aa" * 6 -YUL = Contact(name="YUL-Cartierville", public_key="3d" * 32, node_type=2) +Hub = Contact(name="Hilltop-Repeater", public_key="3d" * 32, node_type=2) ALICE = Contact(name="Alice", public_key="b2" * 32, last_seen=utcnow() - timedelta(minutes=5)) @@ -31,13 +31,13 @@ class _FakeSession: def _topo(*, with_island: bool = False) -> MeshTopology: - """us — YUL — Alice as a two-ring chain, optionally plus a detached island pair.""" - topo = MeshTopology(US, contacts=[YUL, ALICE]) - yul = topo.canonical(YUL.public_key) + """us — Hub — Alice as a two-ring chain, optionally plus a detached island pair.""" + topo = MeshTopology(US, contacts=[Hub, ALICE]) + hub = topo.canonical(Hub.public_key) alice = topo.canonical(ALICE.public_key) when = utcnow() - topo.add_walk([topo.self_id, yul], snrs=[6.0], when=when, source="trace") - topo.add_walk([yul, alice], snrs=[-2.0], when=when, source="packet") + topo.add_walk([topo.self_id, hub], snrs=[6.0], when=when, source="trace") + topo.add_walk([hub, alice], snrs=[-2.0], when=when, source="packet") if with_island: topo.add_walk(["c3" * 6, "d4" * 6], snrs=[1.0], when=when, source="neighbour") return topo @@ -48,7 +48,7 @@ def _screen(topo: MeshTopology, cell_h: int = 24) -> WalkScreen: session=_FakeSession(), topo=topo, contacts={ - topo.canonical(YUL.public_key): YUL, + topo.canonical(Hub.public_key): Hub, topo.canonical(ALICE.public_key): ALICE, }, self_label="Homestead", @@ -69,7 +69,7 @@ def test_walk_opens_focused_on_us_with_the_link_list() -> None: body = _plain(screen.render_body(80)) assert "Homestead" in body and "this device" in body assert "Links" in body and "strongest observed first" in body - assert "YUL-Cartierville" in body # our one neighbour, as a selectable row + assert "Hilltop-Repeater" in body # our one neighbour, as a selectable row assert "edge = SNR" in body # the canvas legend # The screen's name, and nothing else: the focus, and how big the neighbourhood is, # are what the body under it is for. @@ -80,11 +80,11 @@ def test_walk_link_rows_carry_snr_evidence_and_onward_count() -> None: """A neighbour row reads SNR, samples, source tags, age, and its onward links.""" screen = _screen(_topo()) body = _plain(screen.render_body(80)) - row = next(line for line in body.split("\n") if "YUL-Cartierville" in line and "❯" in line) + row = next(line for line in body.split("\n") if "Hilltop-Repeater" in line and "❯" in line) assert "+6.0" in row # the link's median SNR assert "1×" in row # samples assert "T" in row # trace evidence tag - assert "⋯ 1" in row # one link onward (YUL — Alice) + assert "⋯ 1" in row # one link onward (Hub — Alice) assert "3d" * 6 in row # the whole 12-hex id, not a truncated prefix… assert "…" not in row # …and nothing about it elided @@ -127,7 +127,7 @@ def test_walk_graph_labels_names_in_full_when_the_canvas_has_room() -> None: long contact name keeps its letters wherever the canvas can hold it. """ long_names = [ - Contact(name="YUL-Polytechnique", public_key="e8" * 32, node_type=2), + Contact(name="Lakesidetechnique", public_key="e8" * 32, node_type=2), Contact(name="Repeater-Downtown-01", public_key="d4" * 32, node_type=2), ] topo = MeshTopology(US, contacts=long_names) @@ -145,7 +145,7 @@ def test_walk_graph_labels_names_in_full_when_the_canvas_has_room() -> None: # The canvas rows are everything above the legend line. lines = _plain(screen.render_body(80)).split("\n") canvas = "\n".join(lines[: next(i for i, l in enumerate(lines) if "edge = SNR" in l)]) - assert "YUL-Polytechnique" in canvas # 17 chars, drawn whole — no "YUL-Polytechniq…" + assert "Lakesidetechnique" in canvas # 17 chars, drawn whole — no "Lakesidetechniq…" assert "Repeater-Downtown-01" in canvas # 20 chars, whole @@ -159,23 +159,23 @@ def test_walk_selected_link_lights_the_route_that_reaches_it() -> None: from meshterm.ui.walk_screen import _snr_rgb stale = utcnow() - timedelta(days=10) # older than a week → _freshness 0.5 - yul = Contact(name="YUL", public_key="3d" * 32, node_type=2) + hub = Contact(name="Hub", public_key="3d" * 32, node_type=2) alice = Contact(name="Alice", public_key="b2" * 32) bob = Contact(name="Bob", public_key="c4" * 32) - topo = MeshTopology(US, contacts=[yul, alice, bob]) - y = topo.canonical(yul.public_key) + topo = MeshTopology(US, contacts=[hub, alice, bob]) + y = topo.canonical(hub.public_key) topo.add_walk([topo.self_id, y], snrs=[10.0], when=stale, source="trace") # approach: green topo.add_walk([y, topo.canonical(alice.public_key)], snrs=[0.0], when=stale, source="trace") # amber topo.add_walk([y, topo.canonical(bob.public_key)], snrs=[-15.0], when=stale, source="trace") # red screen = WalkScreen( session=_FakeSession(), topo=topo, - contacts={topo.canonical(c.public_key): c for c in (yul, alice, bob)}, + contacts={topo.canonical(c.public_key): c for c in (hub, alice, bob)}, self_label="Homestead", ) screen.note_viewport(24) screen.render_body(80) - screen.handle("enter") # walk to YUL — now came_from is us, the approach edge + screen.handle("enter") # walk to Hub — now came_from is us, the approach edge # Land the selection on Alice (a fan neighbour, not the ⌫-back row). while screen._rows()[screen._index] != topo.canonical(alice.public_key): screen.handle("down") @@ -212,13 +212,13 @@ def test_walk_enter_walks_and_grows_the_trail() -> None: topo = _topo() screen = _screen(topo) screen.render_body(80) - screen.handle("enter") # walk to YUL (our only neighbour) - assert screen._focus == topo.canonical(YUL.public_key) + screen.handle("enter") # walk to Hub (our only neighbour) + assert screen._focus == topo.canonical(Hub.public_key) body = _plain(screen.render_body(80)) - assert "★ › YUL-Cartierville" in body # the trail, our end on the app-wide star + assert "★ › Hilltop-Repeater" in body # the trail, our end on the app-wide star # The focus line reads name (hash) — the glyph carries the type, not a spelled-out kind. - assert "YUL-Cartierville (3d)" in body and "1 hop out" in body - assert "Alice" in body # YUL's onward neighbour is now a row + assert "Hilltop-Repeater (3d)" in body and "1 hop out" in body + assert "Alice" in body # Hub's onward neighbour is now a row # The node we walked in from is not offered back as a link — ⌫ is the way back — so # the list holds only ways *onward* and opens on the strongest of them. assert screen._rows() == [topo.canonical(ALICE.public_key)] @@ -247,7 +247,7 @@ def test_walk_the_node_walked_in_from_is_not_offered_back_as_a_link() -> None: topo = _topo() screen = _screen(topo) screen.render_body(80) - screen.handle("enter") # us → YUL + screen.handle("enter") # us → Hub assert topo.self_id not in screen._rows() assert screen._rows() == screen._fan_nodes() # the canvas draws the same set @@ -266,12 +266,12 @@ def test_walk_the_node_walked_in_from_is_not_offered_back_as_a_link() -> None: def test_walk_walking_to_an_earlier_node_drops_the_loop() -> None: """Revisiting a node already on the trail truncates the stack to its first appearance, dropping the circular stretch walked to get back there.""" - topo = MeshTopology(US, contacts=[YUL, ALICE]) - yul = topo.canonical(YUL.public_key) + topo = MeshTopology(US, contacts=[Hub, ALICE]) + hub = topo.canonical(Hub.public_key) alice = topo.canonical(ALICE.public_key) when = utcnow() - topo.add_walk([topo.self_id, yul], snrs=[6.0], when=when, source="trace") - topo.add_walk([yul, alice], snrs=[-2.0], when=when, source="packet") + topo.add_walk([topo.self_id, hub], snrs=[6.0], when=when, source="trace") + topo.add_walk([hub, alice], snrs=[-2.0], when=when, source="packet") topo.add_walk([topo.self_id, alice], snrs=[3.0], when=when, source="trace") # closes the loop screen = _screen(topo) @@ -280,11 +280,11 @@ def test_walk_walking_to_an_earlier_node_drops_the_loop() -> None: screen._index = screen._rows().index(node) screen.handle("enter") - walk_to(yul) # us → YUL - walk_to(alice) # YUL → Alice - assert screen._trail == [topo.self_id, yul, alice] + walk_to(hub) # us → Hub + walk_to(alice) # Hub → Alice + assert screen._trail == [topo.self_id, hub, alice] # Alice → us closes the loop. It is a real link onward (not the node we arrived from, - # which is YUL), so it is on offer — and taking it drops the stretch walked to get here. + # which is Hub), so it is on offer — and taking it drops the stretch walked to get here. walk_to(topo.self_id) assert screen._trail == [topo.self_id] @@ -293,10 +293,10 @@ def test_walk_trail_crops_its_head_not_its_tail_when_narrow() -> None: """A trail too long for the line is cropped at its *start*, the trophy case's crop.""" screen = _screen(_topo()) us = screen._topo.self_id - yul = screen._topo.canonical(YUL.public_key) + hub = screen._topo.canonical(Hub.public_key) alice = screen._topo.canonical(ALICE.public_key) - screen._trail = [us, yul, alice] - width = 20 # too narrow for the whole "Homestead › YUL-Cartierville › Alice" + screen._trail = [us, hub, alice] + width = 20 # too narrow for the whole "Homestead › Hilltop-Repeater › Alice" text = screen._trail_text(width).plain assert text[0] in ("…", CRACK_HEAD) # cut, not elided: no whole hop bought the mark assert "⋯" not in text @@ -346,13 +346,13 @@ def test_walk_trail_names_carry_their_node_hues() -> None: screen = _screen(_topo()) us = screen._topo.self_id - yul = screen._topo.canonical(YUL.public_key) - screen._trail = [us, yul] + hub = screen._topo.canonical(Hub.public_key) + screen._trail = [us, hub] text = screen._trail_text(80) plain = text.plain - yul_at = plain.index("YUL-Cartierville") + hub_at = plain.index("Hilltop-Repeater") assert any( - s.start <= yul_at < s.end and "bold" in str(s.style) and node_style(yul) in str(s.style) + s.start <= hub_at < s.end and "bold" in str(s.style) and node_style(hub) in str(s.style) for s in text.spans ) @@ -360,24 +360,24 @@ def test_walk_trail_names_carry_their_node_hues() -> None: def test_walk_locate_refocuses_us_and_home_end_walk_the_list() -> None: """^U resets the walk to our own node from anywhere; Home/End are the list's ends.""" # A hub of spokes off one of our neighbours: a walked-away focus with a list to move in. - topo = MeshTopology(US, contacts=[YUL]) - yul = topo.canonical(YUL.public_key) + topo = MeshTopology(US, contacts=[Hub]) + hub = topo.canonical(Hub.public_key) when = utcnow() - topo.add_walk([topo.self_id, yul], snrs=[6.0], when=when, source="trace") + topo.add_walk([topo.self_id, hub], snrs=[6.0], when=when, source="trace") for i in range(5): - topo.add_walk([yul, f"{i:02x}" * 6], snrs=[5.0 - i], when=when, source="trace") + topo.add_walk([hub, f"{i:02x}" * 6], snrs=[5.0 - i], when=when, source="trace") screen = _screen(topo) screen.render_body(80) - screen.handle("enter") # us → YUL + screen.handle("enter") # us → Hub screen.render_body(80) rows = screen._rows() assert len(rows) > 1 and topo.self_id not in rows # Home/End move the highlight — they no longer abandon the walk. screen.handle("end") - assert screen._index == len(rows) - 1 and screen._trail == [topo.self_id, yul] + assert screen._index == len(rows) - 1 and screen._trail == [topo.self_id, hub] screen.handle("home") - assert screen._index == 0 and screen._trail == [topo.self_id, yul] + assert screen._index == 0 and screen._trail == [topo.self_id, hub] screen.handle("locate") assert screen._trail == [topo.self_id] @@ -454,7 +454,7 @@ def test_walk_the_fan_is_all_east_and_holds_no_came_from() -> None: topo = _topo() screen = _screen(topo) screen.render_body(80) - screen.handle("enter") # focus YUL; we came from us + screen.handle("enter") # focus Hub; we came from us alice = topo.canonical(ALICE.public_key) fx, _fy = screen._focus_pos(80, 12) placed = screen._place_neighbours(80, 12, screen._fan_nodes(), False) @@ -736,23 +736,23 @@ def test_walk_trail_scroll_is_inert_on_a_walk_that_fits() -> None: def test_walk_find_narrows_the_canvas_fan_but_not_the_walk() -> None: """Typing thins the ways onward; the focus and the node walked from hold their place.""" bob = Contact(name="Bob-Tower", public_key="c7" * 32) - topo = MeshTopology(US, contacts=[YUL, ALICE, bob]) - yul, alice = topo.canonical(YUL.public_key), topo.canonical(ALICE.public_key) + topo = MeshTopology(US, contacts=[Hub, ALICE, bob]) + hub, alice = topo.canonical(Hub.public_key), topo.canonical(ALICE.public_key) bob_id = topo.canonical(bob.public_key) when = utcnow() - topo.add_walk([topo.self_id, yul], snrs=[6.0], when=when, source="trace") - topo.add_walk([yul, alice], snrs=[-2.0], when=when, source="packet") - topo.add_walk([yul, bob_id], snrs=[1.0], when=when, source="packet") + topo.add_walk([topo.self_id, hub], snrs=[6.0], when=when, source="trace") + topo.add_walk([hub, alice], snrs=[-2.0], when=when, source="packet") + topo.add_walk([hub, bob_id], snrs=[1.0], when=when, source="packet") screen = WalkScreen( session=_FakeSession(), topo=topo, - contacts={yul: YUL, alice: ALICE, bob_id: bob}, + contacts={hub: Hub, alice: ALICE, bob_id: bob}, self_label="Homestead", ) screen.note_viewport(24) screen.render_body(80) - screen._index = screen._rows().index(yul) - screen.handle("enter") # focus YUL, came from us + screen._index = screen._rows().index(hub) + screen.handle("enter") # focus Hub, came from us canvas = _plain(screen._canvas_lines(80, 12, None)) assert "Alice" in canvas and "Bob-Tower" in canvas # the whole fan, unfiltered @@ -763,7 +763,7 @@ def test_walk_find_narrows_the_canvas_fan_but_not_the_walk() -> None: assert "Alice" in canvas # the way onward the query is about assert "Bob-Tower" not in canvas # and the one it isn't # The focus is not a candidate: it is the walk so far, and holds through any query. - assert "YUL-Cartierville" in canvas + assert "Hilltop-Repeater" in canvas def test_walk_marks_wear_their_node_type_colour_not_the_key_hue() -> None: @@ -773,15 +773,15 @@ def test_walk_marks_wear_their_node_type_colour_not_the_key_hue() -> None: topo = _topo() screen = _screen(topo) - yul, alice = topo.canonical(YUL.public_key), topo.canonical(ALICE.public_key) + hub, alice = topo.canonical(Hub.public_key), topo.canonical(ALICE.public_key) - assert screen._glyph(yul) == (REPEATER_MARK[0], "type.repeater") + assert screen._glyph(hub) == (REPEATER_MARK[0], "type.repeater") assert screen._glyph(alice) == (NODE_MARK[0], "type.node") - assert screen._marker_rgb(yul) == mark_rgb("type.repeater") + assert screen._marker_rgb(hub) == mark_rgb("type.repeater") assert screen._marker_rgb(topo.self_id) == mark_rgb(SELF_MARK[1]) # Identity has its own lane and keeps it: the *name* is what carries the key hue. - assert screen._marker_rgb(yul) != screen._label_rgb(yul) - assert screen._label_rgb(yul) == _plain_rgb(node_style(yul)) + assert screen._marker_rgb(hub) != screen._label_rgb(hub) + assert screen._label_rgb(hub) == _plain_rgb(node_style(hub)) # The legend is a sample of those very marks, so it keys colour as well as shape. legend = screen._legend() hues = { diff --git a/tests/test_watchtower.py b/tests/test_watchtower.py index 6c54d36..000a309 100644 --- a/tests/test_watchtower.py +++ b/tests/test_watchtower.py @@ -34,7 +34,7 @@ def _service(tmp_path: Path) -> WatchtowerService: return service -def _obs(node=NODE, name="YUL", snr=None, age_s=0, kind="advert") -> Observation: +def _obs(node=NODE, name="Hub", snr=None, age_s=0, kind="advert") -> Observation: return Observation( node=node, name=name, kind=kind, snr=snr, observed_at=utcnow() - timedelta(seconds=age_s), @@ -49,13 +49,13 @@ def test_store_watch_round_trips_and_persists(tmp_path: Path) -> None: path = tmp_path / "watchtower.json" store = WatchStore(path) seen = utcnow() - timedelta(hours=2) - store.watch(NODE, "YUL", last_seen=seen) + store.watch(NODE, "Hub", last_seen=seen) store.set_silence(NODE, 24) store.set_snr_watch(NODE, False) again = WatchStore(path) entry = again.watched()[NODE] - assert entry.name == "YUL" and entry.silence_hours == 24 and not entry.snr_watch + assert entry.name == "Hub" and entry.silence_hours == 24 and not entry.snr_watch assert entry.last_heard == seen again.unwatch(NODE) assert not WatchStore(path).watched() @@ -64,8 +64,8 @@ def test_store_watch_round_trips_and_persists(tmp_path: Path) -> None: def test_store_alert_log_caps_acks_and_clears(tmp_path: Path) -> None: """Alerts append newest-first, cap, acknowledge, and clear.""" store = WatchStore(tmp_path / "watchtower.json") - first = store.add_alert("silence", "YUL", "quiet") - store.add_alert("snr", "YUL", "sagging") + first = store.add_alert("silence", "Hub", "quiet") + store.add_alert("snr", "Hub", "sagging") assert [a.kind for a in store.alerts()] == ["snr", "silence"] assert store.unacked_count() == 2 @@ -87,9 +87,9 @@ def test_store_note_heard_updates_and_refreshes_name(tmp_path: Path) -> None: store = WatchStore(tmp_path / "watchtower.json") store.watch(NODE, NODE) later = utcnow() + timedelta(minutes=5) - store.note_heard(NODE, when=later, name="YUL-Cartierville") + store.note_heard(NODE, when=later, name="Hilltop-Repeater") entry = store.watched()[NODE] - assert entry.last_heard == later and entry.name == "YUL-Cartierville" + assert entry.last_heard == later and entry.name == "Hilltop-Repeater" # --- the silence rule --------------------------------------------------------------------- @@ -99,7 +99,7 @@ def test_silence_fires_once_then_rearms_on_recovery(tmp_path: Path) -> None: """Quiet past the threshold alarms once; hearing again notes the recovery.""" service = _service(tmp_path) store = service._ctx.watch_store - store.watch(NODE, "YUL", last_seen=utcnow() - timedelta(hours=13)) + store.watch(NODE, "Hub", last_seen=utcnow() - timedelta(hours=13)) service.evaluate() # default threshold is 12 h — already past it service.evaluate() # latched: no duplicate @@ -120,7 +120,7 @@ def test_silence_respects_off_and_unheard(tmp_path: Path) -> None: """An OFF rule never alarms, however stale the mark.""" service = _service(tmp_path) store = service._ctx.watch_store - store.watch(NODE, "YUL", last_seen=utcnow() - timedelta(days=30)) + store.watch(NODE, "Hub", last_seen=utcnow() - timedelta(days=30)) store.set_silence(NODE, OFF) service.evaluate() assert store.alerts() == [] @@ -133,7 +133,7 @@ def test_snr_sag_alerts_with_cooldown(tmp_path: Path) -> None: """A clear drop in median SNR alerts once, not on every subsequent packet.""" service = _service(tmp_path) store = service._ctx.watch_store - store.watch(NODE, "YUL") + store.watch(NODE, "Hub") for _ in range(SNR_WINDOW): service.note(_obs(snr=8.0)) @@ -151,7 +151,7 @@ def test_snr_ignores_packet_rows_and_disabled_watch(tmp_path: Path) -> None: """Relay-measured packet SNR and an off switch both keep the rule quiet.""" service = _service(tmp_path) store = service._ctx.watch_store - store.watch(NODE, "YUL") + store.watch(NODE, "Hub") store.set_snr_watch(NODE, False) for snr in (9.0,) * SNR_WINDOW + (-9.0,) * SNR_WINDOW: service.note(_obs(snr=snr)) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 9690887..f0395dd 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -50,13 +50,13 @@ def test_format_ago_speaks_grammatical_prose() -> None: def _resolve(hop: str) -> str: - return {"aa": "Alice", "3d": "YUL"}.get(hop, hop) + return {"aa": "Alice", "3d": "Hub"}.get(hop, hop) def test_path_text_names_hops_and_keeps_hashes_bare() -> None: """Named hops read as their name alone; unnamed ones as their bare hash.""" text = path_text(["aa", "77", "3d"], _resolve) - assert text.plain == "Alice → 77 → YUL" # no parenthesized hash after a name + assert text.plain == "Alice → 77 → Hub" # no parenthesized hash after a name def test_path_text_marks_us_white_and_names_in_their_hue() -> None: @@ -64,7 +64,7 @@ def test_path_text_marks_us_white_and_names_in_their_hue() -> None: text = path_text(["aa", "3d"], _resolve, self_name="Alice") styles = {text.plain[s.start : s.end]: str(s.style) for s in text.spans} assert styles.get("Alice") == "you" - assert "YUL" in styles and styles["YUL"] != "you" + assert "Hub" in styles and styles["Hub"] != "you" def test_path_text_empty_reads_as_direct() -> None: @@ -127,7 +127,7 @@ def test_path_text_dims_the_tail_from_dim_from() -> None: assert ("us", "you") in styles # the departure keeps the white you assert ("us", "faint") in styles # the landing back on us is faint assert any(run.startswith("Alice") and style == "faint" for run, style in styles) - assert any(run.startswith("YUL") and style != "faint" for run, style in styles) + assert any(run.startswith("Hub") and style != "faint" for run, style in styles) # --- the name→key resolver and the keyless-stays-muted rule -------------------------- @@ -196,7 +196,7 @@ def test_revisit_note_lists_every_repeated_hop() -> None: """More than one hop repeated names them all, comma-joined, still one sentence.""" note = revisit_note(["aa", "3d"], _resolve) assert note is not None - assert note.plain.startswith("⚠ Alice, YUL repeats — ") + assert note.plain.startswith("⚠ Alice, Hub repeats — ") assert len(note.plain) <= 72 # the footer/row budget every surface renders it inside