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.
138 lines
4.4 KiB
Elixir
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
|