towerops/test/towerops/uisp/statistics_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

138 lines
4.4 KiB
Elixir

defmodule Towerops.Uisp.StatisticsSyncTest do
use ExUnit.Case, async: true
use ExUnitProperties
alias Towerops.Uisp.StatisticsSync
describe "parse_counter/1" do
test "nil becomes 0" do
assert 0 == StatisticsSync.parse_counter(nil)
end
test "integer passes through" do
assert 42 == StatisticsSync.parse_counter(42)
assert 0 == StatisticsSync.parse_counter(0)
assert -5 == StatisticsSync.parse_counter(-5)
end
test "float rounds to integer" do
assert 3 == StatisticsSync.parse_counter(3.4)
assert 4 == StatisticsSync.parse_counter(3.6)
assert 0 == StatisticsSync.parse_counter(0.0)
end
test "integer-like binary parses" do
assert 1024 == StatisticsSync.parse_counter("1024")
assert 0 == StatisticsSync.parse_counter("0")
assert -7 == StatisticsSync.parse_counter("-7")
end
test "binary with trailing junk still parses leading integer" do
assert 42 == StatisticsSync.parse_counter("42 bytes")
end
test "unparseable binary returns 0" do
assert 0 == StatisticsSync.parse_counter("not a number")
assert 0 == StatisticsSync.parse_counter("")
end
test "unknown types return 0" do
assert 0 == StatisticsSync.parse_counter(:atom)
assert 0 == StatisticsSync.parse_counter([])
assert 0 == StatisticsSync.parse_counter(%{})
end
end
describe "parse_timestamp/1" do
test "nil returns nil" do
assert nil == StatisticsSync.parse_timestamp(nil)
end
test "parses ISO8601 UTC string truncated to seconds" do
assert ~U[2026-03-15 10:30:45Z] == StatisticsSync.parse_timestamp("2026-03-15T10:30:45Z")
end
test "parses ISO8601 with offset and converts to UTC" do
result = StatisticsSync.parse_timestamp("2026-03-15T10:30:45+00:00")
assert %DateTime{} = result
assert result.time_zone == "Etc/UTC"
end
test "parses ISO8601 with sub-second precision but truncates" do
assert ~U[2026-03-15 10:30:45Z] ==
StatisticsSync.parse_timestamp("2026-03-15T10:30:45.123456Z")
end
test "returns nil for invalid string" do
assert nil == StatisticsSync.parse_timestamp("not a date")
assert nil == StatisticsSync.parse_timestamp("")
assert nil == StatisticsSync.parse_timestamp("2026-13-99")
end
test "parses unix timestamp integer" do
# 2026-01-15 10:00:00 UTC = 1_768_514_400
result = StatisticsSync.parse_timestamp(1_768_514_400)
assert %DateTime{} = result
assert DateTime.to_unix(result) == 1_768_514_400
end
test "unknown types return nil" do
assert nil == StatisticsSync.parse_timestamp(:atom)
assert nil == StatisticsSync.parse_timestamp(%{})
assert nil == StatisticsSync.parse_timestamp([1, 2, 3])
assert nil == StatisticsSync.parse_timestamp(3.14)
end
end
describe "property: parse_counter/1" do
property "any integer is returned unchanged" do
check all(n <- integer(-1_000_000..1_000_000)) do
assert StatisticsSync.parse_counter(n) == n
end
end
property "integer-as-string round-trips" do
check all(n <- integer(-1_000_000..1_000_000)) do
assert StatisticsSync.parse_counter(Integer.to_string(n)) == n
end
end
property "float rounds correctly" do
check all(f <- float(min: -1_000_000.0, max: 1_000_000.0)) do
assert StatisticsSync.parse_counter(f) == round(f)
end
end
end
describe "property: parse_timestamp/1" do
property "unix epoch integers round-trip via parse_timestamp" do
check all(ts <- integer(0..4_000_000_000)) do
result = StatisticsSync.parse_timestamp(ts)
assert %DateTime{} = result
assert DateTime.to_unix(result) == ts
end
end
property "ISO8601 UTC strings round-trip" do
check all(
year <- integer(2000..2050),
month <- integer(1..12),
day <- integer(1..28),
hour <- integer(0..23),
minute <- integer(0..59),
second <- integer(0..59)
) do
iso =
"#{year}-#{pad(month)}-#{pad(day)}T#{pad(hour)}:#{pad(minute)}:#{pad(second)}Z"
result = StatisticsSync.parse_timestamp(iso)
assert %DateTime{} = result
assert result.year == year
assert result.month == month
assert result.day == day
end
end
end
defp pad(n), do: n |> Integer.to_string() |> String.pad_leading(2, "0")
end