Switch deployment pipeline from GitLab Agent kubectl to FluxCD image automation. CI now only builds and pushes tagged images to Forgejo registry; FluxCD detects new tags and updates the deployment manifest. Fix Ecto.MultipleResultsError on /dashboard when a device has multiple Preseem access point records by using limit(1) instead of Repo.get_by.
193 lines
5.9 KiB
Elixir
193 lines
5.9 KiB
Elixir
defmodule Towerops.PreseemTest do
|
|
use Towerops.DataCase, async: true
|
|
|
|
import Towerops.AccountsFixtures
|
|
import Towerops.DevicesFixtures
|
|
import Towerops.OrganizationsFixtures
|
|
|
|
alias Towerops.Preseem
|
|
alias Towerops.Preseem.AccessPoint
|
|
alias Towerops.Preseem.SubscriberMetric
|
|
|
|
setup do
|
|
user = user_fixture()
|
|
org = organization_fixture(user.id)
|
|
%{organization: org}
|
|
end
|
|
|
|
defp insert_access_point!(org, attrs \\ %{}) do
|
|
default = %{
|
|
organization_id: org.id,
|
|
preseem_id: "ap-#{System.unique_integer([:positive])}",
|
|
name: "Test AP"
|
|
}
|
|
|
|
{:ok, ap} =
|
|
%AccessPoint{}
|
|
|> AccessPoint.changeset(Map.merge(default, attrs))
|
|
|> Repo.insert()
|
|
|
|
ap
|
|
end
|
|
|
|
defp insert_metric!(ap, attrs) do
|
|
default = %{
|
|
preseem_access_point_id: ap.id,
|
|
recorded_at: DateTime.truncate(DateTime.utc_now(), :second)
|
|
}
|
|
|
|
{:ok, metric} =
|
|
%SubscriberMetric{}
|
|
|> SubscriberMetric.changeset(Map.merge(default, attrs))
|
|
|> Repo.insert()
|
|
|
|
metric
|
|
end
|
|
|
|
describe "get_access_point_for_device/1" do
|
|
test "returns nil when no AP linked to device" do
|
|
assert is_nil(Preseem.get_access_point_for_device(Ecto.UUID.generate()))
|
|
end
|
|
|
|
test "returns AP when linked to device", %{organization: org} do
|
|
device = device_fixture(%{organization_id: org.id})
|
|
|
|
ap =
|
|
insert_access_point!(org, %{
|
|
device_id: device.id,
|
|
match_confidence: "auto_ip"
|
|
})
|
|
|
|
result = Preseem.get_access_point_for_device(device.id)
|
|
assert result.id == ap.id
|
|
end
|
|
|
|
test "returns one AP when multiple are linked to same device", %{organization: org} do
|
|
device = device_fixture(%{organization_id: org.id})
|
|
|
|
_ap1 =
|
|
insert_access_point!(org, %{
|
|
device_id: device.id,
|
|
match_confidence: "auto_ip"
|
|
})
|
|
|
|
_ap2 =
|
|
insert_access_point!(org, %{
|
|
device_id: device.id,
|
|
match_confidence: "auto_mac"
|
|
})
|
|
|
|
result = Preseem.get_access_point_for_device(device.id)
|
|
assert %AccessPoint{} = result
|
|
end
|
|
end
|
|
|
|
describe "list_access_points/1" do
|
|
test "returns all APs for an organization ordered by name", %{organization: org} do
|
|
insert_access_point!(org, %{name: "Bravo AP"})
|
|
insert_access_point!(org, %{name: "Alpha AP"})
|
|
|
|
aps = Preseem.list_access_points(org.id)
|
|
assert length(aps) == 2
|
|
assert Enum.map(aps, & &1.name) == ["Alpha AP", "Bravo AP"]
|
|
end
|
|
|
|
test "does not return APs from other organizations", %{organization: org} do
|
|
other_user = user_fixture()
|
|
other_org = organization_fixture(other_user.id)
|
|
|
|
insert_access_point!(org, %{name: "Our AP"})
|
|
insert_access_point!(other_org, %{name: "Their AP"})
|
|
|
|
aps = Preseem.list_access_points(org.id)
|
|
assert length(aps) == 1
|
|
assert hd(aps).name == "Our AP"
|
|
end
|
|
end
|
|
|
|
describe "list_unmatched_access_points/1" do
|
|
test "returns only unmatched and ambiguous APs", %{organization: org} do
|
|
insert_access_point!(org, %{name: "Matched", match_confidence: "auto_mac"})
|
|
insert_access_point!(org, %{name: "Unmatched", match_confidence: "unmatched"})
|
|
insert_access_point!(org, %{name: "Ambiguous", match_confidence: "ambiguous"})
|
|
insert_access_point!(org, %{name: "Manual", match_confidence: "manual"})
|
|
|
|
unmatched = Preseem.list_unmatched_access_points(org.id)
|
|
assert length(unmatched) == 2
|
|
names = unmatched |> Enum.map(& &1.name) |> Enum.sort()
|
|
assert names == ["Ambiguous", "Unmatched"]
|
|
end
|
|
end
|
|
|
|
describe "list_subscriber_metrics/2" do
|
|
test "returns metrics ordered by recorded_at desc", %{organization: org} do
|
|
ap = insert_access_point!(org)
|
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
|
old = DateTime.add(now, -3600, :second)
|
|
|
|
insert_metric!(ap, %{recorded_at: old, avg_latency: 10.0})
|
|
insert_metric!(ap, %{recorded_at: now, avg_latency: 20.0})
|
|
|
|
metrics = Preseem.list_subscriber_metrics(ap.id)
|
|
assert length(metrics) == 2
|
|
assert hd(metrics).avg_latency == 20.0
|
|
end
|
|
|
|
test "respects limit option", %{organization: org} do
|
|
ap = insert_access_point!(org)
|
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
|
|
|
for i <- 1..5 do
|
|
insert_metric!(ap, %{
|
|
recorded_at: DateTime.add(now, -i, :second),
|
|
avg_latency: i * 1.0
|
|
})
|
|
end
|
|
|
|
metrics = Preseem.list_subscriber_metrics(ap.id, limit: 2)
|
|
assert length(metrics) == 2
|
|
end
|
|
|
|
test "defaults to 100 limit", %{organization: org} do
|
|
ap = insert_access_point!(org)
|
|
# Just verify the function works with default
|
|
metrics = Preseem.list_subscriber_metrics(ap.id)
|
|
assert is_list(metrics)
|
|
end
|
|
end
|
|
|
|
describe "link_access_point/2" do
|
|
test "links AP to device with manual confidence", %{organization: org} do
|
|
ap = insert_access_point!(org, %{match_confidence: "unmatched"})
|
|
device = device_fixture(%{organization_id: org.id})
|
|
|
|
assert {:ok, linked} = Preseem.link_access_point(ap.id, device.id)
|
|
assert linked.device_id == device.id
|
|
assert linked.match_confidence == "manual"
|
|
end
|
|
|
|
test "returns error for non-existent AP" do
|
|
assert {:error, :not_found} = Preseem.link_access_point(Ecto.UUID.generate(), Ecto.UUID.generate())
|
|
end
|
|
end
|
|
|
|
describe "unlink_access_point/1" do
|
|
test "unlinks AP and resets to unmatched", %{organization: org} do
|
|
device = device_fixture(%{organization_id: org.id})
|
|
|
|
ap =
|
|
insert_access_point!(org, %{
|
|
device_id: device.id,
|
|
match_confidence: "manual"
|
|
})
|
|
|
|
assert {:ok, unlinked} = Preseem.unlink_access_point(ap.id)
|
|
assert is_nil(unlinked.device_id)
|
|
assert unlinked.match_confidence == "unmatched"
|
|
end
|
|
|
|
test "returns error for non-existent AP" do
|
|
assert {:error, :not_found} = Preseem.unlink_access_point(Ecto.UUID.generate())
|
|
end
|
|
end
|
|
end
|