summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_app_directories.py66
1 files changed, 64 insertions, 2 deletions
diff --git a/packages/meshbay-node/tests/test_app_directories.py b/packages/meshbay-node/tests/test_app_directories.py
index 69b3be5..726f5bd 100644
--- a/packages/meshbay-node/tests/test_app_directories.py
+++ b/packages/meshbay-node/tests/test_app_directories.py
@@ -14,8 +14,9 @@ Two properties are new rather than moved, and both matter more than the tidying:
then matched no entry — an app showing an empty tab, with nothing to
distinguish "misconfigured" from "no files yet". The moment of setting is the
only one where the operator is present to be told;
-* **the legacy scalar is derived, never stored.** `video_root` still rides on
- the handshake ack for MNP 1.0 clients. Kept as a second stored value it would
+* **a second name is derived, never stored.** `video_root` and friends are gone
+ from the wire entirely; `chat_directory` is the one that survives, and it is
+ computed from the list on every build. Kept as a second *stored* value it would
drift from the list within one run — the shape of bug that reads as "it works
after a restart".
"""
@@ -370,3 +371,64 @@ async def test_an_empty_set_is_signable(tmp_path):
session = _handler_session(tmp_path, authorized=True)
session._do_app_directories({"app": "video", "directories": []})
assert session.issued == [(OP_APP_DIRECTORIES, "video:")]
+
+
+# ── One list of applications, not two ────────────────────────────────────────
+
+async def test_the_ack_carries_every_app_the_context_knows_about():
+ """
+ The handshake ack emits whatever `<app>_directories` the group context
+ holds, so an application cannot be configurable on the node and invisible on
+ the wire.
+
+ There were two lists until 2026-09-10 — the daemon built the context from
+ one and the ack was assembled from a copy — and they had already drifted by
+ one entry. The application that entry belonged to is the reference app,
+ which exists precisely to prove that a new application needs no
+ special-casing; it was the single application whose directories never
+ reached a client, so the claim was false exactly where it is demonstrated.
+
+ Deriving the ack from the context removes the second list rather than
+ syncing it, which is the only version of this that cannot drift again.
+ """
+ session = WebRTCPeerSession.__new__(WebRTCPeerSession)
+ session._ctx = {
+ "video_directories": ["Media/Films"],
+ "photo_directories": [],
+ # An application nothing on the node names. It reaches the ack because
+ # the context carries it, which is the whole property.
+ "helloworld_directories": ["Media"],
+ # Not directories, and must not be swept in.
+ "chat_directory": "Media",
+ "chat_epoch": 3,
+ }
+ session._group_id = ""
+
+ ack = session._app_directories_ack()
+
+ assert ack == {
+ "video_directories": ["Media/Films"],
+ "photo_directories": [],
+ "helloworld_directories": ["Media"],
+ }
+
+
+async def test_the_ack_has_no_list_of_applications_of_its_own():
+ """
+ `NodeDaemon.APP_DIR_KEYS` is the list, and the transport must not keep a
+ copy: a second list is a second thing to remember when an application is
+ added, and the one that is forgotten disagrees silently.
+
+ The transport names an application in exactly one place — `ALLOWED_APPS`,
+ which is server-side enforcement rather than a directory list.
+ """
+ from meshbay_node.daemon import NodeDaemon
+
+ source = Path(WebRTCPeerSession.__module__.replace(".", "/"))
+ text = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src"
+ / f"{source}.py").read_text(encoding="utf-8")
+ body = text[text.index("def _app_directories_ack"):]
+ body = body[:body.index("\n def ", 1)]
+ for app in NodeDaemon.APP_DIR_KEYS:
+ assert app not in body, (
+ f"the ack names {app!r}; it must read the context's own keys")