defmodule ToweropsWeb.Api.V1.CoveragesControllerTest do use ToweropsWeb.ConnCase, async: false import Towerops.AccountsFixtures import Towerops.CoveragesFixtures import Towerops.OrganizationsFixtures alias Towerops.ApiTokens setup %{conn: conn} do user = user_fixture() org = organization_fixture(user.id) site = site_fixture(org.id, %{latitude: 30.27, longitude: -97.74}) {:ok, {_token, raw}} = ApiTokens.create_api_token(%{ organization_id: org.id, user_id: user.id, name: "test" }) conn = put_req_header(conn, "authorization", "Bearer #{raw}") %{conn: conn, org: org, user: user, site: site} end describe "GET /api/v1/coverages" do test "returns coverages for the authenticated organization", %{conn: conn, org: org, site: site} do cov = coverage_fixture(org.id, site.id, %{name: "Sector A"}) resp = conn |> get(~p"/api/v1/coverages") |> json_response(200) assert [%{"id" => id, "name" => "Sector A"}] = resp["coverages"] assert id == cov.id end test "returns empty list when org has no coverages", %{conn: conn} do assert %{"coverages" => []} = conn |> get(~p"/api/v1/coverages") |> json_response(200) end end describe "POST /api/v1/coverages" do test "creates and queues a coverage", %{conn: conn, site: site} do attrs = %{ "name" => "API-created", "site_id" => site.id, "antenna_slug" => "simulate-isotropic-omni-0", "frequency_ghz" => 5.8, "tx_power_dbm" => 20.0, "height_agl_ft" => 80, "azimuth_deg" => 0, "downtilt_deg" => 0, "radius_mi" => 3 } resp = conn |> post(~p"/api/v1/coverages", coverage: attrs) |> json_response(201) assert resp["name"] == "API-created" assert resp["status"] == "queued" assert resp["site_id"] == site.id assert is_binary(resp["id"]) end test "returns 422 with field errors on bad input", %{conn: conn} do resp = conn |> post(~p"/api/v1/coverages", coverage: %{"name" => ""}) |> json_response(422) assert resp["errors"] end test "returns 400 when 'coverage' wrapper is missing", %{conn: conn} do resp = conn |> post(~p"/api/v1/coverages", %{}) |> json_response(400) assert resp["error"] =~ "Missing" end end describe "GET /api/v1/coverages/:id" do test "returns a single coverage", %{conn: conn, org: org, site: site} do cov = coverage_fixture(org.id, site.id) resp = conn |> get(~p"/api/v1/coverages/#{cov.id}") |> json_response(200) assert resp["id"] == cov.id end test "returns 404 for cross-org coverage", %{conn: conn} do assert conn |> get(~p"/api/v1/coverages/#{Ecto.UUID.generate()}") |> json_response(404) end end describe "PATCH /api/v1/coverages/:id" do test "updates editable fields", %{conn: conn, org: org, site: site} do cov = coverage_fixture(org.id, site.id, %{name: "before"}) resp = conn |> patch(~p"/api/v1/coverages/#{cov.id}", coverage: %{"name" => "after"}) |> json_response(200) assert resp["name"] == "after" end end describe "DELETE /api/v1/coverages/:id" do test "deletes the coverage", %{conn: conn, org: org, site: site} do cov = coverage_fixture(org.id, site.id) conn = delete(conn, ~p"/api/v1/coverages/#{cov.id}") assert response(conn, 204) assert nil == Towerops.Coverages.get_coverage(org.id, cov.id) end end describe "POST /api/v1/coverages/:id/recompute" do test "queues a recompute and returns 202", %{conn: conn, org: org, site: site} do cov = coverage_fixture(org.id, site.id, %{status: "ready"}) resp = conn |> post(~p"/api/v1/coverages/#{cov.id}/recompute") |> json_response(202) assert resp["status"] == "queued" end end describe "auth" do test "rejects requests without a Bearer token" do conn = get(build_conn(), ~p"/api/v1/coverages") assert response(conn, 401) end end end