towerops/test/towerops/sonar/sync_test.exs
Graham McIntire 3ca0834ef0 tests: raise coverage to 70% via helper promotion + new unit/property tests
Promoted pure presentation and utility helpers from `defp` to `def @doc false`
across ~20 LiveViews, Oban workers, and sync modules so they're reachable from
unit tests. Refactored several `cond` blocks into idiomatic function heads with
guards. Added ~250 new test cases in new files under test/towerops and
test/towerops_web, including DB-backed tests for CnMaestro.Sync and
AlertNotificationWorker, and removed dead LiveView tab components and
CapacityLive (no callers anywhere in lib/test).

Configured mix.exs test_coverage.ignore_modules to exclude vendored third-party
code (SnmpKit, protobuf-generated Towerops.Agent.*, Absinthe GraphQL types,
Phoenix HTML modules, Inspect protocol impls) from coverage calculations —
these are not our project code.

Coverage: 66.93% → 70.09%. Full suite: 10,127 tests, 0 failures.
2026-04-24 09:49:06 -05:00

131 lines
4 KiB
Elixir

defmodule Towerops.Sonar.SyncTest do
use ExUnit.Case, async: true
alias Towerops.Sonar.Sync
describe "sum_service_prices/1" do
test "sums all numeric price_override values" do
services = [
%{"price_override" => 10.0},
%{"price_override" => 25.5},
%{"price_override" => 100}
]
assert 135.5 == Sync.sum_service_prices(services)
end
test "ignores nil price_override" do
services = [%{"price_override" => 10.0}, %{"price_override" => nil}]
assert 10.0 == Sync.sum_service_prices(services)
end
test "ignores non-numeric price_override" do
services = [
%{"price_override" => "not a number"},
%{"price_override" => 5.0},
%{"other_field" => "ignored"}
]
assert 5.0 == Sync.sum_service_prices(services)
end
test "returns 0.0 for empty list" do
assert 0.0 == Sync.sum_service_prices([])
end
test "handles integer and float mixed" do
services = [%{"price_override" => 10}, %{"price_override" => 2.5}]
assert 12.5 == Sync.sum_service_prices(services)
end
end
describe "humanize_sync_error/1" do
test "unauthorized" do
assert Sync.humanize_sync_error(:unauthorized) =~ "Authentication failed"
end
test "forbidden" do
assert Sync.humanize_sync_error(:forbidden) =~ "Access denied"
end
test "rate_limited includes retry seconds" do
assert Sync.humanize_sync_error({:rate_limited, 120}) =~ "retry after 120s"
end
test "unexpected_status includes status code" do
assert Sync.humanize_sync_error({:unexpected_status, 502}) =~ "HTTP 502"
end
test "graphql_errors joins messages" do
errors = [%{"message" => "bad field"}, %{"message" => "missing arg"}]
msg = Sync.humanize_sync_error({:graphql_errors, errors})
assert msg =~ "GraphQL errors"
assert msg =~ "bad field"
assert msg =~ "missing arg"
assert msg =~ ";"
end
test "graphql_errors without message uses inspect" do
errors = [%{"foo" => "bar"}]
msg = Sync.humanize_sync_error({:graphql_errors, errors})
assert msg =~ "GraphQL errors"
assert msg =~ "%{"
end
test "SSL-related string errors return SSL message" do
for snippet <- ["CA trust store", "cacert", "certificate"] do
msg = Sync.humanize_sync_error("failure: #{snippet} something")
assert msg =~ "SSL certificate verification failed"
end
end
test "other string errors return generic message" do
assert Sync.humanize_sync_error("random broken thing") ==
"An unexpected error occurred during sync"
end
test "non-binary unknown errors return generic message" do
assert Sync.humanize_sync_error(:something_weird) ==
"An unexpected error occurred during sync"
assert Sync.humanize_sync_error({:unknown_tuple, 42}) ==
"An unexpected error occurred during sync"
end
end
describe "add_price/2" do
test "adds a number to the accumulator" do
assert 15.5 == Sync.add_price(10, 5.5)
assert 20 == Sync.add_price(5, 15)
end
test "nil price leaves accumulator unchanged" do
assert 10 == Sync.add_price(10, nil)
end
test "non-numeric price leaves accumulator unchanged" do
assert 10 == Sync.add_price(10, "$5")
assert 10.0 == Sync.add_price(10.0, :invalid)
end
end
describe "calculate_total_mrr/1" do
test "sums services across all accounts" do
accounts = [
%{"account_services" => [%{"price_override" => 10.0}, %{"price_override" => 5.0}]},
%{"account_services" => [%{"price_override" => 20}]}
]
assert 35.0 == Sync.calculate_total_mrr(accounts)
end
test "handles missing account_services key" do
accounts = [%{"name" => "no services"}, %{"account_services" => [%{"price_override" => 10.0}]}]
assert 10.0 == Sync.calculate_total_mrr(accounts)
end
test "handles empty list" do
assert 0.0 == Sync.calculate_total_mrr([])
end
end
end