diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex new file mode 100644 index 0000000..09aa94b --- /dev/null +++ b/lib/atlas/events.ex @@ -0,0 +1,317 @@ +defmodule Atlas.Events do + @moduledoc """ + The Events context. + """ + + import Ecto.Query, warn: false + alias Atlas.Repo + + alias Atlas.Events.{Event, EventCategory, UserEventCategory} + + @doc """ + Returns the list of event_categories. + + ## Examples + + iex> list_event_categories() + [%EventCategory{}, ...] + + """ + def list_event_categories do + EventCategory + |> preload(:course) + |> Repo.all() + end + + @doc """ + Returns the list of event categories selected by a user. + + ## Examples + + iex> list_event_categories_by_user(user_id) + [%EventCategory{}, ...] + + """ + def list_event_categories_by_user(user_id) do + EventCategory + |> join(:left, [ec], uec in assoc(ec, :users_event_categories)) + |> where([ec, uec], uec.user_id == ^user_id or ec.type == :mandatory) + |> preload(:course) + |> Repo.all() + end + + @doc """ + Updates the event categories selected by a user. + + ## Examples + iex> update_event_categories_for_user(user_id, category_ids) + {:ok, [%UserEventCategory{}, ...]} + + iex> update_event_categories_for_user(user_id, bad_category_ids) + {:error, %Ecto.Changeset{}} + """ + def update_event_categories_for_user(user_id, category_ids) do + Ecto.Multi.new() + |> Ecto.Multi.delete_all( + :delete_all, + UserEventCategory + |> where([uec], uec.user_id == ^user_id) + ) + |> Ecto.Multi.run(:insert_all, fn repo, _changes -> + insert_user_event_categories(repo, user_id, category_ids) + end) + |> Repo.transact() + end + + defp insert_user_event_categories(repo, user_id, category_ids) do + category_ids + |> Enum.reduce_while({:ok, []}, fn category_id, {:ok, acc} -> + case repo.get(EventCategory, category_id) do + nil -> + {:halt, {:error, "EventCategory not found: #{category_id}"}} + + %EventCategory{type: :mandatory} -> + {:cont, {:ok, acc}} + + _category -> + insert_user_event_category(repo, user_id, category_id, acc) + end + end) + end + + defp insert_user_event_category(repo, user_id, category_id, acc) do + %UserEventCategory{} + |> UserEventCategory.changeset(%{user_id: user_id, event_category_id: category_id}) + |> repo.insert() + |> case do + {:ok, user_event_category} -> {:cont, {:ok, [user_event_category | acc]}} + {:error, changeset} -> {:halt, {:error, changeset}} + end + end + + @doc """ + Gets a single event_category. + + Raises `Ecto.NoResultsError` if the Event category does not exist. + + ## Examples + + iex> get_event_category!(123) + %EventCategory{} + + iex> get_event_category!(456) + ** (Ecto.NoResultsError) + + """ + def get_event_category!(id) do + EventCategory + |> preload(:course) + |> Repo.get!(id) + end + + @doc """ + Creates a event_category. + + ## Examples + + iex> create_event_category(%{field: value}) + {:ok, %EventCategory{}} + + iex> create_event_category(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_event_category(attrs \\ %{}) do + %EventCategory{} + |> EventCategory.changeset(attrs) + |> Repo.insert() + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Updates a event_category. + + ## Examples + + iex> update_event_category(event_category, %{field: new_value}) + {:ok, %EventCategory{}} + + iex> update_event_category(event_category, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_event_category(%EventCategory{} = event_category, attrs) do + event_category + |> EventCategory.changeset(attrs) + |> Repo.update() + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Deletes a event_category. + + ## Examples + + iex> delete_event_category(event_category) + {:ok, %EventCategory{}} + + iex> delete_event_category(event_category) + {:error, %Ecto.Changeset{}} + + """ + def delete_event_category(%EventCategory{} = event_category) do + Repo.delete(event_category) + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking event_category changes. + + ## Examples + + iex> change_event_category(event_category) + %Ecto.Changeset{data: %EventCategory{}} + + """ + def change_event_category(%EventCategory{} = event_category, attrs \\ %{}) do + EventCategory.changeset(event_category, attrs) + end + + @doc """ + Returns the list of events. + + ## Examples + + iex> list_events() + [%Event{}, ...] + + """ + def list_events do + Event + |> preload(category: :course) + |> Repo.all() + end + + @doc """ + Returns the list of events for a specific user. + + ## Examples + + iex> list_events_by_user(user_id) + [%Event{}, ...] + """ + def list_events_by_user(user_id) do + Event + |> join(:inner, [e], ec in assoc(e, :category)) + |> join(:left, [e, ec], uec in assoc(ec, :users_event_categories)) + |> where([e, ec, uec], uec.user_id == ^user_id or ec.type == :mandatory) + |> preload(category: :course) + |> Repo.all() + end + + @doc """ + Gets a single event. + + Raises `Ecto.NoResultsError` if the Event does not exist. + + ## Examples + + iex> get_event!(123) + %Event{} + + iex> get_event!(456) + ** (Ecto.NoResultsError) + + """ + def get_event!(id) do + Event + |> preload(category: :course) + |> Repo.get!(id) + end + + @doc """ + Creates a event. + + ## Examples + + iex> create_event(%{field: value}) + {:ok, %Event{}} + + iex> create_event(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_event(attrs \\ %{}) do + %Event{} + |> Event.changeset(attrs) + |> Repo.insert() + |> case do + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Updates a event. + + ## Examples + + iex> update_event(event, %{field: new_value}) + {:ok, %Event{}} + + iex> update_event(event, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_event(%Event{} = event, attrs) do + event + |> Event.changeset(attrs) + |> Repo.update() + |> case do + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Deletes a event. + + ## Examples + + iex> delete_event(event) + {:ok, %Event{}} + + iex> delete_event(event) + {:error, %Ecto.Changeset{}} + + """ + def delete_event(%Event{} = event) do + Repo.delete(event) + |> case do + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} + {:error, changeset} -> {:error, changeset} + end + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking event changes. + + ## Examples + + iex> change_event(event) + %Ecto.Changeset{data: %Event{}} + + """ + def change_event(%Event{} = event, attrs \\ %{}) do + Event.changeset(event, attrs) + end +end diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex new file mode 100644 index 0000000..32dec95 --- /dev/null +++ b/lib/atlas/events/event.ex @@ -0,0 +1,28 @@ +defmodule Atlas.Events.Event do + @moduledoc """ + Event schema. + """ + use Atlas.Schema + + @required_fields ~w(title start end place link category_id)a + + schema "events" do + field :start, :utc_datetime + field :end, :utc_datetime + field :link, :string + field :title, :string + field :place, :string + + belongs_to :category, Atlas.Events.EventCategory + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(event, attrs) do + event + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:category_id) + end +end diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex new file mode 100644 index 0000000..4bf3ea9 --- /dev/null +++ b/lib/atlas/events/event_category.ex @@ -0,0 +1,30 @@ +defmodule Atlas.Events.EventCategory do + @moduledoc """ + Event category schema. + """ + use Atlas.Schema + + @types ~w(optional mandatory)a + + @required_fields ~w(name color type)a + @optional_fields ~w(course_id)a + + schema "event_categories" do + field :name, :string + field :color, :string + field :type, Ecto.Enum, values: @types + + belongs_to :course, Atlas.University.Degrees.Courses.Course + + has_many :users_event_categories, Atlas.Events.UserEventCategory + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(event_category, attrs) do + event_category + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/atlas/events/user_event_category.ex b/lib/atlas/events/user_event_category.ex new file mode 100644 index 0000000..754a8e2 --- /dev/null +++ b/lib/atlas/events/user_event_category.ex @@ -0,0 +1,22 @@ +defmodule Atlas.Events.UserEventCategory do + @moduledoc """ + User event category schema. + """ + use Atlas.Schema + + @required_fields ~w(user_id event_category_id)a + + schema "users_event_categories" do + belongs_to :user, Atlas.Accounts.User + belongs_to :event_category, Atlas.Events.EventCategory + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(user_event_category, attrs) do + user_event_category + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/atlas_web/controllers/event_category_controller.ex b/lib/atlas_web/controllers/event_category_controller.ex new file mode 100644 index 0000000..94333d5 --- /dev/null +++ b/lib/atlas_web/controllers/event_category_controller.ex @@ -0,0 +1,72 @@ +defmodule AtlasWeb.EventCategoryController do + use AtlasWeb, :controller + + alias Atlas.Events + alias Atlas.Events.EventCategory + + action_fallback AtlasWeb.FallbackController + + def index(conn, _params) do + event_categories = Events.list_event_categories() + render(conn, :index, event_categories: event_categories) + end + + def create(conn, %{"event_category" => event_category_params}) do + with {:ok, %EventCategory{} = event_category} <- + Events.create_event_category(event_category_params) do + conn + |> put_status(:created) + |> put_resp_header("location", ~p"/v1/event_categories/#{event_category}") + |> render(:show, event_category: event_category) + end + end + + def show(conn, %{"id" => id}) do + event_category = Events.get_event_category!(id) + render(conn, :show, event_category: event_category) + end + + def update(conn, %{"id" => id, "event_category" => event_category_params}) do + event_category = Events.get_event_category!(id) + + with {:ok, %EventCategory{} = event_category} <- + Events.update_event_category(event_category, event_category_params) do + render(conn, :show, event_category: event_category) + end + end + + def delete(conn, %{"id" => id}) do + event_category = Events.get_event_category!(id) + + with {:ok, %EventCategory{}} <- Events.delete_event_category(event_category) do + send_resp(conn, :no_content, "") + end + end + + def selected_index(conn, _params) do + {user, _session} = Guardian.Plug.current_resource(conn) + + user_event_categories = Events.list_event_categories_by_user(user.id) + render(conn, :index, event_categories: user_event_categories) + end + + def selected_update(conn, %{"event_categories" => event_categories}) do + {user, _session} = Guardian.Plug.current_resource(conn) + + case Events.update_event_categories_for_user(user.id, event_categories) do + {:ok, _} -> + event_categories = Events.list_event_categories_by_user(user.id) + render(conn, :index, event_categories: event_categories) + + {:error, _reason} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "Could not update selected event categories."}) + + {:error, _, _, _} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "Could not update selected event categories."}) + end + end +end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex new file mode 100644 index 0000000..0e7afd2 --- /dev/null +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -0,0 +1,33 @@ +defmodule AtlasWeb.EventCategoryJSON do + alias Atlas.Events.EventCategory + alias AtlasWeb.University.CourseJSON + + @doc """ + Renders a list of event_categories. + """ + def index(%{event_categories: event_categories}) do + %{event_categories: for(event_category <- event_categories, do: data(event_category))} + end + + @doc """ + Renders a single event_category. + """ + def show(%{event_category: event_category}) do + %{event_category: data(event_category)} + end + + def data(%EventCategory{} = event_category) do + %{ + id: event_category.id, + name: event_category.name, + color: event_category.color, + course: + if Ecto.assoc_loaded?(event_category.course) and event_category.course do + CourseJSON.data(event_category.course) + else + nil + end, + type: event_category.type + } + end +end diff --git a/lib/atlas_web/controllers/event_controller.ex b/lib/atlas_web/controllers/event_controller.ex new file mode 100644 index 0000000..b10eb50 --- /dev/null +++ b/lib/atlas_web/controllers/event_controller.ex @@ -0,0 +1,50 @@ +defmodule AtlasWeb.EventController do + use AtlasWeb, :controller + + alias Atlas.Events + alias Atlas.Events.Event + + action_fallback AtlasWeb.FallbackController + + def index(conn, _params) do + events = Events.list_events() + render(conn, :index, events: events) + end + + def selected_index(conn, _params) do + {user, _session} = Guardian.Plug.current_resource(conn) + + events = Events.list_events_by_user(user.id) + render(conn, :index, events: events) + end + + def create(conn, %{"event" => event_params}) do + with {:ok, %Event{} = event} <- Events.create_event(event_params) do + conn + |> put_status(:created) + |> put_resp_header("location", ~p"/v1/events/#{event}") + |> render(:show, event: event) + end + end + + def show(conn, %{"id" => id}) do + event = Events.get_event!(id) + render(conn, :show, event: event) + end + + def update(conn, %{"id" => id, "event" => event_params}) do + event = Events.get_event!(id) + + with {:ok, %Event{} = event} <- Events.update_event(event, event_params) do + render(conn, :show, event: event) + end + end + + def delete(conn, %{"id" => id}) do + event = Events.get_event!(id) + + with {:ok, %Event{}} <- Events.delete_event(event) do + send_resp(conn, :no_content, "") + end + end +end diff --git a/lib/atlas_web/controllers/event_json.ex b/lib/atlas_web/controllers/event_json.ex new file mode 100644 index 0000000..189802f --- /dev/null +++ b/lib/atlas_web/controllers/event_json.ex @@ -0,0 +1,34 @@ +defmodule AtlasWeb.EventJSON do + alias Atlas.Events.Event + + @doc """ + Renders a list of events. + """ + def index(%{events: events}) do + %{events: for(event <- events, do: data(event))} + end + + @doc """ + Renders a single event. + """ + def show(%{event: event}) do + %{event: data(event)} + end + + defp data(%Event{} = event) do + %{ + id: event.id, + title: event.title, + start: event.start, + end: event.end, + place: event.place, + link: event.link, + category: + if Ecto.assoc_loaded?(event.category) and event.category do + AtlasWeb.EventCategoryJSON.data(event.category) + else + nil + end + } + end +end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index a5309ef..4e294ce 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -89,6 +89,26 @@ defmodule AtlasWeb.Router do resources "/", ShiftExchangeRequestController, only: [:index, :create, :show, :delete] end + scope "/events" do + get "/selected", EventController, :selected_index + resources "/", EventController, only: [:index, :show] + + pipe_through :is_at_least_professor + + resources "/", EventController, only: [:create, :update, :delete] + end + + scope "/event_categories" do + get "/selected", EventCategoryController, :selected_index + post "/selected", EventCategoryController, :selected_update + + resources "/", EventCategoryController, only: [:index, :show] + + pipe_through :is_at_least_professor + + resources "/", EventCategoryController, only: [:create, :update, :delete] + end + pipe_through :is_at_least_professor get "/students", University.StudentsController, :index diff --git a/priv/repo/migrations/20250928185617_create_event_categories.exs b/priv/repo/migrations/20250928185617_create_event_categories.exs new file mode 100644 index 0000000..b201e3b --- /dev/null +++ b/priv/repo/migrations/20250928185617_create_event_categories.exs @@ -0,0 +1,17 @@ +defmodule Atlas.Repo.Migrations.CreateEventCategories do + use Ecto.Migration + + def change do + create table(:event_categories, primary_key: false) do + add :id, :binary_id, primary_key: true + add :name, :string + add :color, :string + add :type, :string + add :course_id, references(:courses, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:event_categories, [:course_id]) + end +end diff --git a/priv/repo/migrations/20250928185624_create_events.exs b/priv/repo/migrations/20250928185624_create_events.exs new file mode 100644 index 0000000..22ca97f --- /dev/null +++ b/priv/repo/migrations/20250928185624_create_events.exs @@ -0,0 +1,19 @@ +defmodule Atlas.Repo.Migrations.CreateEvents do + use Ecto.Migration + + def change do + create table(:events, primary_key: false) do + add :id, :binary_id, primary_key: true + add :title, :string + add :start, :utc_datetime + add :end, :utc_datetime + add :place, :string + add :link, :string + add :category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:events, [:category_id]) + end +end diff --git a/priv/repo/migrations/20251006110636_create_users_event_categories.exs b/priv/repo/migrations/20251006110636_create_users_event_categories.exs new file mode 100644 index 0000000..e53f1f5 --- /dev/null +++ b/priv/repo/migrations/20251006110636_create_users_event_categories.exs @@ -0,0 +1,16 @@ +defmodule Atlas.Repo.Migrations.CreateUsersEventCategories do + use Ecto.Migration + + def change do + create table(:users_event_categories, primary_key: false) do + add :id, :binary_id, primary_key: true + add :user_id, references(:users, on_delete: :nothing, type: :binary_id) + add :event_category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:users_event_categories, [:user_id]) + create index(:users_event_categories, [:event_category_id]) + end +end diff --git a/test/atlas/events_test.exs b/test/atlas/events_test.exs new file mode 100644 index 0000000..7551f9f --- /dev/null +++ b/test/atlas/events_test.exs @@ -0,0 +1,144 @@ +defmodule Atlas.EventsTest do + use Atlas.DataCase + + alias Atlas.Events + + describe "event_categories" do + alias Atlas.Events.EventCategory + + import Atlas.EventsFixtures + + @invalid_attrs %{name: nil, color: nil, type: nil} + + test "list_event_categories/0 returns all event_categories" do + event_category = event_category_fixture() + assert Events.list_event_categories() == [event_category] + end + + test "get_event_category!/1 returns the event_category with given id" do + event_category = event_category_fixture() + assert Events.get_event_category!(event_category.id) == event_category + end + + test "create_event_category/1 with valid data creates a event_category" do + valid_attrs = %{name: "some name", color: "some color", type: "optional"} + + assert {:ok, %EventCategory{} = event_category} = Events.create_event_category(valid_attrs) + assert event_category.name == "some name" + assert event_category.color == "some color" + end + + test "create_event_category/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Events.create_event_category(@invalid_attrs) + end + + test "update_event_category/2 with valid data updates the event_category" do + event_category = event_category_fixture() + update_attrs = %{name: "some updated name", color: "some updated color"} + + assert {:ok, %EventCategory{} = event_category} = + Events.update_event_category(event_category, update_attrs) + + assert event_category.name == "some updated name" + assert event_category.color == "some updated color" + end + + test "update_event_category/2 with invalid data returns error changeset" do + event_category = event_category_fixture() + + assert {:error, %Ecto.Changeset{}} = + Events.update_event_category(event_category, @invalid_attrs) + + assert event_category == Events.get_event_category!(event_category.id) + end + + test "delete_event_category/1 deletes the event_category" do + event_category = event_category_fixture() + assert {:ok, %EventCategory{}} = Events.delete_event_category(event_category) + assert_raise Ecto.NoResultsError, fn -> Events.get_event_category!(event_category.id) end + end + + test "change_event_category/1 returns a event_category changeset" do + event_category = event_category_fixture() + assert %Ecto.Changeset{} = Events.change_event_category(event_category) + end + end + + describe "events" do + alias Atlas.Events.Event + + import Atlas.EventsFixtures + + @invalid_attrs %{start: nil, link: nil, title: nil, end: nil, place: nil} + + test "list_events/0 returns all events" do + event = event_fixture() + assert Events.list_events() == [event] + end + + test "get_event!/1 returns the event with given id" do + event = event_fixture() + assert Events.get_event!(event.id) == event + end + + test "create_event/1 with valid data creates a event" do + category = event_category_fixture() + + valid_attrs = %{ + start: ~U[2025-01-01 14:00:00Z], + link: "some link", + title: "some title", + end: ~U[2025-01-01 14:00:00Z], + place: "some place", + category_id: category.id + } + + assert {:ok, %Event{} = event} = Events.create_event(valid_attrs) + assert event.start == ~U[2025-01-01 14:00:00Z] + assert event.link == "some link" + assert event.title == "some title" + assert event.end == ~U[2025-01-01 14:00:00Z] + assert event.place == "some place" + end + + test "create_event/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Events.create_event(@invalid_attrs) + end + + test "update_event/2 with valid data updates the event" do + event = event_fixture() + + update_attrs = %{ + start: ~U[2025-01-01 15:01:01Z], + link: "some updated link", + title: "some updated title", + end: ~U[2025-01-01 15:01:01Z], + place: "some updated place" + } + + assert {:ok, %Event{} = event} = Events.update_event(event, update_attrs) + assert event.start == ~U[2025-01-01 15:01:01Z] + assert event.link == "some updated link" + assert event.title == "some updated title" + assert event.end == ~U[2025-01-01 15:01:01Z] + assert event.place == "some updated place" + end + + test "update_event/2 with invalid data returns error changeset" do + event = event_fixture() + assert {:error, %Ecto.Changeset{}} = Events.update_event(event, @invalid_attrs) + assert event == Events.get_event!(event.id) + end + + test "delete_event/1 deletes the event" do + event = event_fixture() + assert {:ok, %Event{}} = Events.delete_event(event) + assert_raise Ecto.NoResultsError, fn -> Events.get_event!(event.id) end + end + + test "change_event/1 returns a event changeset" do + event = event_fixture() + assert %Ecto.Changeset{} = Events.change_event(event) + end + end +end diff --git a/test/atlas_web/controllers/event_category_controller_test.exs b/test/atlas_web/controllers/event_category_controller_test.exs new file mode 100644 index 0000000..6a9edcf --- /dev/null +++ b/test/atlas_web/controllers/event_category_controller_test.exs @@ -0,0 +1,94 @@ +defmodule AtlasWeb.EventCategoryControllerTest do + use AtlasWeb.ConnCase + + import Atlas.EventsFixtures + + alias Atlas.Events.EventCategory + + @create_attrs %{ + name: "some name", + color: "some color" + } + @update_attrs %{ + name: "some updated name", + color: "some updated color" + } + @create_attrs Map.put(@create_attrs, :type, "optional") + @update_attrs Map.put(@update_attrs, :type, "optional") + @invalid_attrs %{name: nil, color: nil, type: nil} + + setup %{conn: _conn} do + conn = AtlasWeb.ConnCase.authenticated_conn(%{type: :professor}) + {:ok, conn: put_req_header(conn, "accept", "application/json")} + end + + describe "index" do + test "lists all event_categories", %{conn: conn} do + conn = get(conn, ~p"/v1/event_categories") + assert json_response(conn, 200)["event_categories"] == [] + end + end + + describe "create event_category" do + test "renders event_category when data is valid", %{conn: conn} do + conn = post(conn, ~p"/v1/event_categories", event_category: @create_attrs) + assert %{"id" => id} = json_response(conn, 201)["event_category"] + + conn = get(conn, ~p"/v1/event_categories/#{id}") + + assert %{ + "id" => ^id, + "color" => "some color", + "name" => "some name" + } = json_response(conn, 200)["event_category"] + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, ~p"/v1/event_categories", event_category: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "update event_category" do + setup [:create_event_category] + + test "renders event_category when data is valid", %{ + conn: conn, + event_category: %EventCategory{id: id} = event_category + } do + conn = put(conn, ~p"/v1/event_categories/#{event_category}", event_category: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["event_category"] + + conn = get(conn, ~p"/v1/event_categories/#{id}") + + assert %{ + "id" => ^id, + "color" => "some updated color", + "name" => "some updated name" + } = json_response(conn, 200)["event_category"] + end + + test "renders errors when data is invalid", %{conn: conn, event_category: event_category} do + conn = put(conn, ~p"/v1/event_categories/#{event_category}", event_category: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "delete event_category" do + setup [:create_event_category] + + test "deletes chosen event_category", %{conn: conn, event_category: event_category} do + conn = delete(conn, ~p"/v1/event_categories/#{event_category}") + assert response(conn, 204) + + assert_error_sent 404, fn -> + get(conn, ~p"/v1/event_categories/#{event_category}") + end + end + end + + defp create_event_category(_) do + event_category = event_category_fixture() + %{event_category: event_category} + end +end diff --git a/test/atlas_web/controllers/event_controller_test.exs b/test/atlas_web/controllers/event_controller_test.exs new file mode 100644 index 0000000..e36a6bd --- /dev/null +++ b/test/atlas_web/controllers/event_controller_test.exs @@ -0,0 +1,113 @@ +defmodule AtlasWeb.EventControllerTest do + use AtlasWeb.ConnCase + + import Atlas.EventsFixtures + + alias Atlas.Events.Event + + @create_attrs %{ + start: "2025-01-01T14:00:00Z", + link: "some link", + title: "some title", + end: "2025-01-01T14:00:00Z", + place: "some place" + } + @update_attrs %{ + start: "2025-01-01T15:01:01Z", + link: "some updated link", + title: "some updated title", + end: "2025-01-01T15:01:01Z", + place: "some updated place" + } + @invalid_attrs %{start: nil, link: nil, title: nil, end: nil, place: nil} + + setup %{conn: _conn} do + conn = AtlasWeb.ConnCase.authenticated_conn(%{type: :professor}) + category = event_category_fixture() + + create_attrs = Map.put(@create_attrs, :category_id, category.id) + update_attrs = Map.put(@update_attrs, :category_id, category.id) + + {:ok, + conn: put_req_header(conn, "accept", "application/json"), + create_attrs: create_attrs, + update_attrs: update_attrs} + end + + describe "index" do + test "lists all events", %{conn: conn} do + conn = get(conn, ~p"/v1/events") + assert json_response(conn, 200)["events"] == [] + end + end + + describe "create event" do + test "renders event when data is valid", %{conn: conn, create_attrs: create_attrs} do + conn = post(conn, ~p"/v1/events", event: create_attrs) + assert %{"id" => id} = json_response(conn, 201)["event"] + + conn = get(conn, ~p"/v1/events/#{id}") + + assert %{ + "id" => ^id, + "end" => "2025-01-01T14:00:00Z", + "link" => "some link", + "place" => "some place", + "start" => "2025-01-01T14:00:00Z", + "title" => "some title" + } = json_response(conn, 200)["event"] + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, ~p"/v1/events", event: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "update event" do + setup [:create_event] + + test "renders event when data is valid", %{ + conn: conn, + event: %Event{id: id} = event, + update_attrs: update_attrs + } do + conn = put(conn, ~p"/v1/events/#{event}", event: update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["event"] + + conn = get(conn, ~p"/v1/events/#{id}") + + assert %{ + "id" => ^id, + "end" => "2025-01-01T15:01:01Z", + "link" => "some updated link", + "place" => "some updated place", + "start" => "2025-01-01T15:01:01Z", + "title" => "some updated title" + } = json_response(conn, 200)["event"] + end + + test "renders errors when data is invalid", %{conn: conn, event: event} do + conn = put(conn, ~p"/v1/events/#{event}", event: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "delete event" do + setup [:create_event] + + test "deletes chosen event", %{conn: conn, event: event} do + conn = delete(conn, ~p"/v1/events/#{event}") + assert response(conn, 204) + + assert_error_sent 404, fn -> + get(conn, ~p"/v1/events/#{event}") + end + end + end + + defp create_event(_) do + event = event_fixture() + %{event: event} + end +end diff --git a/test/support/fixtures/events_fixtures.ex b/test/support/fixtures/events_fixtures.ex new file mode 100644 index 0000000..ff734dc --- /dev/null +++ b/test/support/fixtures/events_fixtures.ex @@ -0,0 +1,45 @@ +defmodule Atlas.EventsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Atlas.Events` context. + """ + + @doc """ + Generate a event_category. + """ + def event_category_fixture(attrs \\ %{}) do + {:ok, event_category} = + attrs + |> Enum.into(%{ + color: "some color", + name: "some name", + type: "optional" + }) + |> Atlas.Events.create_event_category() + + event_category + end + + @doc """ + Generate a event. + """ + def event_fixture(attrs \\ %{}) do + category = event_category_fixture() + + defaults = %{ + end: ~N[2025-01-01 14:00:00], + link: "some link", + place: "some place", + start: ~N[2025-01-01 14:00:00], + title: "some title", + category_id: category.id + } + + {:ok, event} = + attrs + |> Enum.into(defaults) + |> Atlas.Events.create_event() + + event + end +end