aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_media_pager.py
blob: a8a05690e61cd76009c0b360b3a73ec8701e776b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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