diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_media_pager.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_media_pager.py | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_media_pager.py b/packages/meshbay-hub/tests/test_media_pager.py new file mode 100644 index 0000000..a8a0569 --- /dev/null +++ b/packages/meshbay-hub/tests/test_media_pager.py @@ -0,0 +1,71 @@ +""" +Videos and Music show their cards a page at a time (`pager.js`). + +The page size is a hub preference, so two things can go wrong without any +error on screen: a stored value the client misreads, and a key the hub refuses +— the Settings select would snap back and nothing would say why. The functions +are read out of `pager.js` rather than copied, and the key out of both files. +""" + +import json +import re +import shutil +import subprocess +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" +PAGER = ROOT / "static" / "pager.js" +USERS = ROOT / "api" / "users.py" + +pytestmark = pytest.mark.skipif( + shutil.which("node") is None or not PAGER.exists(), + reason="node or the SPA sources are not available") + + +@pytest.fixture(scope="module") +def source(): + text = PAGER.read_text(encoding="utf-8") + consts = re.findall(r"^export const PAGE_SIZE_\w+ = [^;]+;", text, re.M) + funcs = [re.search(rf"^export function {name}\(.*?^\}}", text, re.M | re.S) + for name in ("pageSizeFrom", "pageBounds")] + assert len(consts) == 4 and all(funcs), "pager.js no longer has what this test reads" + return "\n".join(consts + [m.group(0) for m in funcs]).replace("export ", "") + + +def _run(tmp_path, source, expr): + script = tmp_path / "case.js" + script.write_text(f"{source}\nconsole.log(JSON.stringify({expr}));") + out = subprocess.run(["node", str(script)], capture_output=True, text=True, check=True) + return json.loads(out.stdout) + + +@pytest.mark.parametrize("stored, expected", [ + (None, 50), ("", 50), ("30", 30), ("10", 10), ("200", 200), + ("0", 50), ("210", 50), ("35", 50), ("abc", 50), ("-10", 50), +]) +def test_page_size_from_preference(tmp_path, source, stored, expected): + prefs = {} if stored is None else {"media_page_size": stored} + assert _run(tmp_path, source, f"pageSizeFrom({json.dumps(prefs)})") == expected + + +@pytest.mark.parametrize("total, size, page, expected", [ + (0, 50, 0, {"page": 0, "last": 0, "start": 0, "end": 0}), + (120, 50, 0, {"page": 0, "last": 2, "start": 0, "end": 50}), + (120, 50, 2, {"page": 2, "last": 2, "start": 100, "end": 120}), + (100, 50, 1, {"page": 1, "last": 1, "start": 50, "end": 100}), + # The list shrank under the reader: the last page that exists, not an empty one. + (60, 50, 4, {"page": 1, "last": 1, "start": 50, "end": 60}), + (60, 50, -1, {"page": 0, "last": 1, "start": 0, "end": 50}), +]) +def test_page_bounds(tmp_path, source, total, size, page, expected): + assert _run(tmp_path, source, f"pageBounds({total}, {size}, {page})") == expected + + +def test_hub_accepts_the_key_the_client_writes(): + key = re.search(r"^export const PAGE_SIZE_PREF = '([^']+)';", + PAGER.read_text(encoding="utf-8"), re.M).group(1) + allowed = re.search(r"ALLOWED_PREF_KEYS = frozenset\(\[(.*?)\]\)", + USERS.read_text(encoding="utf-8"), re.S).group(1) + assert f'"{key}"' in allowed |