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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
"""
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}
|