add preseem subscriber metrics schema and migration

This commit is contained in:
Graham McIntire 2026-02-12 17:12:53 -06:00
parent 26e19b00f7
commit 5118b7eb56
No known key found for this signature in database
3 changed files with 132 additions and 0 deletions

View file

@ -0,0 +1,39 @@
defmodule Towerops.Preseem.SubscriberMetric do
@moduledoc """
Time-series aggregate QoE metrics per Preseem access point.
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "preseem_subscriber_metrics" do
field :avg_latency, :float
field :avg_jitter, :float
field :avg_loss, :float
field :avg_throughput, :float
field :p95_latency, :float
field :subscriber_count, :integer
field :recorded_at, :utc_datetime
belongs_to :preseem_access_point, Towerops.Preseem.AccessPoint
end
def changeset(metric, attrs) do
metric
|> cast(attrs, [
:preseem_access_point_id,
:avg_latency,
:avg_jitter,
:avg_loss,
:avg_throughput,
:p95_latency,
:subscriber_count,
:recorded_at
])
|> validate_required([:preseem_access_point_id, :recorded_at])
|> foreign_key_constraint(:preseem_access_point_id)
end
end

View file

@ -0,0 +1,24 @@
defmodule Towerops.Repo.Migrations.CreatePreseemSubscriberMetrics do
use Ecto.Migration
def change do
create table(:preseem_subscriber_metrics, primary_key: false) do
add :id, :binary_id, primary_key: true
add :preseem_access_point_id,
references(:preseem_access_points, type: :binary_id, on_delete: :delete_all),
null: false
add :avg_latency, :float
add :avg_jitter, :float
add :avg_loss, :float
add :avg_throughput, :float
add :p95_latency, :float
add :subscriber_count, :integer
add :recorded_at, :utc_datetime, null: false
end
create index(:preseem_subscriber_metrics, [:preseem_access_point_id])
create index(:preseem_subscriber_metrics, [:recorded_at])
end
end

View file

@ -0,0 +1,69 @@
defmodule Towerops.Preseem.SubscriberMetricTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Preseem.AccessPoint
alias Towerops.Preseem.SubscriberMetric
setup do
user = user_fixture()
org = organization_fixture(user.id)
{:ok, ap} =
%AccessPoint{}
|> AccessPoint.changeset(%{organization_id: org.id, preseem_id: "ap-1", name: "Test AP"})
|> Repo.insert()
%{access_point: ap}
end
describe "changeset/2" do
test "valid changeset", %{access_point: ap} do
attrs = %{
preseem_access_point_id: ap.id,
avg_latency: 12.5,
avg_jitter: 3.2,
avg_loss: 0.5,
avg_throughput: 45.8,
p95_latency: 28.3,
subscriber_count: 42,
recorded_at: DateTime.truncate(DateTime.utc_now(), :second)
}
changeset = SubscriberMetric.changeset(%SubscriberMetric{}, attrs)
assert changeset.valid?
end
test "requires preseem_access_point_id" do
attrs = %{recorded_at: DateTime.truncate(DateTime.utc_now(), :second)}
changeset = SubscriberMetric.changeset(%SubscriberMetric{}, attrs)
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).preseem_access_point_id
end
test "requires recorded_at" do
attrs = %{preseem_access_point_id: Ecto.UUID.generate()}
changeset = SubscriberMetric.changeset(%SubscriberMetric{}, attrs)
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).recorded_at
end
test "persists and reloads metrics", %{access_point: ap} do
attrs = %{
preseem_access_point_id: ap.id,
avg_latency: 12.5,
p95_latency: 28.3,
subscriber_count: 42,
recorded_at: DateTime.truncate(DateTime.utc_now(), :second)
}
{:ok, metric} = %SubscriberMetric{} |> SubscriberMetric.changeset(attrs) |> Repo.insert()
reloaded = Repo.get!(SubscriberMetric, metric.id)
assert reloaded.avg_latency == 12.5
assert reloaded.p95_latency == 28.3
assert reloaded.subscriber_count == 42
end
end
end