103 lines
4.1 KiB
Python
103 lines
4.1 KiB
Python
import json
|
|
import inspect
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
import fgai.dashboard as dashboard
|
|
from fgai.dashboard import HTML, _ollama_models
|
|
|
|
|
|
class DashboardTests(unittest.TestCase):
|
|
def test_ollama_models_returns_sorted_model_names(self):
|
|
class Response:
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *_args):
|
|
return False
|
|
|
|
def read(self):
|
|
return json.dumps({"models": [{"name": "qwen3:8b", "size": 2}, {"name": "llama3.1", "size": 1}]}).encode("utf-8")
|
|
|
|
with patch("fgai.dashboard.request.urlopen", return_value=Response()):
|
|
result = _ollama_models()
|
|
|
|
self.assertEqual(result["status"], "ok")
|
|
self.assertEqual([item["name"] for item in result["models"]], ["llama3.1", "qwen3:8b"])
|
|
|
|
def test_dashboard_includes_how_to_view(self):
|
|
self.assertIn('data-tab="howto"', HTML)
|
|
self.assertIn('data-view="howto"', HTML)
|
|
self.assertIn("How To Use SignalScope", HTML)
|
|
|
|
def test_dashboard_can_update_existing_recommended_profiles(self):
|
|
self.assertIn("Update profile", HTML)
|
|
self.assertIn("function mergeProfile", HTML)
|
|
|
|
def test_dashboard_can_delete_stream_profiles(self):
|
|
self.assertIn("Delete profile", HTML)
|
|
self.assertIn("function deleteStreamProfile", HTML)
|
|
self.assertIn("delete-stream-profile", HTML)
|
|
|
|
def test_dashboard_has_direct_stream_selection_save(self):
|
|
self.assertIn("Save streams", HTML)
|
|
self.assertIn("function saveStreamSelection", HTML)
|
|
|
|
def test_dashboard_includes_operator_guidance(self):
|
|
self.assertIn("Operator Guidance", HTML)
|
|
self.assertIn("function buildActionPlan", HTML)
|
|
|
|
def test_dashboard_links_evidence_to_graylog(self):
|
|
self.assertIn("function graylogEvidenceLink", HTML)
|
|
self.assertIn("/search?rangetype=relative", HTML)
|
|
|
|
def test_dashboard_has_correlation_explorer(self):
|
|
self.assertIn("correlationExplorer", HTML)
|
|
self.assertIn("function renderCorrelationExplorer", HTML)
|
|
self.assertIn("correlationHitboxes", HTML)
|
|
|
|
def test_dashboard_shows_refreshing_mcp_as_polling_badge(self):
|
|
self.assertIn("function mcpCapability", HTML)
|
|
self.assertIn("connected, ${suffix}", HTML)
|
|
|
|
def test_dashboard_has_visible_incident_action_status(self):
|
|
self.assertIn("incidentNotice", HTML)
|
|
self.assertIn("Incident marked", HTML)
|
|
|
|
def test_dashboard_can_clear_stored_incident_states(self):
|
|
self.assertIn('id="clearResolvedIncidents"', HTML)
|
|
self.assertIn('id="clearAllIncidents"', HTML)
|
|
self.assertIn("/api/incidents/clear", HTML)
|
|
|
|
def test_dashboard_exposes_review_state_reset_endpoints(self):
|
|
source = inspect.getsource(dashboard)
|
|
self.assertIn("/api/feedback/clear", source)
|
|
self.assertIn("/api/review-state/clear", source)
|
|
|
|
def test_dashboard_does_not_clear_streams_when_picker_is_unloaded(self):
|
|
self.assertIn("const streamValues = Object.values(window.streamSelection || {})", HTML)
|
|
self.assertIn("if (streamValues.length)", HTML)
|
|
self.assertIn("values.graylog_streams = streamValues", HTML)
|
|
|
|
def test_dashboard_can_recover_stream_selection_from_profiles(self):
|
|
self.assertIn("No enabled streams were saved", HTML)
|
|
self.assertIn("streams with existing profiles are selected for recovery", HTML)
|
|
|
|
def test_dashboard_shows_profile_advisor_fallback_as_notice(self):
|
|
self.assertIn("heuristic_fallback", HTML)
|
|
self.assertIn("Profile advisor: heuristic fallback", HTML)
|
|
self.assertIn("heuristic fallback", HTML)
|
|
|
|
def test_dashboard_labels_ollama_assessment_errors(self):
|
|
self.assertIn("function llmCapability", HTML)
|
|
self.assertIn("assessment unavailable", HTML)
|
|
self.assertIn("Ollama assessment: unavailable", HTML)
|
|
|
|
def test_dashboard_exposes_ollama_assessment_timeout_setting(self):
|
|
self.assertIn('name="llm_timeout"', HTML)
|
|
self.assertIn("llm_timeout: config.llm_timeout || 180", HTML)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|