add test to see if fail for streams and evetns
This commit is contained in:
@@ -534,12 +534,33 @@ async function loadOllamaModels() {
|
|||||||
async function loadStreams() {
|
async function loadStreams() {
|
||||||
const response = await fetch('/api/graylog/streams');
|
const response = await fetch('/api/graylog/streams');
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
|
if (!response.ok) {
|
||||||
|
renderStreamPicker(payload.error || `Could not load streams: ${response.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const selected = new Set((payload.selected || []).filter(item => item.enabled).map(item => item.id));
|
const selected = new Set((payload.selected || []).filter(item => item.enabled).map(item => item.id));
|
||||||
window.availableStreams = payload.streams || [];
|
window.availableStreams = payload.streams || [];
|
||||||
window.streamSelection = Object.fromEntries(window.availableStreams.map(stream => [stream.id, {id:stream.id, title:stream.title, enabled:selected.has(stream.id)}]));
|
window.streamSelection = Object.fromEntries(window.availableStreams.map(stream => [stream.id, {id:stream.id, title:stream.title, enabled:selected.has(stream.id)}]));
|
||||||
renderStreamPicker(payload.error || '');
|
renderStreamPicker(payload.error || '');
|
||||||
}
|
}
|
||||||
document.getElementById('loadStreams').addEventListener('click', loadStreams);
|
document.getElementById('loadStreams').addEventListener('click', loadStreams);
|
||||||
|
async function saveStreamSelection() {
|
||||||
|
const result = document.getElementById('settingsResult');
|
||||||
|
const streams = Object.values(window.streamSelection || {});
|
||||||
|
if (!streams.length) {
|
||||||
|
result.textContent = 'Load streams before saving stream selection.';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const response = await fetch('/api/config', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({graylog_streams: streams})});
|
||||||
|
let payload = {};
|
||||||
|
try { payload = await response.json(); } catch {}
|
||||||
|
if (!response.ok) {
|
||||||
|
result.textContent = `Could not save enabled streams: ${payload.error || response.statusText || response.status}`;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result.textContent = `Saved ${streams.filter(item => item.enabled).length} enabled stream(s). Monitor will use them on the next cycle.`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
function renderStreamPicker(errorText='') {
|
function renderStreamPicker(errorText='') {
|
||||||
const target = document.getElementById('streamPicker');
|
const target = document.getElementById('streamPicker');
|
||||||
if (!window.availableStreams.length) {
|
if (!window.availableStreams.length) {
|
||||||
@@ -559,6 +580,7 @@ function renderStreamPicker(errorText='') {
|
|||||||
<label><input id="streamEnabledOnly" type="checkbox" ${previousEnabledOnly ? 'checked' : ''}> enabled only</label>
|
<label><input id="streamEnabledOnly" type="checkbox" ${previousEnabledOnly ? 'checked' : ''}> enabled only</label>
|
||||||
<button type="button" id="enableVisibleStreams">Enable visible</button>
|
<button type="button" id="enableVisibleStreams">Enable visible</button>
|
||||||
<button type="button" id="disableVisibleStreams">Disable visible</button>
|
<button type="button" id="disableVisibleStreams">Disable visible</button>
|
||||||
|
<button type="button" id="saveVisibleStreams">Save streams</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="stream-counts">${esc(enabledCount)} enabled, ${esc(streams.length)} visible, ${esc(window.availableStreams.length)} total</div>
|
<div class="stream-counts">${esc(enabledCount)} enabled, ${esc(streams.length)} visible, ${esc(window.availableStreams.length)} total</div>
|
||||||
<div class="stream-list">${streams.map(stream => {
|
<div class="stream-list">${streams.map(stream => {
|
||||||
@@ -569,6 +591,7 @@ function renderStreamPicker(errorText='') {
|
|||||||
document.getElementById('streamEnabledOnly').addEventListener('change', () => renderStreamPicker());
|
document.getElementById('streamEnabledOnly').addEventListener('change', () => renderStreamPicker());
|
||||||
document.getElementById('enableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = true; }); renderStreamPicker(); });
|
document.getElementById('enableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = true; }); renderStreamPicker(); });
|
||||||
document.getElementById('disableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = false; }); renderStreamPicker(); });
|
document.getElementById('disableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = false; }); renderStreamPicker(); });
|
||||||
|
document.getElementById('saveVisibleStreams').addEventListener('click', saveStreamSelection);
|
||||||
}
|
}
|
||||||
function mergeProfile(existing, recommended) {
|
function mergeProfile(existing, recommended) {
|
||||||
const mergeList = (left, right) => [...new Set([...(left || []), ...(right || [])].filter(Boolean))];
|
const mergeList = (left, right) => [...new Set([...(left || []), ...(right || [])].filter(Boolean))];
|
||||||
@@ -670,6 +693,7 @@ document.getElementById('streamPicker').addEventListener('change', event => {
|
|||||||
if (!checkbox || !window.streamSelection[checkbox.dataset.id]) return;
|
if (!checkbox || !window.streamSelection[checkbox.dataset.id]) return;
|
||||||
window.streamSelection[checkbox.dataset.id].enabled = checkbox.checked;
|
window.streamSelection[checkbox.dataset.id].enabled = checkbox.checked;
|
||||||
document.querySelector('.stream-counts').textContent = `${Object.values(window.streamSelection).filter(item => item.enabled).length} enabled, ${document.querySelectorAll('.stream-row').length} visible, ${window.availableStreams.length} total`;
|
document.querySelector('.stream-counts').textContent = `${Object.values(window.streamSelection).filter(item => item.enabled).length} enabled, ${document.querySelectorAll('.stream-row').length} visible, ${window.availableStreams.length} total`;
|
||||||
|
document.getElementById('settingsResult').textContent = 'Stream selection changed. Click Save streams or Save configuration.';
|
||||||
});
|
});
|
||||||
document.getElementById('loadFields').addEventListener('click', async () => {
|
document.getElementById('loadFields').addEventListener('click', async () => {
|
||||||
if (window.activeProfileStream) { editStreamProfile(window.activeProfileStream, window.activeProfileTitle); return; }
|
if (window.activeProfileStream) { editStreamProfile(window.activeProfileStream, window.activeProfileTitle); return; }
|
||||||
|
|||||||
@@ -135,7 +135,13 @@ def build_status(
|
|||||||
stream_configs = [item for item in configured_streams if isinstance(item, dict) and item.get("enabled") and item.get("id")]
|
stream_configs = [item for item in configured_streams if isinstance(item, dict) and item.get("enabled") and item.get("id")]
|
||||||
stream_ids = [str(item.get("id")) for item in stream_configs]
|
stream_ids = [str(item.get("id")) for item in stream_configs]
|
||||||
if not stream_ids:
|
if not stream_ids:
|
||||||
stream_configs = [{"id": str(runtime_values.get("graylog_stream", "")), "title": "Graylog"}]
|
legacy_stream = str(runtime_values.get("graylog_stream", "") or "")
|
||||||
|
if legacy_stream:
|
||||||
|
stream_configs = [{"id": legacy_stream, "title": "Graylog"}]
|
||||||
|
else:
|
||||||
|
mcp_status = {"status": "no_streams_enabled", "streams": [], "events_fetched": 0, "coverage_status": "no_streams_enabled"}
|
||||||
|
events = []
|
||||||
|
raise StopIteration
|
||||||
stream_statuses = []
|
stream_statuses = []
|
||||||
events = []
|
events = []
|
||||||
range_seconds = _range_seconds(runtime_values.get("graylog_range_seconds", 300))
|
range_seconds = _range_seconds(runtime_values.get("graylog_range_seconds", 300))
|
||||||
@@ -193,6 +199,8 @@ def build_status(
|
|||||||
"coverage_status": "partial" if partial_streams else "truncated" if truncated_streams else "complete_window",
|
"coverage_status": "partial" if partial_streams else "truncated" if truncated_streams else "complete_window",
|
||||||
"coverage_warning": " ".join(warnings),
|
"coverage_warning": " ".join(warnings),
|
||||||
}
|
}
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
except RuntimeError as exc:
|
except RuntimeError as exc:
|
||||||
mcp_status = {"status": "error", "error": str(exc)}
|
mcp_status = {"status": "error", "error": str(exc)}
|
||||||
events = []
|
events = []
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class DashboardTests(unittest.TestCase):
|
|||||||
self.assertIn("Update profile", HTML)
|
self.assertIn("Update profile", HTML)
|
||||||
self.assertIn("function mergeProfile", HTML)
|
self.assertIn("function mergeProfile", HTML)
|
||||||
|
|
||||||
|
def test_dashboard_has_direct_stream_selection_save(self):
|
||||||
|
self.assertIn("Save streams", HTML)
|
||||||
|
self.assertIn("function saveStreamSelection", HTML)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -106,6 +106,26 @@ class MonitorTests(unittest.TestCase):
|
|||||||
self.assertEqual(coverage["firewall"]["readiness"], "1/1")
|
self.assertEqual(coverage["firewall"]["readiness"], "1/1")
|
||||||
self.assertEqual(coverage["dns"]["health"], "missing_profile")
|
self.assertEqual(coverage["dns"]["health"], "missing_profile")
|
||||||
|
|
||||||
|
def test_graylog_mcp_without_enabled_streams_reports_clear_status(self):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
config_path = Path(tmp) / "config.json"
|
||||||
|
config_path.write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"log_source": "graylog_mcp",
|
||||||
|
"graylog_mcp_url": "https://graylog.example/api/mcp",
|
||||||
|
"graylog_mcp_token": "token",
|
||||||
|
"graylog_streams": [{"id": "disabled", "title": "Disabled", "enabled": False}],
|
||||||
|
}
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
status = build_status(str(Path(tmp) / "missing.log"), config_path=str(config_path), incident_path=str(Path(tmp) / "incidents.json"))
|
||||||
|
|
||||||
|
self.assertEqual(status["capabilities"]["graylog_mcp"]["status"], "no_streams_enabled")
|
||||||
|
self.assertEqual(status["summary"]["total"], 0)
|
||||||
|
|
||||||
def test_add_llm_assessment_records_error_without_ollama(self):
|
def test_add_llm_assessment_records_error_without_ollama(self):
|
||||||
status = {"summary": {}, "anomalies": []}
|
status = {"summary": {}, "anomalies": []}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user