defmodule ToweropsWeb.Api.V1.IntegrationsControllerTest do use ToweropsWeb.ConnCase, async: true import Towerops.AccountsFixtures import Towerops.IntegrationsFixtures import Towerops.OrganizationsFixtures alias Towerops.ApiTokens setup do user = user_fixture() organization = organization_fixture(user.id) {:ok, {_token, raw_token}} = ApiTokens.create_api_token(%{ organization_id: organization.id, user_id: user.id, name: "Test Token" }) conn = build_conn() |> put_req_header("authorization", "Bearer #{raw_token}") |> put_req_header("accept", "application/json") %{ conn: conn, user: user, organization: organization } end describe "index/2" do test "lists all integrations for authenticated organization", %{ conn: conn, organization: organization } do integration1 = integration_fixture(organization.id, %{provider: "preseem"}) integration2 = integration_fixture(organization.id, %{provider: "gaiia"}) conn = get(conn, ~p"/api/v1/integrations") assert %{"data" => integrations} = json_response(conn, 200) assert length(integrations) == 2 ids = Enum.map(integrations, & &1["id"]) assert integration1.id in ids assert integration2.id in ids integration_json = List.first(integrations) assert Map.has_key?(integration_json, "id") assert Map.has_key?(integration_json, "provider") assert Map.has_key?(integration_json, "enabled") assert Map.has_key?(integration_json, "sync_interval_minutes") assert Map.has_key?(integration_json, "last_synced_at") assert Map.has_key?(integration_json, "last_sync_status") assert Map.has_key?(integration_json, "last_sync_message") assert Map.has_key?(integration_json, "inserted_at") assert Map.has_key?(integration_json, "updated_at") end test "returns empty list when organization has no integrations", %{conn: conn} do conn = get(conn, ~p"/api/v1/integrations") assert %{"data" => []} = json_response(conn, 200) end test "does not return integrations from other organizations", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) _other_integration = integration_fixture(other_org.id) conn = get(conn, ~p"/api/v1/integrations") assert %{"data" => integrations} = json_response(conn, 200) assert Enum.empty?(integrations) end end describe "create/2" do test "creates integration with valid params", %{conn: conn} do params = %{ "integration" => %{ "provider" => "preseem", "enabled" => true, "credentials" => %{"api_key" => "test-key-123"} } } conn = post(conn, ~p"/api/v1/integrations", params) assert %{"data" => integration} = json_response(conn, 201) assert integration["provider"] == "preseem" assert integration["enabled"] == true assert is_binary(integration["id"]) end test "returns 400 when integration parameter is missing", %{conn: conn} do conn = post(conn, ~p"/api/v1/integrations", %{}) assert %{"error" => "Missing 'integration' parameter"} = json_response(conn, 400) end end describe "show/2" do test "returns integration when it belongs to organization", %{ conn: conn, organization: organization } do integration = integration_fixture(organization.id, %{provider: "preseem"}) conn = get(conn, ~p"/api/v1/integrations/#{integration.id}") assert %{"data" => data} = json_response(conn, 200) assert data["id"] == integration.id assert data["provider"] == "preseem" assert data["enabled"] == true end test "returns 404 when integration belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_integration = integration_fixture(other_org.id) conn = get(conn, ~p"/api/v1/integrations/#{other_integration.id}") assert %{"error" => "Integration not found"} = json_response(conn, 404) end test "returns 404 when integration does not exist", %{conn: conn} do conn = get(conn, ~p"/api/v1/integrations/#{Ecto.UUID.generate()}") assert %{"error" => "Integration not found"} = json_response(conn, 404) end end describe "update/2" do test "updates integration with valid attributes", %{ conn: conn, organization: organization } do integration = integration_fixture(organization.id, %{provider: "preseem", enabled: true}) update_params = %{ "integration" => %{ "enabled" => false } } conn = patch(conn, ~p"/api/v1/integrations/#{integration.id}", update_params) assert %{"data" => data} = json_response(conn, 200) assert data["id"] == integration.id assert data["enabled"] == false end test "returns 400 when integration parameter is missing", %{ conn: conn, organization: organization } do integration = integration_fixture(organization.id) conn = patch(conn, ~p"/api/v1/integrations/#{integration.id}", %{}) assert %{"error" => "Missing 'integration' parameter"} = json_response(conn, 400) end test "returns 404 when integration belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_integration = integration_fixture(other_org.id) update_params = %{ "integration" => %{ "enabled" => false } } conn = patch(conn, ~p"/api/v1/integrations/#{other_integration.id}", update_params) assert %{"error" => "Integration not found"} = json_response(conn, 404) end end describe "delete/2" do test "deletes integration successfully", %{conn: conn, organization: organization} do integration = integration_fixture(organization.id) conn = delete(conn, ~p"/api/v1/integrations/#{integration.id}") assert response(conn, 204) end test "returns 404 when integration belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_integration = integration_fixture(other_org.id) conn = delete(conn, ~p"/api/v1/integrations/#{other_integration.id}") assert %{"error" => "Integration not found"} = json_response(conn, 404) end test "returns 404 when integration does not exist", %{conn: conn} do conn = delete(conn, ~p"/api/v1/integrations/#{Ecto.UUID.generate()}") assert %{"error" => "Integration not found"} = json_response(conn, 404) end end describe "test_connection/2" do test "returns ok status for valid integration", %{ conn: conn, organization: organization } do integration = integration_fixture(organization.id, %{provider: "preseem"}) conn = post(conn, ~p"/api/v1/integrations/#{integration.id}/test") assert %{"data" => %{"status" => "ok", "provider" => "preseem"}} = json_response(conn, 200) end test "returns 404 when integration belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_integration = integration_fixture(other_org.id) conn = post(conn, ~p"/api/v1/integrations/#{other_integration.id}/test") assert %{"error" => "Integration not found"} = json_response(conn, 404) end test "returns 404 when integration does not exist", %{conn: conn} do conn = post(conn, ~p"/api/v1/integrations/#{Ecto.UUID.generate()}/test") assert %{"error" => "Integration not found"} = json_response(conn, 404) end end describe "authentication" do test "returns 401 without authorization header" do conn = build_conn() |> put_req_header("accept", "application/json") |> get(~p"/api/v1/integrations") assert %{"error" => _message} = json_response(conn, 401) end test "returns 401 with invalid token" do conn = build_conn() |> put_req_header("authorization", "Bearer towerops_invalid_token") |> put_req_header("accept", "application/json") |> get(~p"/api/v1/integrations") assert %{"error" => _message} = json_response(conn, 401) end end end