aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/webapp.py1
-rw-r--r--packages/meshbay-hub/tests/conftest.py27
-rw-r--r--packages/meshbay-hub/tests/test_asset_versioning.py27
3 files changed, 54 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
index 6dfd3ed..a1807ea 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
@@ -34,6 +34,7 @@ _ASSETS = ("style.css", "keyderive.js", "crypto.js", "transport.js", "app.js",
# app.js or group-page.js, so a change to any of them is a change
# to what the browser must fetch.
"icon.js", "file-utils.js", "hub-client.js", "apps.js",
+ "source-merge.js",
"chat-app.js", "files-app.js", "video-player.js", "video-app.js",
"music-app.js", "music-player.js", "photos-app.js",
"group-settings.js", "group-page.js",
diff --git a/packages/meshbay-hub/tests/conftest.py b/packages/meshbay-hub/tests/conftest.py
index 2769f7e..cb595c5 100644
--- a/packages/meshbay-hub/tests/conftest.py
+++ b/packages/meshbay-hub/tests/conftest.py
@@ -96,3 +96,30 @@ def _skip_email_verification(monkeypatch):
monkeypatch.setattr(
"meshbay_hub.api.users._create_and_send_verification", _noop)
monkeypatch.setattr("meshbay_hub.mail._send", lambda msg: True)
+
+
+@pytest.fixture(autouse=True)
+def _no_cleanup_task(monkeypatch):
+ """
+ Do not run the maintenance loop under test.
+
+ `create_app`'s lifespan starts `cleanup_loop` as an asyncio task, so every
+ test — each of which enters that lifespan — ran a purge pass concurrently
+ with its own requests. On SQLite `:memory:` that is not merely noisy: the
+ engine uses a **StaticPool**, one connection for the whole process, so the
+ request's session and the cleanup task's session interleave their
+ transactions on the *same* connection. A registration could commit and then
+ not be visible to the login three lines later, which surfaced as
+ `401 Invalid credentials` for an account created moments before, in about
+ one run of `test_node_ws_auth.py` in four.
+
+ The purge itself is not at fault and this is not a production condition:
+ the DELETE was measured removing 0 rows, and PostgreSQL gives every session
+ its own connection. What is removed here is the second user of the shared
+ one. Tests that want the maintenance behaviour call the `purge_*` functions
+ directly, which is how they are covered.
+ """
+ async def _noop(get_session):
+ return
+
+ monkeypatch.setattr("meshbay_hub.tasks.cleanup.cleanup_loop", _noop)
diff --git a/packages/meshbay-hub/tests/test_asset_versioning.py b/packages/meshbay-hub/tests/test_asset_versioning.py
index e5889f0..3d84d99 100644
--- a/packages/meshbay-hub/tests/test_asset_versioning.py
+++ b/packages/meshbay-hub/tests/test_asset_versioning.py
@@ -23,7 +23,7 @@ import re
import pytest
from fastapi.testclient import TestClient
-from meshbay_hub.api.webapp import ASSET_V, STATIC_DIR, _asset_version
+from meshbay_hub.api.webapp import _ASSETS, ASSET_V, STATIC_DIR, _asset_version
from meshbay_hub.app import create_app
@@ -101,3 +101,28 @@ def test_the_fingerprint_follows_the_content(tmp_path, monkeypatch):
finally:
target.write_bytes(original)
assert _asset_version() == before, "the fingerprint is not reproducible"
+
+
+def test_every_static_script_participates_in_the_fingerprint():
+ """
+ `_ASSETS` is hand-maintained, and forgetting an entry fails silently: the
+ file is imported by the page, so the browser fetches it, but it does not
+ feed the content hash — so a change confined to that one file ships at the
+ URL a cache already holds. Nothing errors, and the symptom is a fix that
+ "doesn't work" on exactly the machines that visited before.
+
+ Found by `source-merge.js`, added to the Search view and left out of the
+ list. It happened to be harmless that day because `search-page.js` changed
+ in the same commit and *is* listed — which is the worst way for this to go
+ unnoticed. The checklist in docs/apps.md §4 step 5 names this trap; this
+ enforces it instead of relying on remembering.
+
+ `sw.js` is the one deliberate exclusion — the service worker is served
+ unversioned on purpose (`test_the_service_worker_is_not_versioned`).
+ """
+ on_disk = {p.name for p in STATIC_DIR.glob("*.js")} - {"sw.js"}
+ missing = sorted(on_disk - set(_ASSETS))
+ assert not missing, (
+ f"static scripts missing from webapp._ASSETS: {missing}. A change to "
+ "one of these will not move the asset URL, so a browser that cached "
+ "the page keeps running the old copy")