Renames the rover-location status enum and every label that referenced it. Existing rows are migrated in place. Also touches the consumers: - Rover.Location enum + default - RoverLocationsLive index, status filter, form, show, badges - Rover.Compute and rover_live (only_ideal_locations → only_good_locations) - RoverPlanning context candidate filter (status == :good) - RoverPlanning.Show / Form copy - All rover-related tests
144 lines
5.1 KiB
Elixir
144 lines
5.1 KiB
Elixir
defmodule Microwaveprop.RoverPlanningTest do
|
||
use Microwaveprop.DataCase, async: true
|
||
|
||
alias Microwaveprop.AccountsFixtures
|
||
alias Microwaveprop.Repo
|
||
alias Microwaveprop.Rover
|
||
alias Microwaveprop.RoverPlanning
|
||
alias Microwaveprop.RoverPlanning.Mission
|
||
alias Microwaveprop.Terrain.ElevationClient
|
||
|
||
setup do
|
||
# Oban runs inline in test, so RoverPathProfileWorker fires immediately
|
||
# when the context enqueues jobs. Stub elevation lookups so the worker
|
||
# can complete without hitting the real API.
|
||
Req.Test.stub(ElevationClient, fn conn ->
|
||
params = Plug.Conn.fetch_query_params(conn).query_params
|
||
lat_count = params["latitude"] |> String.split(",") |> length()
|
||
Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)})
|
||
end)
|
||
|
||
:ok
|
||
end
|
||
|
||
defp create_rover_location(attrs) do
|
||
user = AccountsFixtures.user_fixture()
|
||
|
||
{:ok, loc} =
|
||
Rover.create_location(
|
||
user,
|
||
Map.merge(%{lat: 32.5, lon: -97.5, status: :good}, attrs)
|
||
)
|
||
|
||
loc
|
||
end
|
||
|
||
defp valid_attrs(extra \\ %{}) do
|
||
Map.merge(
|
||
%{
|
||
"name" => "Test mission",
|
||
"band_mhz" => 10_000,
|
||
"only_known_good" => true,
|
||
"rover_height_ft" => 8.0,
|
||
"station_height_ft" => 30.0,
|
||
"stations" => %{
|
||
"0" => %{"input" => "EM12kp", "position" => 0}
|
||
}
|
||
},
|
||
extra
|
||
)
|
||
end
|
||
|
||
describe "create_mission/2" do
|
||
test "inserts a mission with stations and enqueues a path per (rover × station) pair" do
|
||
user = AccountsFixtures.user_fixture()
|
||
_ideal_loc = create_rover_location(%{lat: 32.0, lon: -97.0, status: :good})
|
||
_off_limits = create_rover_location(%{lat: 33.0, lon: -98.0, status: :bad})
|
||
|
||
assert {:ok, mission} = RoverPlanning.create_mission(user, valid_attrs())
|
||
assert mission.user_id == user.id
|
||
assert [station] = mission.stations
|
||
assert station.grid == "EM12KP"
|
||
assert is_float(station.lat)
|
||
assert is_float(station.lon)
|
||
|
||
paths = RoverPlanning.list_paths(mission)
|
||
# only_known_good=true → only the :good location pairs with the station.
|
||
# Oban runs inline in test so the worker has already completed.
|
||
assert length(paths) == 1
|
||
assert hd(paths).status in [:complete, :failed]
|
||
end
|
||
|
||
test "with only_known_good=false, all rover-locations get a path entry" do
|
||
user = AccountsFixtures.user_fixture()
|
||
_ideal = create_rover_location(%{lat: 32.0, lon: -97.0, status: :good})
|
||
_off = create_rover_location(%{lat: 33.0, lon: -98.0, status: :bad})
|
||
|
||
attrs = valid_attrs(%{"only_known_good" => false})
|
||
assert {:ok, mission} = RoverPlanning.create_mission(user, attrs)
|
||
assert length(RoverPlanning.list_paths(mission)) == 2
|
||
end
|
||
|
||
test "missing stations returns a changeset error" do
|
||
user = AccountsFixtures.user_fixture()
|
||
attrs = Map.delete(valid_attrs(), "stations")
|
||
|
||
assert {:error, %Ecto.Changeset{} = cs} = RoverPlanning.create_mission(user, attrs)
|
||
refute cs.valid?
|
||
assert {_, _} = cs.errors[:stations] || hd(cs.errors)
|
||
end
|
||
end
|
||
|
||
describe "update_mission/3" do
|
||
test "owner can update a mission and paths get rebuilt" do
|
||
user = AccountsFixtures.user_fixture()
|
||
_ = create_rover_location(%{lat: 32.0, lon: -97.0, status: :good})
|
||
{:ok, mission} = RoverPlanning.create_mission(user, valid_attrs())
|
||
|
||
[original] = RoverPlanning.list_paths(mission)
|
||
|
||
attrs = valid_attrs(%{"name" => "Updated"})
|
||
assert {:ok, updated} = RoverPlanning.update_mission(user, mission.id, attrs)
|
||
assert updated.name == "Updated"
|
||
|
||
# Old path was deleted by replace, fresh row inserted with new id.
|
||
[fresh] = RoverPlanning.list_paths(updated)
|
||
refute fresh.id == original.id
|
||
end
|
||
|
||
test "non-owner update is denied" do
|
||
owner = AccountsFixtures.user_fixture()
|
||
other = AccountsFixtures.user_fixture()
|
||
_ = create_rover_location(%{lat: 32.0, lon: -97.0, status: :good})
|
||
{:ok, mission} = RoverPlanning.create_mission(owner, valid_attrs())
|
||
|
||
assert {:error, :not_found} =
|
||
RoverPlanning.update_mission(other, mission.id, valid_attrs())
|
||
end
|
||
|
||
test "admin can update any mission" do
|
||
owner = AccountsFixtures.user_fixture()
|
||
admin = AccountsFixtures.user_fixture()
|
||
{:ok, admin} = Microwaveprop.Accounts.admin_update_user(admin, %{is_admin: true})
|
||
_ = create_rover_location(%{lat: 32.0, lon: -97.0, status: :good})
|
||
{:ok, mission} = RoverPlanning.create_mission(owner, valid_attrs())
|
||
|
||
assert {:ok, updated} =
|
||
RoverPlanning.update_mission(admin, mission.id, valid_attrs(%{"name" => "By admin"}))
|
||
|
||
assert updated.name == "By admin"
|
||
end
|
||
end
|
||
|
||
describe "delete_mission/2" do
|
||
test "owner can delete; non-owner cannot" do
|
||
owner = AccountsFixtures.user_fixture()
|
||
other = AccountsFixtures.user_fixture()
|
||
{:ok, mission} = RoverPlanning.create_mission(owner, valid_attrs())
|
||
|
||
assert {:error, :not_found} = RoverPlanning.delete_mission(other, mission.id)
|
||
assert {:ok, _} = RoverPlanning.delete_mission(owner, mission.id)
|
||
refute Repo.get(Mission, mission.id)
|
||
end
|
||
end
|
||
end
|