diff --git a/config/config.exs b/config/config.exs index f7e944e0..0dab81e4 100644 --- a/config/config.exs +++ b/config/config.exs @@ -18,7 +18,7 @@ config :safira, roulette_tokens_min: String.to_integer(System.get_env("ROULETTE_TOKENS_MIN") || "5"), roulette_tokens_max: String.to_integer(System.get_env("ROULETTE_TOKENS_MAX") || "20"), token_bonus: String.to_integer(System.get_env("TOKEN_BONUS") || "10"), - spotlight_duration: String.to_integer(System.get_env("SPOTLIGHT_DURATION") || "30"), + spotlight_duration: String.to_integer(System.get_env("SPOTLIGHT_DURATION") || "20"), discord_bot_url: System.get_env("DISCORD_BOT_URL"), discord_bot_api_key: System.get_env("DISCORD_BOT_API_KEY"), discord_invite_url: System.get_env("DISCORD_INVITE_URL"), diff --git a/lib/safira/accounts/accounts.ex b/lib/safira/accounts/accounts.ex index b37ec5f6..c062e945 100644 --- a/lib/safira/accounts/accounts.ex +++ b/lib/safira/accounts/accounts.ex @@ -221,6 +221,8 @@ defmodule Safira.Accounts do def get_company!(id), do: Repo.get!(Company, id) + def get_company_by_badge!(badge_id), do: Repo.get_by(Company, badge_id: badge_id) + def create_company(attrs \\ %{}) do %Company{} |> Company.changeset(attrs) diff --git a/lib/safira/accounts/company.ex b/lib/safira/accounts/company.ex index 123a6589..c170f31e 100644 --- a/lib/safira/accounts/company.ex +++ b/lib/safira/accounts/company.ex @@ -3,10 +3,10 @@ defmodule Safira.Accounts.Company do A company participating in SEI """ use Ecto.Schema + import Ecto.Changeset alias Safira.Accounts.User - alias Safira.Contest.Badge schema "companies" do @@ -15,6 +15,7 @@ defmodule Safira.Accounts.Company do field :channel_id, :string field :remaining_spotlights, :integer field :has_cv_access, :boolean + belongs_to :user, User belongs_to :badge, Badge diff --git a/lib/safira/contest/contest.ex b/lib/safira/contest/contest.ex index 717a4893..9a5dd3fd 100644 --- a/lib/safira/contest/contest.ex +++ b/lib/safira/contest/contest.ex @@ -8,13 +8,12 @@ defmodule Safira.Contest do alias Ecto.Multi + alias Safira.Accounts alias Safira.Accounts.Attendee alias Safira.Contest.Badge alias Safira.Contest.DailyToken alias Safira.Contest.Redeem - alias Safira.Interaction - alias Safira.Repo def list_badges do @@ -278,9 +277,20 @@ defmodule Safira.Contest do defp calculate_badge_tokens(badge) do if Interaction.is_badge_spotlighted(badge.id) do - badge.tokens * 2 + ceil(badge.tokens * get_multiplier(badge)) else badge.tokens end end + + defp get_multiplier(badge) do + company = Accounts.get_company_by_badge!(badge.id) + + case company.sponsorship do + "Exclusive" -> 2 + "Gold" -> 1.8 + "Silver" -> 1.5 + "Bronze" -> 1 + end + end end diff --git a/lib/safira/interaction/interaction.ex b/lib/safira/interaction/interaction.ex index 40a9f113..c0215b60 100644 --- a/lib/safira/interaction/interaction.ex +++ b/lib/safira/interaction/interaction.ex @@ -159,16 +159,18 @@ defmodule Safira.Interaction do end @doc """ - Returns the only existing Spotlight + Returns the only existing spotlight """ def get_spotlight do Repo.all(Spotlight) |> List.first() end + @spotlight_duration Application.compile_env!(:safira, :spotlight_duration) + @doc """ Starts the spotlight """ - def start_spotlight(company) do + def start_spotlight(company, duration \\ nil) do Multi.new() # get or build a spotlight |> Multi.run(:get_spotlight, fn _repo, _changes -> @@ -178,7 +180,10 @@ defmodule Safira.Interaction do end) # update the spotlight |> Multi.insert_or_update(:upsert_spotlight, fn %{get_spotlight: spotlight} -> - Spotlight.changeset(spotlight, %{badge_id: company.badge_id, active: true}) + Spotlight.changeset(spotlight, %{ + badge_id: company.badge_id, + end: DateTime.utc_now() |> DateTime.add(duration || @spotlight_duration, :minute) + }) end) # update company's remaining_spotlights |> Multi.update( @@ -191,19 +196,11 @@ defmodule Safira.Interaction do |> apply_transaction() end - @doc """ - Signals spotlight as inactive - """ - def finish_spotlight do - get_spotlight() - |> Spotlight.finish_changeset(%{active: false}) - |> Repo.update() - end - def is_badge_spotlighted(badge_id) do spotlight = get_spotlight() - !is_nil(spotlight) and spotlight.active and spotlight.badge_id == badge_id + !is_nil(spotlight) and DateTime.compare(spotlight.end, DateTime.utc_now()) == :gt and + spotlight.badge_id == badge_id end defp apply_transaction(multi) do diff --git a/lib/safira/interaction/spotlight.ex b/lib/safira/interaction/spotlight.ex index b8fdc9d3..2f1e233f 100644 --- a/lib/safira/interaction/spotlight.ex +++ b/lib/safira/interaction/spotlight.ex @@ -1,40 +1,40 @@ defmodule Safira.Interaction.Spotlight do @moduledoc """ - Spotlight was a feature that would highlight a company - for a given amount of time in discord - (Deprecated, used for online SEI) + Spotlight allows companies to highlight a badge for a period of time, + giving more tokens to attendees who redeem it. """ use Ecto.Schema + import Ecto.Changeset + alias Safira.Interaction + schema "spotlights" do - field :active, :boolean, default: false + field :end, :utc_datetime field :badge_id, :id + field :lock_version, :integer, default: 1 timestamps() end - @doc false def changeset(spotlight, attrs) do spotlight - |> cast(attrs, [:active, :badge_id]) - |> validate_required([:active, :badge_id]) + |> cast(attrs, [:end, :badge_id]) + |> validate_required([:end, :badge_id]) |> validate_not_already_active() |> Ecto.Changeset.optimistic_lock(:lock_version) end - def finish_changeset(spotlight, attrs) do - spotlight - |> cast(attrs, [:active]) - |> validate_required([:active]) - end - defp validate_not_already_active(changeset) do - if changeset.data && changeset.data.active do - add_error(changeset, :active, "Another spotlight is still active") - else - changeset + if changeset.data do + spotlight = Interaction.get_spotlight() + + if spotlight && DateTime.compare(spotlight.end, DateTime.utc_now()) == :gt do + add_error(changeset, :end, "Another spotlight is still active") + else + changeset + end end end end diff --git a/lib/safira_web/controllers/spotlight/spotlight_controller.ex b/lib/safira_web/controllers/spotlight/spotlight_controller.ex new file mode 100644 index 00000000..4995e46f --- /dev/null +++ b/lib/safira_web/controllers/spotlight/spotlight_controller.ex @@ -0,0 +1,70 @@ +defmodule SafiraWeb.SpotlightController do + use SafiraWeb, :controller + + alias Safira.Accounts + alias Safira.Interaction + + action_fallback SafiraWeb.FallbackController + + def index(conn, _params) do + is_admin = Accounts.is_admin(conn) + + if is_admin do + companies = Accounts.list_companies() + spotlight = Interaction.get_spotlight() + + conn + |> put_status(:ok) + |> render(:index, companies: companies, spotlight: spotlight) + else + conn + |> put_status(:unauthorized) + |> json(%{error: "Cannot access resource"}) + |> halt() + end + end + + def current(conn, _params) do + is_company = Accounts.is_company(conn) + spotlight = Interaction.get_spotlight() + + if is_company do + conn + |> put_status(:unauthorized) + |> json(%{error: "Cannot access resource"}) + |> halt() + else + if !spotlight || DateTime.compare(spotlight.end, DateTime.utc_now()) == :lt do + conn + |> put_status(:not_found) + |> json(%{error: "Spotlight not found"}) + |> halt() + else + company = Accounts.get_company_by_badge!(spotlight.badge_id) + + conn + |> put_status(:ok) + |> render(:current, company: company, spotlight: spotlight) + end + end + end + + def create(conn, %{"company_id" => company_id} = params) do + is_admin = Accounts.is_admin(conn) + + if is_admin do + company = Accounts.get_company!(company_id) + + with {:ok, _struct} <- Interaction.start_spotlight(company, params["duration"]) do + conn + |> put_status(:created) + |> json(%{spotlight: "Spotlight created successfully"}) + end + else + conn + |> put_status(:unauthorized) + |> json(%{error: "Cannot access resource"}) + |> halt() + end + end +end diff --git a/lib/safira_web/controllers/spotlight/spotlight_json.ex b/lib/safira_web/controllers/spotlight/spotlight_json.ex new file mode 100644 index 00000000..c6a3cc5c --- /dev/null +++ b/lib/safira_web/controllers/spotlight/spotlight_json.ex @@ -0,0 +1,38 @@ +defmodule SafiraWeb.SpotlightJSON do + @moduledoc false + + def index(%{companies: companies, spotlight: spotlight}) do + %{ + data: for(company <- companies, do: data(company, spotlight)) + } + end + + def current(%{company: company, spotlight: spotlight}) do + data = + data(company, spotlight) + |> Map.drop([:remaining]) + |> Map.put(:badge_id, company.badge_id) + + %{ + data: data + } + end + + defp data(company, spotlight) do + default = default(company) + + if spotlight && spotlight.badge_id == company.badge_id do + Map.put(default, :end, spotlight.end) + else + default + end + end + + defp default(company) do + %{ + id: company.id, + name: company.name, + remaining: company.remaining_spotlights + } + end +end diff --git a/lib/safira_web/controllers/spotlight_controller.ex b/lib/safira_web/controllers/spotlight_controller.ex deleted file mode 100644 index 61449d53..00000000 --- a/lib/safira_web/controllers/spotlight_controller.ex +++ /dev/null @@ -1,57 +0,0 @@ -defmodule SafiraWeb.SpotlightController do - use SafiraWeb, controller: "1.6" - - alias Safira.Accounts - alias Safira.Interaction - - action_fallback SafiraWeb.FallbackController - - def create(conn, _params) do - user = Accounts.get_user(conn) - - if Accounts.is_company(conn) do - with {:ok, _struct} <- Interaction.start_spotlight(user.company) do - # to signal discord to start the spotlight - spotlight_discord_request(user.company.name, :post) - - schedule_spotlight_finish(user.company.name) - - conn - |> put_status(:created) - |> json(%{spotlight: "Spotlight requested succesfully"}) - end - else - conn - |> put_status(:unauthorized) - |> json(%{error: "Cannot access resource"}) - |> halt() - end - end - - defp spotlight_discord_request(company_name, request_type) do - headers = %{ - "Content-Type" => "application/json", - "Authorization" => Application.fetch_env!(:safira, :discord_bot_api_key) - } - - url = "#{Application.fetch_env!(:safira, :discord_bot_url)}/spotlight" - - case request_type do - :post -> - body = Poison.encode!(%{"company" => company_name}) - HTTPoison.post(url, body, headers, []) - - :delete -> - HTTPoison.delete(url, headers) - end - end - - defp schedule_spotlight_finish(company_name) do - Task.async(fn -> - :timer.sleep(Application.fetch_env!(:safira, :spotlight_duration) * 60 * 1000) - Interaction.finish_spotlight() - # To signal discord to end the spotlight - spotlight_discord_request(company_name, :delete) - end) - end -end diff --git a/lib/safira_web/router.ex b/lib/safira_web/router.ex index b9d016f3..8f56e459 100644 --- a/lib/safira_web/router.ex +++ b/lib/safira_web/router.ex @@ -60,7 +60,11 @@ defmodule SafiraWeb.Router do post "/roulette", RouletteController, :spin post "/give_bonus/:id", BonusController, :give_bonus - post "/spotlight", SpotlightController, :create + + get "/spotlights", SpotlightController, :index + get "/spotlight", SpotlightController, :current + post "/spotlight/:company_id", SpotlightController, :create + post "/store/redeem", DeliverRedeemableController, :create post "/roulette/redeem", DeliverPrizeController, :create diff --git a/priv/repo/migrations/20210218152530_create_spotlights.exs b/priv/repo/migrations/20210218152530_create_spotlights.exs index c32743d4..4d7c6140 100644 --- a/priv/repo/migrations/20210218152530_create_spotlights.exs +++ b/priv/repo/migrations/20210218152530_create_spotlights.exs @@ -3,8 +3,9 @@ defmodule Safira.Repo.Migrations.CreateSpotlights do def change do create table(:spotlights) do - add :active, :boolean, default: false, null: false + add :end, :utc_datetime add :badge_id, references(:badges, on_delete: :nothing) + add :lock_version, :integer, default: 1 timestamps() diff --git a/test/factories/accounts_factory.ex b/test/factories/accounts_factory.ex index a6bf1a21..44be5ee7 100644 --- a/test/factories/accounts_factory.ex +++ b/test/factories/accounts_factory.ex @@ -23,8 +23,9 @@ defmodule Safira.AccountsFactory do %Safira.Accounts.Company{ name: name, - sponsorship: Enum.random(["exclusive", "gold", "silver", "bronze"]), + sponsorship: Enum.random(["Exclusive", "Gold", "Silver", "Bronze"]), channel_id: String.downcase(name), + remaining_spotlights: Enum.random(1..3), user: build(:user), badge: build(:badge), has_cv_access: Enum.random([true, false]) diff --git a/test/safira_web/controllers/spotlight_controller_test.exs b/test/safira_web/controllers/spotlight_controller_test.exs new file mode 100644 index 00000000..0eb123cd --- /dev/null +++ b/test/safira_web/controllers/spotlight_controller_test.exs @@ -0,0 +1,541 @@ +defmodule SafiraWeb.SpotlightControllerTest do + @moduledoc """ + Tests for the SpotlightController. + """ + use SafiraWeb.ConnCase + + alias Safira.Accounts.{Attendee, Company} + alias Safira.Contest + alias Safira.Interaction + alias Safira.Interaction.Spotlight + alias Safira.Repo + + setup %{conn: conn} do + user = create_user_strategy(:user) + company = insert(:company) + + {:ok, conn: put_req_header(conn, "accept", "application/json"), user: user, company: company} + end + + describe "index" do + test "when user is an admin", %{user: user, company: company} do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + assert json_response(conn, 200) == %{ + "data" => [ + %{ + "id" => company.id, + "name" => company.name, + "remaining" => company.remaining_spotlights + } + ] + } + end + + test "when a spotlight for the given company is currently running", %{ + user: user, + company: company + } do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + # Start a spotlight for the company + Interaction.start_spotlight(company) + spotlight = Interaction.get_spotlight() + assert spotlight != nil + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + # Get company again, since it was only in-memory + company = Repo.get_by!(Company, id: company.id) + + assert json_response(conn, 200) == %{ + "data" => [ + %{ + "id" => company.id, + "name" => company.name, + "remaining" => company.remaining_spotlights, + "end" => DateTime.to_iso8601(spotlight.end) + } + ] + } + end + + test "when there are two companies, but only one has a spotlight", %{ + user: user, + company: company1 + } do + _staff = insert(:staff, user: user, is_admin: true) + company2 = insert(:company, remaining_spotlights: 1) + + %{conn: conn, user: _user} = api_authenticate(user) + + # Start a spotlight for the first company + Interaction.start_spotlight(company1) + spotlight = Interaction.get_spotlight() + assert spotlight != nil + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + # Get company again, since it was only in-memory + company1 = Repo.get_by!(Company, id: company1.id) + company2 = Repo.get_by!(Company, id: company2.id) + + assert json_response(conn, 200) == %{ + "data" => [ + %{ + "id" => company2.id, + "name" => company2.name, + "remaining" => company2.remaining_spotlights + }, + %{ + "id" => company1.id, + "name" => company1.name, + "remaining" => company1.remaining_spotlights, + "end" => DateTime.to_iso8601(spotlight.end) + } + ] + } + end + + test "when there are two companies, but none of them have a spotlight", %{ + user: user, + company: company1 + } do + _staff = insert(:staff, user: user, is_admin: true) + company2 = insert(:company, remaining_spotlights: 1) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + # Get company again, since it was only in-memory + company1 = Repo.get_by!(Company, id: company1.id) + company2 = Repo.get_by!(Company, id: company2.id) + + assert json_response(conn, 200) == %{ + "data" => [ + %{ + "id" => company1.id, + "name" => company1.name, + "remaining" => company1.remaining_spotlights + }, + %{ + "id" => company2.id, + "name" => company2.name, + "remaining" => company2.remaining_spotlights + } + ] + } + end + + test "when user is a staff", %{user: user} do + _staff = insert(:staff, user: user, is_admin: false) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when user is an attendee", %{user: user} do + _attendee = insert(:attendee, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when user is a company", %{user: user} do + _company = insert(:company, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :index)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + end + + describe "current" do + test "when user is an admin", %{user: user, company: company} do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + # Start a spotlight for the company + Interaction.start_spotlight(company) + spotlight = Interaction.get_spotlight() + assert spotlight != nil + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + # Get company again, since it was only in-memory + company = Repo.get_by!(Company, id: company.id) + + assert json_response(conn, 200) == %{ + "data" => %{ + "id" => company.id, + "name" => company.name, + "badge_id" => company.badge_id, + "end" => DateTime.to_iso8601(spotlight.end) + } + } + end + + test "when there's no spotlight running", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 404) == %{"error" => "Spotlight not found"} + end + + test "when a spotlight exists, but is not running", %{user: user, company: company} do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + # Start a spotlight for the company + Interaction.start_spotlight(company, -1) + spotlight = Interaction.get_spotlight() + assert spotlight != nil + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 404) == %{"error" => "Spotlight not found"} + end + + test "when user is a staff", %{user: user} do + _staff = insert(:staff, user: user, is_admin: false) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 404) == %{"error" => "Spotlight not found"} + end + + test "when user is an attendee", %{user: user} do + _attendee = insert(:attendee, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 404) == %{"error" => "Spotlight not found"} + end + + test "when user is an attendee and a spotlight is running", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 1) + + %{conn: conn, user: _user} = api_authenticate(user) + + # Start a spotlight for the company + Interaction.start_spotlight(company) + spotlight = Interaction.get_spotlight() + assert spotlight != nil + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 200) == %{ + "data" => %{ + "id" => company.id, + "name" => company.name, + "badge_id" => company.badge_id, + "end" => DateTime.to_iso8601(spotlight.end) + } + } + end + + test "when user is a company", %{user: user} do + _company = insert(:company, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> get(Routes.spotlight_path(conn, :current)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + end + + describe "create" do + test "when user is an admin", %{user: user, company: company} do + _staff = insert(:staff, user: user, is_admin: true) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + end + + test "when user is a staff", %{user: user, company: company} do + _staff = insert(:staff, user: user, is_admin: false) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when user is an attendee", %{user: user, company: company} do + _attendee = insert(:attendee, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when user is a company", %{user: user, company: company} do + _company = insert(:company, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when user is a company creating a spotlight for itself", %{user: user} do + company = insert(:company, user: user) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 401) == %{"error" => "Cannot access resource"} + end + + test "when another spotlight is already running", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 2) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 422) == %{ + "errors" => %{"end" => ["Another spotlight is still active"]} + } + + # Ensure the current spotlight was not deleted by this creation try + spotlights = Repo.all(Spotlight) + assert length(spotlights) == 1 + end + + test "when there's a spotlight, but has already ended", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 2) + + %{conn: conn, user: _user} = api_authenticate(user) + + attrs = %{"duration" => -1} + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company), attrs) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + # Ensure there's always only one spotlight (one line at the table) + spotlights = Repo.all(Spotlight) + assert length(spotlights) == 1 + end + + test "when the company does not have more remaining spotlights", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 0) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 422) == %{ + "errors" => %{"remaining_spotlights" => ["must be greater than or equal to 0"]} + } + end + + test "when the company trying to create already has a spotlight", %{user: user} do + _staff = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 2) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 422) == %{ + "errors" => %{"end" => ["Another spotlight is still active"]} + } + + # Ensure the current spotlight was not deleted by this creation try + spotlights = Repo.all(Spotlight) + assert length(spotlights) == 1 + end + + test "when a badge is given within a spotlight by an admin", %{user: user} do + admin = insert(:staff, user: user, is_admin: true) + company = insert(:company, remaining_spotlights: 1) + attendee = insert(:attendee) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + # Give badge to attendee within spotlight + Contest.create_redeem( + %{ + attendee_id: attendee.id, + staff_id: admin.id, + badge_id: company.badge_id + }, + :admin + ) + + # Get attendee again, since it was only in-memory + attendee = Repo.get_by!(Attendee, id: attendee.id) + + assert attendee.token_balance == ceil(company.badge.tokens * get_multiplier(company)) + end + + test "when a badge is give within a spotlight, but by a staff", %{user: user} do + _admin = insert(:staff, user: user, is_admin: true) + staff = insert(:staff, is_admin: false) + company = insert(:company, remaining_spotlights: 1) + attendee = insert(:attendee) + + %{conn: conn, user: _user} = api_authenticate(user) + + conn = + conn + |> post(Routes.spotlight_path(conn, :create, company)) + + assert json_response(conn, 201) == %{"spotlight" => "Spotlight created successfully"} + + # Give badge to attendee within spotlight + Contest.create_redeem( + %{ + attendee_id: attendee.id, + staff_id: staff.id, + badge_id: company.badge_id + }, + :staff + ) + + # Get attendee again, since it was only in-memory + attendee = Repo.get_by!(Attendee, id: attendee.id) + + assert attendee.token_balance == ceil(company.badge.tokens * get_multiplier(company)) + end + + test "when a badge is given while there are no spotlights", %{user: user} do + staff = insert(:staff, user: user, is_admin: false) + company = insert(:company) + attendee = insert(:attendee) + + # Give badge to attendee within spotlight + Contest.create_redeem( + %{ + attendee_id: attendee.id, + staff_id: staff.id, + badge_id: company.badge_id + }, + :staff + ) + + # Get attendee again, since it was only in-memory + attendee = Repo.get_by!(Attendee, id: attendee.id) + + assert attendee.token_balance == company.badge.tokens + end + + defp get_multiplier(company) do + case company.sponsorship do + "Exclusive" -> 2 + "Gold" -> 1.8 + "Silver" -> 1.5 + "Bronze" -> 1 + end + end + end +end