feat(web-console): cherry-pick React web console GUI from gary-the-ai fork
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 59s
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 59s
Cherry-pick the Hermes Web Console from gary-the-ai/hermes-web-console-gui. React + TypeScript frontend with Vite, Python aiohttp backend API. Components: - web_console/ — React frontend (chat, sessions, memory, settings, skills, gateway config, cron, workspace, tools, browser, insights pages) - gateway/web_console/ — Python backend API (23 endpoints, SSE event bus, 11 service modules) - gateway/platforms/api_server_ui.py — embedded browser UI for API server - gateway/platforms/api_server.py — route registration refactored into _register_routes(), web console mounted via maybe_register_web_console() - run-gui.sh / setup-gui.sh — one-command launch and setup scripts - tests/gateway/test_api_server_gui_mount.py — 4 integration tests (passing) - tests/web_console/ — 13 backend test files (51 passing) - docs/plans/ — implementation plan, API schema, frontend architecture Fix: added missing ModelContextError class and CRON_MIN_CONTEXT_TOKENS to cron/scheduler.py (pre-existing import bug). Closes #325
This commit is contained in:
64
tests/web_console/test_logs_api.py
Normal file
64
tests/web_console/test_logs_api.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Tests for the web console logs API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from aiohttp import web
|
||||
from aiohttp.test_utils import TestClient, TestServer
|
||||
|
||||
from gateway.web_console.api.logs import LOG_SERVICE_APP_KEY
|
||||
from gateway.web_console.routes import register_web_console_routes
|
||||
|
||||
|
||||
class FakeLogService:
|
||||
def get_logs(self, *, file_name=None, limit=200):
|
||||
if file_name == "missing.log":
|
||||
raise FileNotFoundError(file_name)
|
||||
return {
|
||||
"directory": "/tmp/hermes/logs",
|
||||
"file": file_name or "gateway.log",
|
||||
"available_files": ["gateway.log", "gateway.error.log"],
|
||||
"line_count": min(2, limit),
|
||||
"lines": ["line 1", "line 2"][:limit],
|
||||
}
|
||||
|
||||
|
||||
class TestLogsApi:
|
||||
@staticmethod
|
||||
async def _make_client(service: FakeLogService) -> TestClient:
|
||||
app = web.Application()
|
||||
app[LOG_SERVICE_APP_KEY] = service
|
||||
register_web_console_routes(app)
|
||||
client = TestClient(TestServer(app))
|
||||
await client.start_server()
|
||||
return client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_logs_route(self):
|
||||
client = await self._make_client(FakeLogService())
|
||||
try:
|
||||
resp = await client.get("/api/gui/logs?file=gateway.error.log&limit=1")
|
||||
assert resp.status == 200
|
||||
payload = await resp.json()
|
||||
assert payload["ok"] is True
|
||||
assert payload["logs"]["file"] == "gateway.error.log"
|
||||
assert payload["logs"]["line_count"] == 1
|
||||
assert payload["logs"]["lines"] == ["line 1"]
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_logs_route_validation_and_not_found(self):
|
||||
client = await self._make_client(FakeLogService())
|
||||
try:
|
||||
invalid_limit_resp = await client.get("/api/gui/logs?limit=abc")
|
||||
assert invalid_limit_resp.status == 400
|
||||
assert (await invalid_limit_resp.json())["error"]["code"] == "invalid_limit"
|
||||
|
||||
missing_resp = await client.get("/api/gui/logs?file=missing.log")
|
||||
assert missing_resp.status == 404
|
||||
missing_payload = await missing_resp.json()
|
||||
assert missing_payload["error"]["code"] == "log_not_found"
|
||||
assert missing_payload["error"]["file"] == "missing.log"
|
||||
finally:
|
||||
await client.close()
|
||||
Reference in New Issue
Block a user