""" 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()