Commit graph

7 commits

Author SHA1 Message Date
fd976b0cd5
fix: resolve 391 Credo issues across codebase
- 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
2026-06-12 13:51:32 -05:00
e879f291f7
chore: update phoenix_live_view to 1.2.0 and fix test failures
Dependency updates:
- phoenix_live_view: ~> 1.2.0 (was ~> 1.1.0)
- Transitive bumps: bandit 1.11.1→1.12.0, credo 1.7.18→1.7.19,
  phoenix 1.8.7→1.8.8, req 0.5.18→0.6.1, swoosh 1.26.0→1.26.1

Fixes:
- core_components.ex: add 'type' to button :global allowlist
  (Phoenix 1.8+ attribute validation)
- status_live_test.exs: invalidate {:all_stats} cache key to
  prevent stale ETS cache from leaking between tests
- path_live_test.exs: accept all valid async states in assertion
  (race between progress frame and compute completion)
- skewt_live_test.exs: accept loading or completed state in assertion

HEEx auto-formatting from mix format (self-closing tag normalization)
2026-06-12 10:42:41 -05:00
2c6458957b
test: SkewtLive HRRR + sounding fixtures bring coverage to 82.7%
Added profile/sounding setup blocks to SkewtLive tests so the
list_valid_times_near + nearest_sounding_to paths run with real
data. SkewtLive: 33% -> 46%.

Total project coverage: 82.7%.
2026-05-08 09:38:41 -05:00
888701e627
test: bump coverage 80% -> 81.3% via more LiveView + module tests
- Pskr.Client: 0% -> 18% (init/standby/handle_info/terminate)
- RoverPlanningLive.PathShow: 0% -> 82% (mount/load_path)
- Rover.CandidateDetail: 0% -> partial (summarize/5 with stations)
- Mix.Tasks.Weather.RebatchAsos: 0% -> covered (wrapper run/1)
- HrrrNativeClient: 57% -> 67% (fetch_native_duct_grid error paths)
- RoverLive: 40% -> 48% (toggle/delete/update_station_grid logged-in)
- PathLive: 62% -> 63% (path_forecast_detail + rover_path_id branches)
- PathCompute: 64% -> 64% (compute/4 full pipeline + resolve_location)
- SkewtLive: 29% -> 31% (search/select_time event no-ops)
- SkewtLocationResolver: 38% -> 60%+ (callsign cache + error paths)

3557 tests passing.
2026-05-08 09:10:34 -05:00
9ef10f5aae
test: expand coverage across rover, pskr, valkey, skewt, scores
- Rover.Compute: road/prominence/clearance/clutter/canopy tests (90%)
- Rover.Prominence: full unit test suite with mock elev_lookup (100%)
- Rover.Elevation: dedup + multi-tile tests (44%)
- Rover.LinkMargin: edge cases, negative scores, all modes (57%)
- Rover.Location: changeset validation + statuses/0 (100%)
- RoverPlanning.Path: changeset validation + statuses/0 (67%)
- Pskr.FeatureBin: changeset validation (100%)
- Pskr.Mqtt: QoS reject, unknown type, varint overflow, ping/disconnect (97%)
- Valkey: not-configured error paths, empty guards (55%)
- ScoresController: bad params, time format, missing band tests (81%)
- SkewtLive: info/no-profiles state, loading states (29%)
2026-05-07 12:51:58 -05:00
3a81d05b4e
perf(skewt): load profile + sounding asynchronously via start_async
URL-parameter loads (`/skewt?q=…`) were blocking the initial mount
on three serial steps: geocoder/grid resolve, ProfilesFile or
HrrrProfileLookup read, and the SkewtParams/SoundingParams/SkewtSvg
derivation. The address path in particular hits an external API,
which made the page take "forever" to load on first paint.

Restructure SkewtLive so the page chrome (header, search bar) renders
synchronously on mount, and the heavy work runs in a single
`start_async(:load_skewt, …)` task that calls SkewtLocationResolver,
walks the on-disk profile store with a DB fallback, and produces the
SVG. A new `loading?` assign drives a small spinner + "Resolving
location and loading HRRR profile…" message that shows while the task
is in flight; previous data stays on screen so navigation between
forecast hours doesn't blank out the diagram.

handle_async/3 covers three landings:

  * `{:ok, {:ok, result}}` — apply assigns from the resolver
  * `{:ok, {:error, reason}}` — surface as the existing error alert
    (geocoder failure, unknown callsign, etc.)
  * `{:exit, reason}` — log a warning and show a generic retry message

start_async/3 cancels any prior task with the same name on each call,
so a rapid-fire URL patch (e.g. clicking through forecast-hour
buttons before the first response returns) never lands stale data.

Tests: extended SkewtLiveTest from 3 to 4 cases — the URL-param
initial render now asserts the chrome + spinner appear pre-async,
and a separate case `render_async/2`s the view to confirm the
location label arrives once the task completes. 4/4 green.
2026-04-25 14:36:30 -05:00
e40ade3b19
feat(skewt): add /skewt LiveView with HRRR-backed Skew-T-Log-P plot
Single search bar accepts an address, Maidenhead grid square, or
callsign; resolves it to lat/lon via Geocoder / Maidenhead /
CallsignLocation (cheapest classification first), snaps to the HRRR
grid via the existing ProfilesFile reader, and renders an SVG
Skew-T-Log-P with isobars, skewed isotherms, dry/moist adiabats, and
mixing-ratio lines. A button row picks between every available
forecast hour (now → f18); default is the most recent analysis.
Right pane lists derived stability and refractivity stats from
SoundingParams.derive (PWAT, BL depth, K-index, lifted index, min
dN/dh, ducting status + per-duct base/top/ΔM).

Renderer is server-side SVG so the page works without JS and serializes
into the LiveView payload as a single tag. Wind barbs are deliberately
omitted — HRRR's persisted profile carries wind only at 10 m AGL.
2026-04-25 12:13:46 -05:00