mirror of
https://github.com/Av0idD4rk/firegex.git
synced 2026-10-23 15:30:59 +00:00
Bootstrap the fork through run.py, build Docker from source, and prepare standalone with a checksum-pinned native runtime plus the MCP backend and Python dependencies. Preserve existing installations and data, propagate Docker build failures, and update README/setup guides. Validation: 347 unit tests and a real standalone-rootfs MCP smoke test passed in Docker.
92 lines
3.8 KiB
Python
92 lines
3.8 KiB
Python
"""Opt-in packaging smoke: a real upstream rootfs must start this fork's MCP.
|
|
|
|
Run in a disposable Linux namespace with NET_ADMIN and SYS_ADMIN:
|
|
FIREGEX_TEST_INSTALLER=1 ./run_tests.sh standalone/test_installer_rootfs.py
|
|
FIREGEX_STANDALONE_ARCHIVE may name a cached archive; its checksum is still checked.
|
|
"""
|
|
import asyncio
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import pytest
|
|
import requests
|
|
from mcp import Client
|
|
from mcp.client.streamable_http import streamable_http_client, create_mcp_http_client
|
|
from helpers.net import free_port
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
@pytest.mark.root
|
|
def test_standalone_runtime_exposes_fork_mcp(tmp_path, monkeypatch):
|
|
if os.getenv("FIREGEX_TEST_INSTALLER") != "1":
|
|
pytest.skip("Opt in with FIREGEX_TEST_INSTALLER=1 in a disposable namespace")
|
|
monkeypatch.chdir(tmp_path)
|
|
monkeypatch.setattr(sys, "argv", [str(ROOT / "run.py"), "status"])
|
|
spec = importlib.util.spec_from_file_location("standalone_installer", ROOT / "run.py")
|
|
manager = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(manager)
|
|
os.chdir(tmp_path)
|
|
manager.g.rootfs_path = str(tmp_path / "rootfs")
|
|
manager.g.pid_file = str(tmp_path / "backend.pid")
|
|
manager.g.configfile = str(tmp_path / "config.json")
|
|
manager.g.standalone_mode = True
|
|
port = free_port()
|
|
manager.args = manager.gen_args(["start", "--standalone", "--host", "127.0.0.1",
|
|
"--port", str(port), "--threads", "2", "--psw-on-web"])
|
|
cached = os.getenv("FIREGEX_STANDALONE_ARCHIVE")
|
|
if cached:
|
|
monkeypatch.setattr(manager, "download_file", lambda url, path: bool(shutil.copyfile(cached, path)))
|
|
assert manager.setup_standalone_rootfs()
|
|
rootfs = Path(manager.g.rootfs_path)
|
|
pid = None
|
|
try:
|
|
assert manager.setup_standalone_mounts()
|
|
manager.run_standalone()
|
|
pid = manager.read_pid_file()
|
|
address = f"http://127.0.0.1:{port}"
|
|
for _ in range(60):
|
|
try:
|
|
if requests.get(address + "/api/status", timeout=1).status_code == 200:
|
|
break
|
|
except requests.RequestException:
|
|
pass
|
|
time.sleep(0.5)
|
|
else:
|
|
pytest.fail(Path("/tmp/app.log").read_text()[-8000:])
|
|
token = json.loads(subprocess.check_output([
|
|
"chroot", str(rootfs), "/bin/sh", "-c",
|
|
"cd /execute && python3 -m mcp_api.tokens create --name installer-test --role operator --hours 1",
|
|
], text=True))
|
|
assert requests.post(address + "/mcp/", json={}, timeout=5).status_code == 401
|
|
|
|
async def scenario():
|
|
async with create_mcp_http_client(headers={"Authorization": "Bearer " + token["token"]}) as http:
|
|
async with Client(streamable_http_client(address + "/mcp/", http_client=http)) as client:
|
|
catalog = await client.list_tools()
|
|
assert len(catalog.tools) == 28
|
|
overview = await client.call_tool("fgx_overview", {})
|
|
assert overview.structured_content["ok"], overview
|
|
checked = await client.call_tool("fgx_python_check", {"code": "pass"})
|
|
assert checked.structured_content["data"]["ok"], checked
|
|
asyncio.run(scenario())
|
|
assert (rootfs / "execute/.firegex-backend-deps").is_file()
|
|
assert (rootfs / "execute/db/mcp/control.sqlite3").is_file()
|
|
finally:
|
|
pid = pid or manager.read_pid_file()
|
|
if pid:
|
|
try:
|
|
os.killpg(pid, signal.SIGTERM)
|
|
os.waitpid(pid, 0)
|
|
except ProcessLookupError:
|
|
pass
|
|
manager.cleanup_standalone_mounts()
|
|
manager.remove_pid_file()
|