Initial commit: Agentic OS troubleshooting platform
Self-hosted, Docker-based agentic troubleshooting platform: FastAPI backend + LangGraph agent, Next.js UI, tiered LLM routing (local Ollama -> Gemini -> DeepSeek -> OpenRouter), MCP server manager, encrypted device credentials, RBAC, audit log, project-memory + Obsidian integrations, and editable troubleshooting decision rules tuned for the GeneseasX vessel stack. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
112
backend/tests/test_troubleshooting_rules.py
Normal file
112
backend/tests/test_troubleshooting_rules.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Tests for troubleshooting decision rules."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-unit-tests-only")
|
||||
_test_dir = Path(__file__).resolve().parent
|
||||
_backend_root = _test_dir.parent
|
||||
_rules_file = _backend_root / "rules" / "troubleshooting.yaml"
|
||||
if not _rules_file.is_file():
|
||||
_rules_file = _backend_root.parent / "rules" / "troubleshooting.yaml"
|
||||
os.environ["TROUBLESHOOTING_RULES_PATH"] = str(_rules_file)
|
||||
|
||||
from app.services.troubleshooting_rules import ( # noqa: E402
|
||||
invalidate_rules_cache,
|
||||
load_rules,
|
||||
match_rule,
|
||||
select_devices_for_issue,
|
||||
)
|
||||
|
||||
|
||||
def _devices():
|
||||
return [
|
||||
{"catalog_key": "proxmox", "name": "pve"},
|
||||
{"catalog_key": "pfsense", "name": "fw"},
|
||||
{"catalog_key": "docker_vm", "name": "docker"},
|
||||
{"catalog_key": "asterisk_geneseasx", "name": "voip"},
|
||||
]
|
||||
|
||||
|
||||
def test_match_no_registration_rule():
|
||||
invalidate_rules_cache()
|
||||
rule = match_rule("SIP phones down", "Phone won't register on crew VLAN")
|
||||
assert rule is not None
|
||||
assert rule.id == "no-registration"
|
||||
assert "pfsense" in rule.devices
|
||||
|
||||
|
||||
def test_match_broad_health():
|
||||
invalidate_rules_cache()
|
||||
rule = match_rule("Full check", "health check of all systems")
|
||||
assert rule is not None
|
||||
assert rule.broad is True
|
||||
|
||||
|
||||
def test_task4_issue_selects_proxmox_only():
|
||||
"""Scoped VM task must not trigger full-stack sweep."""
|
||||
invalidate_rules_cache()
|
||||
selected, rule = select_devices_for_issue(
|
||||
_devices(),
|
||||
"Check status of all running VMs and provide config for each VM",
|
||||
title="Proxmox VM status",
|
||||
)
|
||||
keys = [d["catalog_key"] for d in selected]
|
||||
assert keys == ["proxmox"]
|
||||
assert rule is None or rule.id == "proxmox-vm-inventory"
|
||||
|
||||
|
||||
def test_multi_device_status_includes_all_named():
|
||||
"""Issue naming two devices must diagnose both even when a single-device rule matches."""
|
||||
invalidate_rules_cache()
|
||||
selected, rule = select_devices_for_issue(
|
||||
_devices(),
|
||||
"check status of proxmox and pfsense",
|
||||
title="Irinas box",
|
||||
)
|
||||
keys = [d["catalog_key"] for d in selected]
|
||||
assert keys == ["proxmox", "pfsense"]
|
||||
assert rule is not None
|
||||
assert rule.id == "proxmox-vm-inventory"
|
||||
|
||||
|
||||
def test_unmatched_issue_selects_nothing():
|
||||
invalidate_rules_cache()
|
||||
selected, _rule = select_devices_for_issue(
|
||||
_devices(),
|
||||
"something completely unrelated with no device keywords",
|
||||
title="Mystery",
|
||||
)
|
||||
assert selected == []
|
||||
|
||||
|
||||
def test_broad_issue_selects_all_devices():
|
||||
invalidate_rules_cache()
|
||||
selected, rule = select_devices_for_issue(
|
||||
_devices(),
|
||||
"Run a full stack health check on everything",
|
||||
title="Weekly audit",
|
||||
)
|
||||
assert rule is not None
|
||||
assert rule.broad is True
|
||||
assert len(selected) == 4
|
||||
|
||||
|
||||
def test_select_devices_order():
|
||||
invalidate_rules_cache()
|
||||
selected, rule = select_devices_for_issue(
|
||||
_devices(),
|
||||
"one-way audio on SIP calls",
|
||||
title="VoIP issue",
|
||||
)
|
||||
assert rule is not None
|
||||
assert rule.id == "one-way-audio"
|
||||
keys = [d["catalog_key"] for d in selected]
|
||||
assert keys == ["pfsense", "asterisk_geneseasx"]
|
||||
|
||||
|
||||
def test_load_rules_has_device_keywords():
|
||||
invalidate_rules_cache()
|
||||
data = load_rules(force=True)
|
||||
assert "asterisk_geneseasx" in data.get("device_keywords", {})
|
||||
Reference in New Issue
Block a user