142 lines
4.1 KiB
Python
142 lines
4.1 KiB
Python
"""Frontend contract tests for Agent Management."""
|
|
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.frontend.router import router
|
|
from app.identity import get_current_user, required_page_permission
|
|
|
|
|
|
STATIC_ROOT = Path(__file__).parents[1] / "app" / "frontend" / "static"
|
|
|
|
|
|
class AgentManagementFrontendTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.app = FastAPI()
|
|
self.app.include_router(router)
|
|
self.client = TestClient(self.app)
|
|
|
|
def authenticate(self, permissions):
|
|
self.app.dependency_overrides[get_current_user] = lambda: {
|
|
"id": "00000000-0000-4000-8000-000000009001",
|
|
"username": "agent-test-user",
|
|
"display_name": "Agent Test User",
|
|
"role": "Test Role",
|
|
"permissions": tuple(permissions),
|
|
}
|
|
|
|
def test_page_requires_agents_view(self):
|
|
self.authenticate(("unrelated.permission",))
|
|
self.assertEqual(
|
|
self.client.get("/agent/management").status_code,
|
|
403,
|
|
)
|
|
|
|
self.authenticate(("agents.view",))
|
|
response = self.client.get("/agent/management")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("Agent Management", response.text)
|
|
self.assertIn('href="/agent/management"', response.text)
|
|
|
|
def test_manage_controls_follow_agents_manage_permission(self):
|
|
self.authenticate(("agents.view",))
|
|
|
|
restricted = self.client.get("/agent/management").text
|
|
|
|
self.assertNotIn('id="agentCreateForm"', restricted)
|
|
restricted_text = " ".join(restricted.split())
|
|
self.assertIn(
|
|
"cannot create Agent enrollments.",
|
|
restricted_text,
|
|
)
|
|
|
|
self.authenticate(("agents.view", "agents.manage"))
|
|
|
|
managed = self.client.get("/agent/management").text
|
|
|
|
self.assertIn('id="agentCreateForm"', managed)
|
|
self.assertIn('id="agentSite"', managed)
|
|
self.assertIn('id="agentZone"', managed)
|
|
self.assertIn('id="agentDisplayName"', managed)
|
|
self.assertIn("Linux x64 — coming later", managed)
|
|
self.assertIn("Linux ARM64 / Raspberry Pi — coming later", managed)
|
|
|
|
def test_route_and_asset_contract(self):
|
|
self.assertEqual(
|
|
required_page_permission("/agent/management"),
|
|
"agents.view",
|
|
)
|
|
|
|
self.authenticate(("agents.view",))
|
|
|
|
page = self.client.get("/agent/management")
|
|
self.assertIn(
|
|
"/assets/minerops-agent-management.js",
|
|
page.text,
|
|
)
|
|
|
|
asset = self.client.get(
|
|
"/assets/minerops-agent-management.js"
|
|
)
|
|
|
|
self.assertEqual(asset.status_code, 200)
|
|
self.assertEqual(
|
|
asset.headers.get("cache-control"),
|
|
"no-store",
|
|
)
|
|
|
|
def test_javascript_is_secret_conscious_and_safe_dom_only(self):
|
|
source = (
|
|
STATIC_ROOT / "agent-management.js"
|
|
).read_text(encoding="utf-8")
|
|
|
|
self.assertIn('api("/api/agents")', source)
|
|
self.assertIn('api("/api/agent-enrollments")', source)
|
|
self.assertIn(
|
|
'api("/api/agent-enrollments", {',
|
|
source,
|
|
)
|
|
|
|
for unsafe in (
|
|
"innerHTML",
|
|
"outerHTML",
|
|
"insertAdjacentHTML",
|
|
"localStorage",
|
|
"sessionStorage",
|
|
"console.log",
|
|
):
|
|
self.assertNotIn(unsafe, source)
|
|
|
|
self.assertIn(
|
|
"el.agentEnrollmentToken.textContent",
|
|
source,
|
|
)
|
|
self.assertIn(
|
|
"el.agentEnrollmentToken.textContent = \"\"",
|
|
source,
|
|
)
|
|
|
|
def test_agent_management_quiet_actions_have_visible_text(self):
|
|
css = (STATIC_ROOT / "app.css").read_text(encoding="utf-8")
|
|
|
|
self.assertIn(
|
|
".agent-management-columns .quiet-button,",
|
|
css,
|
|
)
|
|
self.assertIn(
|
|
".agent-enrollment-panel .quiet-button {",
|
|
css,
|
|
)
|
|
self.assertIn(
|
|
"color: var(--ink);",
|
|
css,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|