""" The settings page asks the node on this machine again before deciding there is none. `readLocalNode` (group-settings.js) is lifted out and run under node against a stub of `platform.node`. One missed 3 s check used to set "no node here", which hides "Add a directory", until the page was reloaded: found by a tester adding a directory while the node was busy indexing the others. """ import json import shutil import subprocess from pathlib import Path import pytest STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" pytestmark = pytest.mark.skipif(shutil.which("node") is None, reason="node is not available") def _run(tmp_path, stub): src = (STATIC / "group-settings.js").read_text(encoding="utf-8") fn = src[src.index("async function readLocalNode"):] fn = fn[:fn.index("\n}\n") + 2] script = tmp_path / "detect.mjs" script.write_text(fn + stub + """ const out = { result: await readLocalNode(node, 'g1', 4, 1), detects: node.detects }; console.log(JSON.stringify(out)); """, encoding="utf-8") proc = subprocess.run(["node", str(script)], capture_output=True, text=True, encoding="utf-8") assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_a_node_that_misses_a_check_is_asked_again(tmp_path): out = _run(tmp_path, """ const node = { detects: 0, async detect() { this.detects++; if (this.detects === 1) return { detected: false, configured: true }; return { detected: true, configured: true }; }, async op() { if (this.detects === 2) throw new Error('mid-reload'); return { groups: [{ id: 'g1', name: 'G', roots: [{ name: 'a' }] }] }; }, }; """) assert out["result"] == {"group": {"id": "g1", "name": "G", "roots": [{"name": "a"}]}} assert out["detects"] == 3 def test_a_machine_with_no_node_set_up_is_not_asked_again(tmp_path): out = _run(tmp_path, """ const node = { detects: 0, async detect() { this.detects++; return { detected: false, configured: false }; }, async op() { throw new Error('unreachable'); }, }; """) assert out == {"result": None, "detects": 1} def test_a_node_that_never_answers_is_given_up_on(tmp_path): out = _run(tmp_path, """ const node = { detects: 0, async detect() { this.detects++; return { detected: false, configured: true }; }, async op() { throw new Error('unreachable'); }, }; """) assert out == {"result": None, "detects": 4} def test_a_node_that_does_not_host_the_group_still_counts_as_a_node(tmp_path): out = _run(tmp_path, """ const node = { detects: 0, async detect() { this.detects++; return { detected: true, configured: true }; }, async op() { return { groups: [{ id: 'other' }] }; }, }; """) assert out == {"result": {"group": None}, "detects": 1}