towerops/test/towerops/integrations_test.exs
Graham McIntire de075ea8d9
Store billing subscriber/MRR data on integrations for dashboard
Each billing sync (Gaiia, Splynx, VISP, Sonar) already computes
subscriber counts and MRR but only stored them in sync messages.
Now persists them on the integrations table so the dashboard can
aggregate across all billing providers instead of only querying
Gaiia network sites (which were never populated).

Also fixes SnmpKit Config environment detection to use Mix.env()
as fallback when MIX_ENV env var is not set.
2026-02-15 16:51:33 -06:00

223 lines
8.4 KiB
Elixir

defmodule Towerops.IntegrationsTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Integrations
alias Towerops.Integrations.Integration
setup do
user = user_fixture()
organization = organization_fixture(user.id)
%{organization: organization}
end
describe "list_integrations/1" do
test "returns all integrations for an organization", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem"})
integrations = Integrations.list_integrations(org.id)
assert length(integrations) == 1
end
test "does not return integrations from other orgs", %{organization: org} do
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, _} = Integrations.create_integration(org2.id, %{provider: "preseem"})
assert Integrations.list_integrations(org.id) == []
end
end
describe "get_integration/2" do
test "returns integration by org and provider", %{organization: org} do
{:ok, created} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:ok, fetched} = Integrations.get_integration(org.id, "preseem")
assert fetched.id == created.id
end
test "returns error when not found", %{organization: org} do
assert {:error, :not_found} = Integrations.get_integration(org.id, "preseem")
end
end
describe "get_integration!/2" do
test "returns integration by org and provider", %{organization: org} do
{:ok, created} = Integrations.create_integration(org.id, %{provider: "preseem"})
fetched = Integrations.get_integration!(org.id, "preseem")
assert fetched.id == created.id
end
test "raises when not found", %{organization: org} do
assert_raise Ecto.NoResultsError, fn ->
Integrations.get_integration!(org.id, "preseem")
end
end
end
describe "create_integration/2" do
test "creates with valid attrs", %{organization: org} do
attrs = %{provider: "preseem", credentials: %{"api_key" => "test-123"}, enabled: true}
assert {:ok, %Integration{} = integration} = Integrations.create_integration(org.id, attrs)
assert integration.provider == "preseem"
assert integration.credentials == %{"api_key" => "test-123"}
assert integration.enabled == true
assert integration.organization_id == org.id
end
test "rejects duplicate provider per org", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:error, changeset} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert "has already been taken" in errors_on(changeset).provider
end
end
describe "update_integration/2" do
test "updates credentials", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{
provider: "preseem",
credentials: %{"api_key" => "old-key"}
})
assert {:ok, updated} =
Integrations.update_integration(integration, %{credentials: %{"api_key" => "new-key"}})
assert updated.credentials == %{"api_key" => "new-key"}
end
test "updates enabled flag", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{provider: "preseem", enabled: false})
assert {:ok, updated} = Integrations.update_integration(integration, %{enabled: true})
assert updated.enabled == true
end
end
describe "update_sync_status/2" do
test "updates last_synced_at and last_sync_status", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert integration.last_sync_status == "never"
assert integration.last_synced_at == nil
assert {:ok, updated} = Integrations.update_sync_status(integration, "success")
assert updated.last_sync_status == "success"
assert updated.last_synced_at
end
end
describe "delete_integration/1" do
test "deletes the integration", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert {:ok, _} = Integrations.delete_integration(integration)
assert {:error, :not_found} = Integrations.get_integration(org.id, "preseem")
end
end
describe "change_integration/2" do
test "returns a changeset", %{organization: org} do
{:ok, integration} = Integrations.create_integration(org.id, %{provider: "preseem"})
assert %Ecto.Changeset{} = Integrations.change_integration(integration)
end
end
describe "update_billing_totals/3" do
test "persists subscriber_count and total_mrr on integration", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{provider: "gaiia", enabled: true})
assert {:ok, updated} =
Integrations.update_billing_totals(integration, 42, Decimal.new("3500.00"))
assert updated.subscriber_count == 42
assert Decimal.equal?(updated.total_mrr, Decimal.new("3500.00"))
end
test "overwrites previous billing totals", %{organization: org} do
{:ok, integration} =
Integrations.create_integration(org.id, %{provider: "splynx", enabled: true})
{:ok, _} = Integrations.update_billing_totals(integration, 10, Decimal.new("500.00"))
integration = Repo.reload!(integration)
{:ok, updated} = Integrations.update_billing_totals(integration, 20, Decimal.new("1000.00"))
assert updated.subscriber_count == 20
assert Decimal.equal?(updated.total_mrr, Decimal.new("1000.00"))
end
end
describe "get_billing_summary/1" do
test "sums across multiple billing integrations", %{organization: org} do
{:ok, i1} =
Integrations.create_integration(org.id, %{provider: "gaiia", enabled: true})
{:ok, _} = Integrations.update_billing_totals(i1, 100, Decimal.new("5000.00"))
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, i2} =
Integrations.create_integration(org2.id, %{provider: "splynx", enabled: true})
{:ok, _} = Integrations.update_billing_totals(i2, 50, Decimal.new("2500.00"))
# Only org's integrations
summary = Integrations.get_billing_summary(org.id)
assert summary.total_subscribers == 100
assert Decimal.equal?(summary.total_mrr, Decimal.new("5000.00"))
end
test "excludes disabled integrations", %{organization: org} do
{:ok, i1} =
Integrations.create_integration(org.id, %{provider: "gaiia", enabled: true})
{:ok, _} = Integrations.update_billing_totals(i1, 100, Decimal.new("5000.00"))
{:ok, i2} =
Integrations.create_integration(org.id, %{provider: "splynx", enabled: false})
{:ok, _} = Integrations.update_billing_totals(i2, 50, Decimal.new("2500.00"))
summary = Integrations.get_billing_summary(org.id)
assert summary.total_subscribers == 100
assert Decimal.equal?(summary.total_mrr, Decimal.new("5000.00"))
end
test "excludes non-billing integrations", %{organization: org} do
{:ok, i1} =
Integrations.create_integration(org.id, %{provider: "gaiia", enabled: true})
{:ok, _} = Integrations.update_billing_totals(i1, 100, Decimal.new("5000.00"))
{:ok, _} =
Integrations.create_integration(org.id, %{
provider: "preseem",
enabled: true
})
summary = Integrations.get_billing_summary(org.id)
assert summary.total_subscribers == 100
assert Decimal.equal?(summary.total_mrr, Decimal.new("5000.00"))
end
test "returns zeros when no billing integrations", %{organization: org} do
summary = Integrations.get_billing_summary(org.id)
assert summary.total_subscribers == 0
assert Decimal.equal?(summary.total_mrr, Decimal.new(0))
end
end
describe "list_enabled_integrations/1" do
test "returns only enabled integrations for a provider", %{organization: org} do
{:ok, _} = Integrations.create_integration(org.id, %{provider: "preseem", enabled: true})
user2 = user_fixture()
org2 = organization_fixture(user2.id)
{:ok, _} = Integrations.create_integration(org2.id, %{provider: "preseem", enabled: false})
enabled = Integrations.list_enabled_integrations("preseem")
assert length(enabled) == 1
assert hd(enabled).organization_id == org.id
end
end
end