diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-07 00:48:55 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-07 00:48:55 +0200 |
| commit | a36080742287bad8f657f688d7fec0022dd696a1 (patch) | |
| tree | 3da5c9eedb9c0d2f22967ebf61bd777e32c7bb5e /packages/meshbay-hub/tests | |
| parent | 6828e64a256caea3c4e51829ae1aa09dfa725324 (diff) | |
| download | meshbay-a36080742287bad8f657f688d7fec0022dd696a1.tar.gz | |
feat(client): HelloWorld, the reference application
Every other test of the plugin architecture reads source for the *absence* of
app names. That proves nobody wrote a special case for Videos; it cannot prove
a genuinely new application works, because there was no new application.
This is one. It stores directories, appears as a tab, has a settings pane and
lists files, and the node has never heard its name outside a single allow-list
entry. Two files and one registry line, which is the claim
`docs/refactor-groups.md` §4.1 makes.
It ships hidden behind `?dev=1` (`dev: true` in the registry, the same opt-in
shape as transport.js's `?trace=1`). Registering it normally would put a toy
app in every operator's group; not registering it would prove nothing, since
registration is exactly what is claimed to be sufficient.
**Adding it found two places where the claim was nearly true rather than true,
and both are fixed by making the code less app-specific:**
`group-settings.js` fell back to the whole registry when a group had no
`enabled_apps` yet — which would have turned a hidden app on for everyone. It
asks `availableApps()` now.
`group-page.js` wrote out `videoDirectories` / `musicDirectories` /
`photoDirectories` by hand, so a fifth app would have needed that file edited.
It derives `<key>Directories` from the registry.
Neither was found by reading; both were found by adding the app, which is the
whole reason it exists.
Verified in a real Electron window as well as by the tests: hidden by default,
present with the flag, offered its own settings section, and listing exactly
the files under its configured folder and its subfolders — not the ones beside
it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011pvMdvLBG92jyhvD5pD6us
Diffstat (limited to 'packages/meshbay-hub/tests')
3 files changed, 162 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py b/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py new file mode 100644 index 0000000..a1baf92 --- /dev/null +++ b/packages/meshbay-hub/tests/test_helloworld_proves_the_plugin_claim.py @@ -0,0 +1,159 @@ +""" +The reference application, and what it is for. + +`docs/refactor-groups.md` claims that adding an application costs a registry +entry and the app's own files — no op, no MNP message, no route, no edit to the +pages that render it. Every other test of that claim reads source for the +*absence* of app names, which proves nobody wrote a special case for Videos. It +cannot prove that a genuinely new app works, because there was no new app. + +HelloWorld is one. It stores directories, appears as a tab, has a settings pane +and lists files, and the node has never heard its name outside one allow-list +entry. The assertions below are the claim, stated as things that must stay true +of a file that was not written for it. + +It ships hidden behind `?dev=1` (apps.js's `dev: true`). Registering it +normally would put a toy app in every operator's group; not registering it +would prove nothing, since registration is exactly the thing being claimed as +sufficient. + +**Two honest exceptions**, both found *by* adding it and both fixed by making +the code less app-specific rather than more: + +* `group-settings.js` fell back to the whole registry when a group had no + `enabled_apps` yet, which would have turned a hidden app on for everyone. It + asks `availableApps()` now. +* `group-page.js` wrote out `videoDirectories` / `musicDirectories` / + `photoDirectories` by hand. It derives `<key>Directories` from the registry + now, which is what made the claim true rather than nearly true. +""" + +import re +from pathlib import Path + +import pytest + +STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" +NODE_SRC = (Path(__file__).resolve().parents[2] / "meshbay-node" / "src" + / "meshbay_node") + +APP = STATIC / "helloworld-app.js" +SETTINGS = STATIC / "helloworld-app-settings.js" + +pytestmark = pytest.mark.skipif(not APP.exists(), + reason="the reference app is not in this checkout") + + +def _code_only(source: str) -> str: + source = re.sub(r"/\*.*?\*/", "", source, flags=re.S) + return re.sub(r"^\s*//.*$", "", source, flags=re.M) + + +# ── The claim ──────────────────────────────────────────────────────────────── + +@pytest.mark.parametrize("name", [ + "group-page.js", "group-settings.js", "files-app.js", "transport.js", + "hub-client.js", "settings-ui.js", "folder-tree.js", +]) +def test_no_shared_client_file_mentions_it(name): + """ + The registry is where an app is named, and nowhere else. A branch on + `'helloworld'` in any of these would mean the architecture works for four + apps somebody wrote plumbing for. + """ + source = _code_only((STATIC / name).read_text(encoding="utf-8")) + assert "helloworld" not in source.lower(), ( + f"{name} names the reference app — adding an application is supposed " + f"to touch nothing here") + + +@pytest.mark.parametrize("name", [ + "ops.py", "roster.py", "config.py", "roots.py", +]) +def test_no_shared_node_module_mentions_it(name): + """ + Its directories are stored by `ops.set_app_directories`, which keys the row + by whatever the app is called. Nothing on the node knows what it is. + """ + source = (NODE_SRC / name).read_text(encoding="utf-8") + assert "helloworld" not in source.lower(), ( + f"{name} names the reference app; the generic path was supposed to " + f"cover it") + + +def test_the_node_names_it_once_and_only_in_the_allow_list(): + """ + `ALLOWED_APPS` is server-side enforcement — a client naming an app this + node does not know is refused — so an app absent from it could not + demonstrate anything. That entry plus the client's registry line is the + whole cost. + """ + source = (NODE_SRC / "transport" / "webrtc_server.py").read_text( + encoding="utf-8") + code = re.sub(r"^\s*#.*$", "", source, flags=re.M) + hits = [ln for ln in code.splitlines() if "helloworld" in ln.lower()] + assert len(hits) == 1, f"expected one mention, got: {hits}" + assert "ALLOWED_APPS" in hits[0] or "helloworld" in hits[0] + assert "ALLOWED_APPS" in code[:code.index("helloworld") + 200] + + +def test_the_registry_entry_is_ordinary(): + source = (STATIC / "apps.js").read_text(encoding="utf-8") + entry = source[source.index("key: 'helloworld'"):] + entry = entry[:entry.index("},") + 2] + for field in ("icon:", "labelKey:", "Component:", "Settings:"): + assert field in entry, f"the entry has no {field}" + assert "dev: true" in entry, "it would ship to every operator" + + +# ── And it is held to the same contract as the rest ────────────────────────── + +def test_its_settings_pane_takes_the_shared_props_and_no_others(): + m = re.search(r"function \w+Settings\(\{([^}]*)\}\)", + SETTINGS.read_text(encoding="utf-8")) + assert m + props = {p.strip() for p in m.group(1).split(",") if p.strip()} + assert props <= {"roots", "dirs", "settings", "saveDirectories", + "transport", "signFn"} + + +def test_it_reads_its_directories_under_its_own_key(): + """ + `<key>Directories` — the shape `group-page.js` derives for every registered + app. An app reading a name spelled anywhere else would need that place + edited too. + """ + for path in (APP, SETTINGS): + assert "helloworldDirectories" in path.read_text(encoding="utf-8") + + +def test_it_does_not_reach_for_the_transport(): + """ + It has no third-party service and no setting of its own, so it needs + neither — which is the case an app author most often starts from, and the + one the architecture has to make free. + """ + source = _code_only(SETTINGS.read_text(encoding="utf-8")) + assert "transport." not in source + assert "saveDirectories" in source + + +# ── Hidden, but genuinely registered ──────────────────────────────────────── + +def test_a_dev_app_is_filtered_out_by_default(): + source = (STATIC / "apps.js").read_text(encoding="utf-8") + assert "function availableApps()" in source + body = source[source.index("function availableApps()"):] + body = body[:body.index("\n}") + 2] + assert "devAppsShown()" in body and "a.dev" in body + + +def test_nothing_falls_back_to_the_unfiltered_registry(): + """ + A fallback of "every app in the registry" would enable a hidden one for the + whole group. This is the exception the reference app found. + """ + source = _code_only((STATIC / "group-settings.js").read_text(encoding="utf-8")) + assert "APPS.map(" not in source and "APPS.filter(" not in source, ( + "group-settings.js reads the raw registry; it should ask " + "availableApps()") diff --git a/packages/meshbay-hub/tests/test_hook_ordering.py b/packages/meshbay-hub/tests/test_hook_ordering.py index 5719be5..01516bd 100644 --- a/packages/meshbay-hub/tests/test_hook_ordering.py +++ b/packages/meshbay-hub/tests/test_hook_ordering.py @@ -43,6 +43,7 @@ STATIC_FILES = [ "settings-ui.js", "folder-tree.js", "chat-app-settings.js", "video-app-settings.js", "music-app-settings.js", "photos-app-settings.js", + "helloworld-app.js", "helloworld-app-settings.js", "auth-page.js", "explore-page.js", "create-group-page.js", ] diff --git a/packages/meshbay-hub/tests/test_transport_contracts.py b/packages/meshbay-hub/tests/test_transport_contracts.py index 3c6d022..b462242 100644 --- a/packages/meshbay-hub/tests/test_transport_contracts.py +++ b/packages/meshbay-hub/tests/test_transport_contracts.py @@ -41,6 +41,8 @@ SPLIT_FILES = [APP, GROUP_PAGE, CHAT_APP, STATIC / "files-app.js", STATIC / "video-app-settings.js", STATIC / "music-app-settings.js", STATIC / "photos-app-settings.js", + STATIC / "helloworld-app.js", + STATIC / "helloworld-app-settings.js", STATIC / "auth-page.js", STATIC / "explore-page.js", CREATE_GROUP] |