defmodule Microwaveprop.RoverLocationsTest do use Microwaveprop.DataCase, async: true alias Microwaveprop.Rover alias Microwaveprop.Rover.Location defp user_fixture do Microwaveprop.AccountsFixtures.user_fixture() end describe "list_locations/0" do test "returns all locations across all users, newest first" do a = user_fixture() b = user_fixture() {:ok, _l1} = Rover.create_location(a, %{lat: 32.5, lon: -97.5, status: :good}) {:ok, _l2} = Rover.create_location(b, %{lat: 33.0, lon: -96.5, status: :bad}) result = Rover.list_locations() assert length(result) == 2 assert Enum.all?(result, &match?(%Location{}, &1)) end end describe "create_location/2" do test "stores the creator's user_id" do user = user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) assert loc.user_id == user.id end test "supports notes and the bad status" do user = user_fixture() {:ok, loc} = Rover.create_location(user, %{ lat: 32.0, lon: -97.0, status: :bad, notes: "private property" }) assert loc.status == :bad assert loc.notes == "private property" end test "rejects out-of-range coordinates" do user = user_fixture() assert {:error, %Ecto.Changeset{}} = Rover.create_location(user, %{lat: 100.0, lon: 0.0, status: :good}) end test "requires lat and lon" do user = user_fixture() assert {:error, cs} = Rover.create_location(user, %{status: :good}) assert "can't be blank" in errors_on(cs).lat end end describe "Location.changeset/2" do test "rejects lon out of range" do changeset = Location.changeset(%Location{}, %{lat: 0.0, lon: 200.0, status: :good}) refute changeset.valid? assert "must be less than or equal to 180.0" in errors_on(changeset).lon end test "rejects lat out of range" do changeset = Location.changeset(%Location{}, %{lat: -91.0, lon: 0.0, status: :good}) refute changeset.valid? assert "must be greater than or equal to -90.0" in errors_on(changeset).lat end test "rejects invalid status" do changeset = Location.changeset(%Location{}, %{lat: 0.0, lon: 0.0, status: :unknown}) refute changeset.valid? assert "is invalid" in errors_on(changeset).status end test "accepts valid lat/lon at the extreme boundaries" do changeset = Location.changeset(%Location{}, %{lat: 90.0, lon: 180.0, status: :good}) assert changeset.valid? end test "notes exceeding max length are rejected" do changeset = Location.changeset(%Location{}, %{lat: 0.0, lon: 0.0, status: :good, notes: String.duplicate("x", 4001)}) refute changeset.valid? assert "should be at most 4000 character(s)" in errors_on(changeset).notes end test "statuses/0 returns available status atoms" do assert Location.statuses() == [:good, :bad] end end describe "update_location/3 and delete_location/2" do test "creator can update their location" do user = user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.0, lon: -97.0, status: :good}) assert {:ok, updated} = Rover.update_location(user, loc.id, %{notes: "updated"}) assert updated.notes == "updated" end test "non-creator cannot update or delete" do user = user_fixture() other = user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.0, lon: -97.0, status: :good}) assert {:error, :not_found} = Rover.update_location(other, loc.id, %{notes: "x"}) assert {:error, :not_found} = Rover.delete_location(other, loc.id) end test "creator can delete their location" do user = user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.0, lon: -97.0, status: :good}) assert {:ok, _} = Rover.delete_location(user, loc.id) refute Enum.any?(Rover.list_locations(), &(&1.id == loc.id)) end end end