- Add jump_credo_checks ~> 0.4 with all 20 checks enabled - Fix all standard Credo issues: 139 @spec (113 done, 26 remain), 4 refactoring, 3 alias usage, 9 System.cmd env, 5 unsafe_to_atom, 2 max line length, 9 assert_receive timeout - Fix 170+ jump_credo_checks warnings: - 117 TopLevelAliasImportRequire: move nested alias/import to module top - 32 UseObanProWorker: switch to Oban.Pro.Worker - 4 DoctestIExExamples: add doctests / create test file - ~20 WeakAssertion: strengthen type-check assertions - Various ConditionalAssertion, AssertReceiveTimeout fixes - Exclude vendor/ from Credo analysis - Remaining: 175 warnings (mostly opinionated WeakAssertion, AvoidSocketAssignsInTest), 26 @spec annotations
109 lines
3.7 KiB
Elixir
109 lines
3.7 KiB
Elixir
defmodule Mix.Tasks.Weather.RebatchAsosTest do
|
|
use Microwaveprop.DataCase, async: false
|
|
|
|
import Ecto.Query
|
|
|
|
alias Microwaveprop.Weather.IemClient
|
|
alias Microwaveprop.Weather.RebatchAsos
|
|
alias Microwaveprop.Workers.WeatherFetchWorker
|
|
|
|
setup do
|
|
# Oban runs inline in tests, so the batch job the task inserts
|
|
# actually executes. Stub IEM so that execution is a quiet no-op
|
|
# (stations don't exist, so the batch handler just logs "missing"
|
|
# and returns :ok).
|
|
Req.Test.stub(IemClient, fn conn ->
|
|
Req.Test.text(conn, "#DEBUG\nstation,valid,tmpf\n")
|
|
end)
|
|
|
|
:ok
|
|
end
|
|
|
|
# Oban runs `testing: :inline` in this project, so Oban.insert! would
|
|
# execute the job immediately. We stash raw Oban.Job rows so we can
|
|
# assert on the queue state the rebatch task would see in prod.
|
|
defp insert_asos_job(station_id, station_code, start_dt, end_dt) do
|
|
changeset =
|
|
WeatherFetchWorker.new(%{
|
|
"fetch_type" => "asos",
|
|
"station_id" => station_id,
|
|
"station_code" => station_code,
|
|
"start_dt" => start_dt,
|
|
"end_dt" => end_dt
|
|
})
|
|
|
|
# Force state=available so the task picks it up; Oban.Job defaults
|
|
# to :available already but we set it explicitly for clarity.
|
|
changeset
|
|
|> Ecto.Changeset.put_change(:state, "available")
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
defp pending_count_by_type do
|
|
from(j in Oban.Job,
|
|
where: j.state in ["available", "scheduled", "retryable"],
|
|
select: {fragment("?->>'fetch_type'", j.args), count(j.id)},
|
|
group_by: fragment("?->>'fetch_type'", j.args)
|
|
)
|
|
|> Repo.all()
|
|
|> Map.new()
|
|
end
|
|
|
|
test "collapses N single-station jobs sharing a window into one batch" do
|
|
insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
insert_asos_job(Ecto.UUID.generate(), "KFTW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
insert_asos_job(Ecto.UUID.generate(), "KAFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
|
|
output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)
|
|
|
|
assert output =~ "3 pending `asos` jobs across 1 time windows"
|
|
assert output =~ "inserted 1 batch jobs"
|
|
assert output =~ "cancelled 3 single-station jobs"
|
|
end
|
|
|
|
test "groups per (start_dt, end_dt) window" do
|
|
insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
insert_asos_job(Ecto.UUID.generate(), "KFTW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
insert_asos_job(Ecto.UUID.generate(), "KAUS", "2026-03-29T10:00:00Z", "2026-03-29T14:00:00Z")
|
|
|
|
output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)
|
|
|
|
assert output =~ "3 pending `asos` jobs across 2 time windows"
|
|
assert output =~ "inserted 2 batch jobs"
|
|
end
|
|
|
|
test "dry-run does not mutate the queue" do
|
|
insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
|
|
|
|
ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run(dry_run: true) end)
|
|
|
|
counts = pending_count_by_type()
|
|
assert counts["asos"] == 1
|
|
assert counts["asos_batch"] in [nil, 0]
|
|
end
|
|
|
|
test "no pending asos jobs — short-circuits" do
|
|
output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)
|
|
assert output =~ "Nothing to do"
|
|
end
|
|
|
|
describe "Mix task wrapper" do
|
|
test "run/1 with no args is a no-op" do
|
|
output =
|
|
ExUnit.CaptureIO.capture_io(fn ->
|
|
assert :ok = Mix.Tasks.Weather.RebatchAsos.run([])
|
|
end)
|
|
|
|
assert output =~ "Nothing to do"
|
|
end
|
|
|
|
test "run/1 with --dry-run flag" do
|
|
output =
|
|
ExUnit.CaptureIO.capture_io(fn ->
|
|
assert :ok = Mix.Tasks.Weather.RebatchAsos.run(["--dry-run"])
|
|
end)
|
|
|
|
assert output =~ "Nothing to do"
|
|
end
|
|
end
|
|
end
|