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
|
"""
Create Group wizard: choosing which apps a brand-new group offers, before
the (potentially long) initial scan — see app.js's CreateGroupWizard. This
is a loopback-only, operator-authenticated endpoint (11.5.3), same shape as
the existing member-upload one: a thin adapter over `ops.set_enabled_apps`,
with only the validation `_do_apps_enabled` (the signed MNP front door)
already does client-side in the wizard, but worth enforcing at this front
door too since nothing else would.
"""
from pathlib import Path
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from fastapi.testclient import TestClient
from meshbay_node.indexer.group_index import GroupIndex
from meshbay_node.roster import Roster
from meshbay_node.ui.app import create_ui_app
from conftest import one_root
pytestmark = pytest.mark.asyncio
async def _client(tmp_path: Path):
shared = tmp_path / "shared"
shared.mkdir(exist_ok=True)
index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
roster = Roster(db_path=tmp_path / "roster.db")
await roster.open()
state = {
"status": "running",
"groups_ctx": {"g" * 32: {"index": index, "roots": one_root(shared)}},
"indexes": {"g" * 32: index},
"roster": roster,
}
app = create_ui_app(state)
return TestClient(app), roster
async def test_narrowing_the_apps_persists_to_the_roster(tmp_path):
client, roster = await _client(tmp_path)
try:
resp = client.put(f"/api/groups/{'g' * 32}/apps", json={"apps": ["files", "video"]})
assert resp.status_code == 200, resp.text
assert sorted(resp.json()["apps"]) == ["files", "video"]
assert sorted(await roster.enabled_apps("g" * 32)) == ["files", "video"]
finally:
await roster.close()
async def test_empty_apps_list_is_refused(tmp_path):
client, roster = await _client(tmp_path)
try:
resp = client.put(f"/api/groups/{'g' * 32}/apps", json={"apps": []})
assert resp.status_code == 400
finally:
await roster.close()
async def test_missing_apps_field_is_refused(tmp_path):
client, roster = await _client(tmp_path)
try:
resp = client.put(f"/api/groups/{'g' * 32}/apps", json={})
assert resp.status_code == 400
finally:
await roster.close()
async def test_unhosted_group_is_refused(tmp_path):
client, roster = await _client(tmp_path)
try:
resp = client.put("/api/groups/" + "z" * 32 + "/apps", json={"apps": ["files"]})
assert resp.status_code == 404
finally:
await roster.close()
|