towerops/test/towerops/llm/network_insight_prompt_test.exs

148 lines
5 KiB
Elixir

defmodule Towerops.LLM.NetworkInsightPromptTest do
use ExUnit.Case, async: true
alias Towerops.LLM.NetworkInsightPrompt
@snapshot %{
organization_id: "org-1",
organization_name: "Acme WISP",
totals: %{devices: 50, monitored_devices: 45, sites: 5, agents: 2, preseem_aps: 12},
preseem: %{
lowest_qoe_aps: [
%{preseem_id: "ap-1", name: "AP North", qoe_score: 42.0, subscriber_count: 30, model: "ePMP 3000"}
],
highest_subscriber_aps: [],
model_breakdown: [%{model: "ePMP 3000", count: 5, avg_qoe: 78.0}]
},
snmp: %{
stale_polled_devices: [],
hottest_devices: [%{device_id: "dev-1", name: "Tower-A", max_temp_c: 78.0}],
weak_signal_clients: [],
low_optical_rx: []
},
backhaul: %{high_utilization: []},
gaiia: %{untracked: 0, ghosts: 0, mismatches: 0},
agents: %{online: 2, offline: 0},
existing_insights: [%{type: "qoe_degradation", count: 3, criticals: 1, warnings: 2}]
}
describe "build/1" do
test "returns a system + user message pair" do
[%{role: "system", content: sys}, %{role: "user", content: user}] =
NetworkInsightPrompt.build(@snapshot)
assert sys =~ ~r/WISP|network/
assert sys =~ "JSON"
assert user =~ ~r/Acme WISP|org-1/
end
test "user message includes the snapshot as JSON" do
[_sys, %{content: user}] = NetworkInsightPrompt.build(@snapshot)
assert user =~ "lowest_qoe_aps"
assert user =~ "Tower-A"
assert user =~ "qoe_degradation"
end
test "system prompt instructs the model to skip findings already covered by existing_insights" do
[%{content: sys}, _user] = NetworkInsightPrompt.build(@snapshot)
assert sys =~ ~r/existing_insights|do not/
end
end
describe "parse/1" do
test "extracts observations from JSON embedded in markdown with extra text" do
content = """
Let me analyze the network snapshot:
```json
{"observations": [{"title": "Channel Congestion", "description": "Channel 6 has 5 APs on same channel", "urgency": "warning"}]}
```
This is my analysis.
"""
assert {:ok, [obs]} = NetworkInsightPrompt.parse(content)
assert obs.title == "Channel Congestion"
assert obs.urgency == "warning"
end
test "extracts JSON from text with leading/trailing content using brace extraction" do
content = """
Here's my analysis:
{"observations": [{"title": "Test", "description": "Some desc", "urgency": "info"}]}
That's all.
"""
assert {:ok, [obs]} = NetworkInsightPrompt.parse(content)
assert obs.title == "Test"
end
test "extracts deeply nested brace structure" do
content =
~s|Some text {"observations": [{"title": "Nested", "description": "escaped \\"quote\\" here", "urgency": "critical"}]} more text|
assert {:ok, [obs]} = NetworkInsightPrompt.parse(content)
assert obs.title == "Nested"
assert obs.description =~ "quote"
end
test "returns empty list for text with unmatched braces" do
content = "text with {unmatched brace"
assert {:ok, []} = NetworkInsightPrompt.parse(content)
end
test "extracts a list of observations from a JSON array" do
json =
~s|{"observations": [{"title": "Sites with thermal stress", "description": "Tower-A is at 78C.", "urgency": "warning"}]}|
assert {:ok, [obs]} = NetworkInsightPrompt.parse(json)
assert obs.title == "Sites with thermal stress"
assert obs.urgency == "warning"
assert obs.description =~ "78"
end
test "tolerates code-fenced JSON" do
content = """
```json
{"observations": [{"title": "X", "description": "Y", "urgency": "info"}]}
```
"""
assert {:ok, [obs]} = NetworkInsightPrompt.parse(content)
assert obs.title == "X"
end
test "returns empty list when JSON is malformed" do
assert {:ok, []} = NetworkInsightPrompt.parse("not json at all")
end
test "returns empty list when observations key is missing" do
assert {:ok, []} = NetworkInsightPrompt.parse(~s|{"foo": "bar"}|)
end
test "drops observations missing required fields" do
json =
~s|{"observations": [{"title": "ok", "description": "fine", "urgency": "info"}, {"description": "no title"}]}|
assert {:ok, [obs]} = NetworkInsightPrompt.parse(json)
assert obs.title == "ok"
end
test "clamps urgency to one of critical/warning/info, defaults to info" do
json = ~s|{"observations": [{"title": "x", "description": "y", "urgency": "panic"}]}|
assert {:ok, [obs]} = NetworkInsightPrompt.parse(json)
assert obs.urgency == "info"
end
test "preserves optional metadata and recommended_action" do
json =
~s|{"observations": [{"title": "x", "description": "y", "urgency": "warning", "recommended_action": "Reboot AP", "device_id": "dev-7"}]}|
assert {:ok, [obs]} = NetworkInsightPrompt.parse(json)
assert obs.recommended_action == "Reboot AP"
assert obs.metadata["device_id"] == "dev-7"
end
end
end