diff --git a/lib/cadet/courses/courses.ex b/lib/cadet/courses/courses.ex index 1a5f236dd..3ac51544c 100644 --- a/lib/cadet/courses/courses.ex +++ b/lib/cadet/courses/courses.ex @@ -308,88 +308,68 @@ defmodule Cadet.Courses do end end - @upload_file_roles ~w(admin staff)a - @doc """ Upload a sourcecast file. - Note that there are no checks for whether the user belongs to the course, as this has been checked - inside a plug in the router. + Note that there are no checks for whether the user belongs to the course, + as this has been checked inside a plug in the router. """ def upload_sourcecast_file( - _inserter = %CourseRegistration{user_id: user_id, course_id: course_id, role: role}, + _inserter = %CourseRegistration{user_id: user_id, course_id: course_id}, attrs = %{} ) do - if role in @upload_file_roles do - course_reg = - CourseRegistration - |> where(user_id: ^user_id) - |> where(course_id: ^course_id) - |> preload(:course) - |> preload(:user) - |> Repo.one() - - changeset = - %Sourcecast{} - |> Sourcecast.changeset(attrs) - |> put_assoc(:uploader, course_reg.user) - |> put_assoc(:course, course_reg.course) - - case Repo.insert(changeset) do - {:ok, sourcecast} -> - {:ok, sourcecast} - - {:error, changeset} -> - {:error, {:bad_request, full_error_messages(changeset)}} - end - else - {:error, {:forbidden, "User is not permitted to upload"}} - end - end + changeset = + Sourcecast.changeset(%Sourcecast{uploader_id: user_id, course_id: course_id}, attrs) - @doc """ - Upload a public sourcecast file. + case Repo.insert(changeset) do + {:ok, sourcecast} -> + {:ok, sourcecast} - Note that there are no checks for whether the user belongs to the course, as this has been checked - inside a plug in the router. - """ - def upload_sourcecast_file_public( - inserter, - _inserter_course_reg = %CourseRegistration{role: role}, - attrs = %{} - ) do - if role in @upload_file_roles do - changeset = - %Sourcecast{} - |> Sourcecast.changeset(attrs) - |> put_assoc(:uploader, inserter) - - case Repo.insert(changeset) do - {:ok, sourcecast} -> - {:ok, sourcecast} - - {:error, changeset} -> - {:error, {:bad_request, full_error_messages(changeset)}} - end - else - {:error, {:forbidden, "User is not permitted to upload"}} + {:error, changeset} -> + {:error, {:bad_request, full_error_messages(changeset)}} end end + # @doc """ + # Upload a public sourcecast file. + + # Note that there are no checks for whether the user belongs to the course, + # as this has been checked inside a plug in the router. + # unused in the current version + # """ + # def upload_sourcecast_file_public( + # inserter, + # _inserter_course_reg = %CourseRegistration{role: role}, + # attrs = %{} + # ) do + # if role in @upload_file_roles do + # changeset = + # %Sourcecast{} + # |> Sourcecast.changeset(attrs) + # |> put_assoc(:uploader, inserter) + + # case Repo.insert(changeset) do + # {:ok, sourcecast} -> + # {:ok, sourcecast} + + # {:error, changeset} -> + # {:error, {:bad_request, full_error_messages(changeset)}} + # end + # else + # {:error, {:forbidden, "User is not permitted to upload"}} + # end + # end + @doc """ Delete a sourcecast file Note that there are no checks for whether the user belongs to the course, as this has been checked inside a plug in the router. """ - def delete_sourcecast_file(_deleter = %CourseRegistration{role: role}, sourcecast_id) do - if role in @upload_file_roles do - sourcecast = Repo.get(Sourcecast, sourcecast_id) - SourcecastUpload.delete({sourcecast.audio, sourcecast}) - Repo.delete(sourcecast) - else - {:error, {:forbidden, "User is not permitted to delete"}} - end + def delete_sourcecast_file(sourcecast_id) do + sourcecast = Repo.get(Sourcecast, sourcecast_id) + SourcecastUpload.delete({sourcecast.audio, sourcecast}) + Repo.delete(sourcecast) end @doc """ @@ -402,13 +382,14 @@ defmodule Cadet.Courses do |> Repo.preload(:uploader) end - def get_sourcecast_files do - Sourcecast - # Public sourcecasts are those without course_id - |> where([s], is_nil(s.course_id)) - |> Repo.all() - |> Repo.preload(:uploader) - end + # unused in the current version + # def get_sourcecast_files do + # Sourcecast + # # Public sourcecasts are those without course_id + # |> where([s], is_nil(s.course_id)) + # |> Repo.all() + # |> Repo.preload(:uploader) + # end @spec assets_prefix(%Course{}) :: binary() def assets_prefix(course) do diff --git a/lib/cadet/stories/stories.ex b/lib/cadet/stories/stories.ex index d996c5961..dab56a33a 100644 --- a/lib/cadet/stories/stories.ex +++ b/lib/cadet/stories/stories.ex @@ -7,16 +7,10 @@ defmodule Cadet.Stories.Stories do import Ecto.Query alias Cadet.Repo - alias Cadet.Accounts.CourseRegistration alias Cadet.Stories.Story - alias Cadet.Courses.Course - @manage_stories_role ~w(staff admin)a - - def list_stories( - _user_course_registration = %CourseRegistration{course_id: course_id, role: role} - ) do - if role in @manage_stories_role do + def list_stories(course_id, list_all) do + if list_all do Story |> where(course_id: ^course_id) |> Repo.all() @@ -29,66 +23,39 @@ defmodule Cadet.Stories.Stories do end end - def create_story( - attrs = %{}, - _user_course_registration = %CourseRegistration{course_id: course_id, role: role} - ) do - if role in @manage_stories_role do - course = - Course - |> where(id: ^course_id) - |> Repo.one() - - %Story{} - |> Story.changeset(Map.put(attrs, :course_id, course.id)) - |> Repo.insert() - else - {:error, {:forbidden, "User not allowed to manage stories"}} - end + def create_story(attrs = %{}, course_id) do + %Story{} + |> Story.changeset(Map.put(attrs, :course_id, course_id)) + |> Repo.insert() end - def update_story( - attrs = %{}, - id, - _user_course_registration = %CourseRegistration{course_id: course_id, role: role} - ) do - if role in @manage_stories_role do - case Repo.get(Story, id) do - nil -> - {:error, {:not_found, "Story not found"}} - - story -> - if story.course_id == course_id do - story - |> Story.changeset(attrs) - |> Repo.update() - else - {:error, {:forbidden, "User not allowed to manage stories from another course"}} - end - end - else - {:error, {:forbidden, "User not allowed to manage stories"}} + def update_story(attrs = %{}, id, course_id) do + case Repo.get(Story, id) do + nil -> + {:error, {:not_found, "Story not found"}} + + story -> + if story.course_id == course_id do + story + |> Story.changeset(attrs) + |> Repo.update() + else + {:error, {:forbidden, "User not allowed to manage stories from another course"}} + end end end - def delete_story( - id, - _user_course_registration = %CourseRegistration{course_id: course_id, role: role} - ) do - if role in @manage_stories_role do - case Repo.get(Story, id) do - nil -> - {:error, {:not_found, "Story not found"}} - - story -> - if story.course_id == course_id do - Repo.delete(story) - else - {:error, {:forbidden, "User not allowed to manage stories from another course"}} - end - end - else - {:error, {:forbidden, "User not allowed to manage stories"}} + def delete_story(id, course_id) do + case Repo.get(Story, id) do + nil -> + {:error, {:not_found, "Story not found"}} + + story -> + if story.course_id == course_id do + Repo.delete(story) + else + {:error, {:forbidden, "User not allowed to manage stories from another course"}} + end end end end diff --git a/lib/cadet_web/admin_controllers/admin_sourcecast_controller.ex b/lib/cadet_web/admin_controllers/admin_sourcecast_controller.ex new file mode 100644 index 000000000..ea595bc6f --- /dev/null +++ b/lib/cadet_web/admin_controllers/admin_sourcecast_controller.ex @@ -0,0 +1,93 @@ +defmodule CadetWeb.AdminSourcecastController do + use CadetWeb, :controller + use PhoenixSwagger + + alias Cadet.Courses + + def create(conn, %{"sourcecast" => sourcecast}) do + result = Courses.upload_sourcecast_file(conn.assigns.course_reg, sourcecast) + + case result do + {:ok, _nil} -> + send_resp(conn, 200, "OK") + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + + def create(conn, _params) do + send_resp(conn, :bad_request, "Missing or invalid parameter(s)") + end + + def delete(conn, %{"id" => id}) do + result = Courses.delete_sourcecast_file(id) + + case result do + {:ok, _nil} -> + send_resp(conn, 200, "OK") + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + + swagger_path :create do + post("/sourcecast") + description("Uploads sourcecast") + summary("Upload sourcecast") + consumes("multipart/form-data") + security([%{JWT: []}]) + + parameters do + public( + :body, + :boolean, + "Uploads as public sourcecast when 'public' is specified regardless of truthy or falsy" + ) + + sourcecast(:body, Schema.ref(:Sourcecast), "sourcecast object", required: true) + end + + response(200, "Success") + response(400, "Invalid or missing parameter(s)") + response(401, "Unauthorised") + end + + swagger_path :delete do + PhoenixSwagger.Path.delete("/sourcecast/{id}") + description("Deletes sourcecast by id") + summary("Delete sourcecast") + security([%{JWT: []}]) + + parameters do + id(:path, :integer, "sourcecast id", required: true) + end + + response(200, "Success") + response(400, "Invalid or missing parameter(s)") + response(401, "Unauthorised") + end + + def swagger_definitions do + %{ + Sourcecast: + swagger_schema do + properties do + title(:string, "title", required: true) + playbackData(:string, "playback data", required: true) + description(:string, "description", required: false) + uid(:string, "uid", required: false) + + # Note: this is technically an invalid type in Swagger/OpenAPI 2.0, + # but represents that a string or integer could be returned. + audio(:file, "audio file", required: true) + end + end + } + end +end diff --git a/lib/cadet_web/admin_controllers/admin_stories_controller.ex b/lib/cadet_web/admin_controllers/admin_stories_controller.ex new file mode 100644 index 000000000..03455a8e4 --- /dev/null +++ b/lib/cadet_web/admin_controllers/admin_stories_controller.ex @@ -0,0 +1,128 @@ +defmodule CadetWeb.AdminStoriesController do + use CadetWeb, :controller + use PhoenixSwagger + + alias Cadet.Stories.Stories + + def create(conn, %{"course_id" => course_id, "story" => story}) do + result = + story + |> to_snake_case_atom_keys() + |> Stories.create_story(course_id |> String.to_integer()) + + case result do + {:ok, _story} -> + conn |> put_status(200) |> text('') + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + + def update(conn, _params = %{"course_id" => course_id, "storyid" => id, "story" => story}) do + result = + story + |> to_snake_case_atom_keys() + |> Stories.update_story(id, course_id |> String.to_integer()) + + case result do + {:ok, _story} -> + conn |> put_status(200) |> text('') + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + + def delete(conn, _params = %{"course_id" => course_id, "storyid" => id}) do + result = Stories.delete_story(id, course_id |> String.to_integer()) + + case result do + {:ok, _nil} -> + conn |> put_status(204) |> text('') + + {:error, {status, message}} -> + conn + |> put_status(status) + |> text(message) + end + end + + swagger_path :index do + get("/v2/courses/{course_id}/stories") + + summary("Get a list of all stories") + + security([%{JWT: []}]) + + response(200, "OK", Schema.array(:Story)) + end + + swagger_path :create do + post("/v2{course_id}/stories") + + summary("Creates a new story") + + security([%{JWT: []}]) + + response(200, "OK", :Story) + response(400, "Bad request") + response(403, "User not allowed to manage stories") + end + + swagger_path :delete do + PhoenixSwagger.Path.delete("/v2/courses/{course_id}/stories/{storyId}") + + summary("Delete a story from database by id") + + parameters do + storyId(:path, :integer, "Story Id", required: true) + end + + security([%{JWT: []}]) + + response(204, "OK") + response(403, "User not allowed to manage stories or stories from another course") + response(404, "Story not found") + end + + swagger_path :update do + post("/v2/courses/{course_id}/stories/{storyId}") + + summary("Update details regarding a story") + + parameters do + storyId(:path, :integer, "Story Id", required: true) + end + + security([%{JWT: []}]) + + produces("application/json") + + response(200, "OK", :Story) + response(403, "User not allowed to manage stories or stories from another course") + response(404, "Story not found") + end + + @spec swagger_definitions :: %{Story: any} + def swagger_definitions do + %{ + Story: + swagger_schema do + properties do + filenames(schema_array(:string), "Filenames of txt files", required: true) + title(:string, "Title shown in Chapter Select Screen", required: true) + imageUrl(:string, "Path to image shown in Chapter Select Screen", required: false) + openAt(:string, "The opening date", format: "date-time", required: true) + closeAt(:string, "The closing date", format: "date-time", required: true) + isPublished(:boolean, "Whether or not is published", required: false) + course_id(:integer, "The id of the course that this story belongs to", required: true) + end + end + } + end +end diff --git a/lib/cadet_web/controllers/sourcecast_controller.ex b/lib/cadet_web/controllers/sourcecast_controller.ex index c16704735..874717666 100644 --- a/lib/cadet_web/controllers/sourcecast_controller.ex +++ b/lib/cadet_web/controllers/sourcecast_controller.ex @@ -9,61 +9,29 @@ defmodule CadetWeb.SourcecastController do render(conn, "index.json", sourcecasts: sourcecasts) end - def index(conn, _params) do - sourcecasts = Courses.get_sourcecast_files() - render(conn, "index.json", sourcecasts: sourcecasts) - end - - def create(conn, %{"sourcecast" => sourcecast, "public" => _public}) do - result = - Courses.upload_sourcecast_file_public( - conn.assigns.current_user, - conn.assigns.course_reg, - sourcecast - ) - - case result do - {:ok, _nil} -> - send_resp(conn, 200, "OK") - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end - - def create(conn, %{"sourcecast" => sourcecast}) do - result = Courses.upload_sourcecast_file(conn.assigns.course_reg, sourcecast) - - case result do - {:ok, _nil} -> - send_resp(conn, 200, "OK") - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end - - def create(conn, _params) do - send_resp(conn, :bad_request, "Missing or invalid parameter(s)") - end - - def delete(conn, %{"id" => id}) do - result = Courses.delete_sourcecast_file(conn.assigns.course_reg, id) - - case result do - {:ok, _nil} -> - send_resp(conn, 200, "OK") - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end + # def index(conn, _params) do + # sourcecasts = Courses.get_sourcecast_files() + # render(conn, "index.json", sourcecasts: sourcecasts) + # end + + # def create(conn, %{"sourcecast" => sourcecast, "public" => _public}) do + # result = + # Courses.upload_sourcecast_file_public( + # conn.assigns.current_user, + # conn.assigns.course_reg, + # sourcecast + # ) + + # case result do + # {:ok, _nil} -> + # send_resp(conn, 200, "OK") + + # {:error, {status, message}} -> + # conn + # |> put_status(status) + # |> text(message) + # end + # end swagger_path :index do get("/sourcecast") @@ -77,43 +45,6 @@ defmodule CadetWeb.SourcecastController do response(401, "Unauthorised") end - swagger_path :create do - post("/sourcecast") - description("Uploads sourcecast") - summary("Upload sourcecast") - consumes("multipart/form-data") - security([%{JWT: []}]) - - parameters do - public( - :body, - :boolean, - "Uploads as public sourcecast when 'public' is specified regardless of truthy or falsy" - ) - - sourcecast(:body, Schema.ref(:Sourcecast), "sourcecast object", required: true) - end - - response(200, "Success") - response(400, "Invalid or missing parameter(s)") - response(401, "Unauthorised") - end - - swagger_path :delete do - PhoenixSwagger.Path.delete("/sourcecast/{id}") - description("Deletes sourcecast by id") - summary("Delete sourcecast") - security([%{JWT: []}]) - - parameters do - id(:path, :integer, "sourcecast id", required: true) - end - - response(200, "Success") - response(400, "Invalid or missing parameter(s)") - response(401, "Unauthorised") - end - def swagger_definitions do %{ Sourcecast: diff --git a/lib/cadet_web/controllers/stories_controller.ex b/lib/cadet_web/controllers/stories_controller.ex index 5d509eac3..a278ee20e 100644 --- a/lib/cadet_web/controllers/stories_controller.ex +++ b/lib/cadet_web/controllers/stories_controller.ex @@ -4,64 +4,12 @@ defmodule CadetWeb.StoriesController do alias Cadet.Stories.Stories - def index(conn, _) do - stories = Stories.list_stories(conn.assigns.course_reg) + def index(conn, %{"course_id" => course_id}) do + list_all = conn.assigns.course_reg.role in [:admin, :staff] + stories = Stories.list_stories(course_id, list_all) render(conn, "index.json", stories: stories) end - def create(conn, story) do - result = - story - |> snake_casify_string_keys() - |> string_to_atom_map_keys() - |> Stories.create_story(conn.assigns.course_reg) - - case result do - {:ok, _story} -> - conn |> put_status(200) |> text('') - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end - - def update(conn, _params = %{"storyid" => id, "story" => story}) do - result = - story - |> snake_casify_string_keys() - |> Stories.update_story(id, conn.assigns.course_reg) - - case result do - {:ok, _story} -> - conn |> put_status(200) |> text('') - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end - - def delete(conn, _params = %{"storyid" => id}) do - result = Stories.delete_story(id, conn.assigns.course_reg) - - case result do - {:ok, _nil} -> - conn |> put_status(204) |> text('') - - {:error, {status, message}} -> - conn - |> put_status(status) - |> text(message) - end - end - - defp string_to_atom_map_keys(map) do - for {key, value} <- map, into: %{}, do: {key |> String.to_atom(), value} - end - swagger_path :index do get("/v2/courses/{course_id}/stories") @@ -72,52 +20,6 @@ defmodule CadetWeb.StoriesController do response(200, "OK", Schema.array(:Story)) end - swagger_path :create do - post("/v2{course_id}/stories") - - summary("Creates a new story") - - security([%{JWT: []}]) - - response(200, "OK", :Story) - response(400, "Bad request") - response(403, "User not allowed to manage stories") - end - - swagger_path :delete do - PhoenixSwagger.Path.delete("/v2/courses/{course_id}/stories/{storyId}") - - summary("Delete a story from database by id") - - parameters do - storyId(:path, :integer, "Story Id", required: true) - end - - security([%{JWT: []}]) - - response(204, "OK") - response(403, "User not allowed to manage stories or stories from another course") - response(404, "Story not found") - end - - swagger_path :update do - post("/v2/courses/{course_id}/stories/{storyId}") - - summary("Update details regarding a story") - - parameters do - storyId(:path, :integer, "Story Id", required: true) - end - - security([%{JWT: []}]) - - produces("application/json") - - response(200, "OK", :Story) - response(403, "User not allowed to manage stories or stories from another course") - response(404, "Story not found") - end - @spec swagger_definitions :: %{Story: any} def swagger_definitions do %{ diff --git a/lib/cadet_web/router.ex b/lib/cadet_web/router.ex index df59ddf63..44522a898 100644 --- a/lib/cadet_web/router.ex +++ b/lib/cadet_web/router.ex @@ -34,7 +34,7 @@ defmodule CadetWeb.Router do scope "/v2", CadetWeb do pipe_through([:api, :auth]) - get("/sourcecast", SourcecastController, :index) + # get("/sourcecast", SourcecastController, :index) post("/auth/refresh", AuthController, :refresh) post("/auth/login", AuthController, :create) post("/auth/logout", AuthController, :logout) @@ -71,7 +71,6 @@ defmodule CadetWeb.Router do pipe_through([:api, :auth, :ensure_auth, :course]) get("/sourcecast", SourcecastController, :index) - resources("/sourcecast", SourcecastController, only: [:create, :delete]) get("/assessments", AssessmentsController, :index) get("/assessments/:assessmentid", AssessmentsController, :show) @@ -82,9 +81,6 @@ defmodule CadetWeb.Router do get("/achievements", IncentivesController, :index_achievements) get("/stories", StoriesController, :index) - post("/stories", StoriesController, :create) - delete("/stories/:storyid", StoriesController, :delete) - post("/stories/:storyid", StoriesController, :update) get("/notifications", NotificationsController, :index) post("/notifications/acknowledge", NotificationsController, :acknowledge) @@ -108,6 +104,8 @@ defmodule CadetWeb.Router do scope "/v2/courses/:course_id/admin", CadetWeb do pipe_through([:api, :auth, :ensure_auth, :course, :ensure_staff]) + resources("/sourcecast", AdminSourcecastController, only: [:create, :delete]) + get("/assets/:foldername", AdminAssetsController, :index) post("/assets/:foldername/*filename", AdminAssetsController, :upload) delete("/assets/:foldername/*filename", AdminAssetsController, :delete) @@ -145,6 +143,10 @@ defmodule CadetWeb.Router do put("/goals/:uuid", AdminGoalsController, :update) delete("/goals/:uuid", AdminGoalsController, :delete) + post("/stories", AdminStoriesController, :create) + delete("/stories/:storyid", AdminStoriesController, :delete) + post("/stories/:storyid", AdminStoriesController, :update) + put("/config", AdminCoursesController, :update_course_config) get("/config/assessment_configs", AdminCoursesController, :get_assessment_configs) put("/config/assessment_configs", AdminCoursesController, :update_assessment_configs) diff --git a/test/cadet/courses/courses_test.exs b/test/cadet/courses/courses_test.exs index 07fd08537..75a5dcdce 100644 --- a/test/cadet/courses/courses_test.exs +++ b/test/cadet/courses/courses_test.exs @@ -514,8 +514,7 @@ defmodule Cadet.CoursesTest do path = SourcecastUpload.url({sourcecast.audio, sourcecast}) assert path =~ "/uploads/test/sourcecasts/upload.wav" - deleter_course_registration = insert(:course_registration, %{role: :staff}) - assert {:ok, _} = Courses.delete_sourcecast_file(deleter_course_registration, sourcecast.id) + assert {:ok, _} = Courses.delete_sourcecast_file(sourcecast.id) assert Repo.get(Sourcecast, sourcecast.id) == nil refute File.exists?("uploads/test/sourcecasts/upload.wav") end diff --git a/test/cadet/stories/stories_test.exs b/test/cadet/stories/stories_test.exs index 51f225859..294cd61aa 100644 --- a/test/cadet/stories/stories_test.exs +++ b/test/cadet/stories/stories_test.exs @@ -39,7 +39,7 @@ defmodule Cadet.StoriesTest do story1 = :story |> insert(%{course: course}) |> remove_course_assoc() story2 = :story |> insert(%{course: course}) |> remove_course_assoc() - assert Stories.list_stories(insert(:course_registration, %{course: course, role: :staff})) == + assert Stories.list_stories(course.id, true) == [story1, story2] end @@ -48,7 +48,7 @@ defmodule Cadet.StoriesTest do insert(:story) story2 = :story |> insert(%{course: course}) |> remove_course_assoc() - assert Stories.list_stories(insert(:course_registration, %{course: course, role: :staff})) == + assert Stories.list_stories(course.id, true) == [story2] end @@ -69,94 +69,71 @@ defmodule Cadet.StoriesTest do }) |> remove_course_assoc() - assert Stories.list_stories(insert(:course_registration, %{course: course, role: :student})) == + assert Stories.list_stories(course.id, false) == [published_open_story] end end describe "Create story" do - test "create course story as staff", %{valid_params: params} do - course_registration = insert(:course_registration, %{role: :staff}) - {:ok, story} = Stories.create_story(params, course_registration) - params = Map.put(params, :course_id, course_registration.course_id) + test "create course story", %{valid_params: params} do + course = insert(:course) + {:ok, story} = Stories.create_story(params, course.id) + params = Map.put(params, :course_id, course.id) assert story |> Map.take(params |> Map.keys()) == params end - - test "students not allowed to create story", %{valid_params: params} do - course_registration = insert(:course_registration, %{role: :student}) - - assert {:error, {:forbidden, "User not allowed to manage stories"}} = - Stories.create_story(params, course_registration) - end end describe "Update story" do test "updating story as staff in own course", %{updated_params: updated_params} do - course_registration = insert(:course_registration, %{role: :staff}) - story = insert(:story, %{course: course_registration.course}) - {:ok, updated_story} = Stories.update_story(updated_params, story.id, course_registration) - updated_params = Map.put(updated_params, :course_id, course_registration.course_id) + course = insert(:course) + story = insert(:story, %{course: course}) + {:ok, updated_story} = Stories.update_story(updated_params, story.id, course.id) + updated_params = Map.put(updated_params, :course_id, course.id) assert updated_story |> Map.take(updated_params |> Map.keys()) == updated_params end test "updating story that does not exist as staff", %{updated_params: updated_params} do - course_registration = insert(:course_registration, %{role: :staff}) - story = insert(:story, %{course: course_registration.course}) + course = insert(:course) + story = insert(:story, %{course: course}) {:error, {:not_found, "Story not found"}} = - Stories.update_story(updated_params, story.id + 1, course_registration) + Stories.update_story(updated_params, story.id + 1, course.id) end test "staff fails to update story of another course", %{updated_params: updated_params} do - course_registration = insert(:course_registration, %{role: :staff}) + course = insert(:course) story = insert(:story, %{course: build(:course)}) assert {:error, {:forbidden, "User not allowed to manage stories from another course"}} = - Stories.update_story(updated_params, story.id, course_registration) - end - - test "student fails to update story of own course", %{updated_params: updated_params} do - course_registration = insert(:course_registration, %{role: :student}) - story = insert(:story, %{course: course_registration.course}) - - assert {:error, {:forbidden, "User not allowed to manage stories"}} = - Stories.update_story(updated_params, story.id, course_registration) + Stories.update_story(updated_params, story.id, course.id) end end describe "Delete story" do test "staff deleting course story from own course" do - course_registration = insert(:course_registration, %{role: :staff}) - story = insert(:story, %{course: course_registration.course}) - {:ok, story} = Stories.delete_story(story.id, course_registration) + course = insert(:course) + story = insert(:story, %{course: course}) + {:ok, story} = Stories.delete_story(story.id, course.id) assert Repo.get(Story, story.id) == nil end test "staff deleting course story that does not exist" do - course_registration = insert(:course_registration, %{role: :staff}) - story = insert(:story, %{course: course_registration.course}) + course = insert(:course) + story = insert(:story, %{course: course}) assert {:error, {:not_found, "Story not found"}} = - Stories.delete_story(story.id + 1, course_registration) + Stories.delete_story(story.id + 1, course.id) end test "staff fails to delete story from another course" do - course_registration = insert(:course_registration, %{role: :staff}) + course = insert(:course) story = insert(:story, %{course: build(:course)}) assert {:error, {:forbidden, "User not allowed to manage stories from another course"}} = - Stories.delete_story(story.id, course_registration) - end - - test "student fails to delete story from own course" do - course_registration = insert(:course_registration, %{role: :student}) - story = insert(:story, %{course: course_registration.course}) - - assert {:error, {:forbidden, "User not allowed to manage stories"}} = - Stories.delete_story(story.id, course_registration) + Stories.delete_story(story.id, course.id) end end diff --git a/test/cadet_web/admin_controllers/admin_sourcecast_controller_test.exs b/test/cadet_web/admin_controllers/admin_sourcecast_controller_test.exs new file mode 100644 index 000000000..96aa4140b --- /dev/null +++ b/test/cadet_web/admin_controllers/admin_sourcecast_controller_test.exs @@ -0,0 +1,329 @@ +defmodule CadetWeb.AdminSourcecastControllerTest do + use CadetWeb.ConnCase + + import Ecto.Query + + alias Cadet.Repo + alias Cadet.Courses.Course + alias CadetWeb.AdminSourcecastController + + test "swagger" do + AdminSourcecastController.swagger_definitions() + AdminSourcecastController.swagger_path_create(nil) + AdminSourcecastController.swagger_path_delete(nil) + end + + describe "POST /v2/courses/{course_id}/sourcecast, unauthenticated" do + test "unauthorized", %{conn: conn} do + course = insert(:course) + conn = post(conn, build_url(course.id), %{}) + assert response(conn, 401) =~ "Unauthorised" + end + end + + describe "DELETE /v2/courses/{course_id}/sourcecast, unauthenticated" do + test "unauthorized", %{conn: conn} do + course = insert(:course) + seed_db(course.id) + conn = delete(conn, build_url(course.id, 1), %{}) + assert response(conn, 401) =~ "Unauthorised" + end + end + + describe "POST /v2/courses/{course_id}/sourcecast, student" do + @tag authenticate: :student + test "prohibited", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = + post(conn, build_url(course_id), %{ + "sourcecast" => %{ + "title" => "Title", + "description" => "Description", + "playbackData" => + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + "audio" => %Plug.Upload{ + content_type: "audio/wav", + filename: "upload.wav", + path: "test/fixtures/upload.wav" + } + } + }) + + assert response(conn, 403) =~ "Forbidden" + end + end + + describe "DELETE /v2/courses/{course_id}/sourcecast, student" do + @tag authenticate: :student + test "prohibited", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = delete(conn, build_url(course_id, 1), %{}) + + assert response(conn, 403) =~ "Forbidden" + end + end + + describe "POST /v2/courses/{course_id}/sourcecast, staff" do + # @tag authenticate: :staff + # test "successful for public sourcecast", %{conn: conn} do + # course_id = conn.assigns[:course_id] + + # post_conn = + # post(conn, build_url(course_id), %{ + # "sourcecast" => %{ + # "title" => "Title", + # "description" => "Description", + # "playbackData" => + # "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + # "audio" => %Plug.Upload{ + # content_type: "audio/wav", + # filename: "upload.wav", + # path: "test/fixtures/upload.wav" + # } + # }, + # "public" => true + # }) + + # assert response(post_conn, 200) == "OK" + + # expected = [ + # %{ + # "title" => "Title", + # "description" => "Description", + # "playbackData" => + # "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + # "uploader" => %{ + # "id" => conn.assigns[:current_user].id, + # "name" => conn.assigns[:current_user].name + # }, + # "courseId" => nil + # } + # ] + + # res = + # conn + # |> get(build_url()) + # |> json_response(200) + # |> Enum.map(&Map.delete(&1, "audio")) + # |> Enum.map(&Map.delete(&1, "inserted_at")) + # |> Enum.map(&Map.delete(&1, "updated_at")) + # |> Enum.map(&Map.delete(&1, "id")) + # |> Enum.map(&Map.delete(&1, "uid")) + # |> Enum.map(&Map.delete(&1, "url")) + + # assert expected == res + # end + + @tag authenticate: :staff + test "successful for course sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + post_conn = + post(conn, build_url(course_id), %{ + "sourcecast" => %{ + "title" => "Title", + "description" => "Description", + "playbackData" => + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + "audio" => %Plug.Upload{ + content_type: "audio/wav", + filename: "upload.wav", + path: "test/fixtures/upload.wav" + } + } + }) + + assert response(post_conn, 200) == "OK" + + expected = [ + %{ + "title" => "Title", + "description" => "Description", + "playbackData" => + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + "uploader" => %{ + "id" => conn.assigns[:current_user].id, + "name" => conn.assigns[:current_user].name + }, + "courseId" => course_id + } + ] + + res = + conn + |> get("/v2/courses/#{course_id}/sourcecast") + |> json_response(200) + |> Enum.map(&Map.delete(&1, "audio")) + |> Enum.map(&Map.delete(&1, "inserted_at")) + |> Enum.map(&Map.delete(&1, "updated_at")) + |> Enum.map(&Map.delete(&1, "id")) + |> Enum.map(&Map.delete(&1, "uid")) + |> Enum.map(&Map.delete(&1, "url")) + + assert expected == res + end + + @tag authenticate: :staff + test "missing parameter", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = post(conn, build_url(course_id), %{}) + assert response(conn, 400) =~ "Missing or invalid parameter(s)" + end + end + + describe "DELETE /v2/courses/{course_id}/sourcecast, staff" do + # @tag authenticate: :staff + # test "successful for public sourcecast", %{conn: conn} do + # course_id = conn.assigns[:course_id] + + # %{sourcecasts: sourcecasts} = seed_db() + # sourcecast = List.first(sourcecasts) + + # conn = delete(conn, build_url(course_id, sourcecast.id), %{}) + + # assert response(conn, 200) =~ "OK" + # end + + @tag authenticate: :staff + test "successful for course sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + %{sourcecasts: sourcecasts} = seed_db(course_id) + sourcecast = List.first(sourcecasts) + + conn = delete(conn, build_url(course_id, sourcecast.id), %{}) + + assert response(conn, 200) =~ "OK" + end + end + + describe "POST /v2/courses/{course_id}/sourcecast, admin" do + @tag authenticate: :admin + test "successful for public sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = + post(conn, build_url(course_id), %{ + "sourcecast" => %{ + "title" => "Title", + "description" => "Description", + "playbackData" => + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + "audio" => %Plug.Upload{ + content_type: "audio/wav", + filename: "upload.wav", + path: "test/fixtures/upload.wav" + } + }, + "public" => true + }) + + assert response(conn, 200) == "OK" + end + + @tag authenticate: :admin + test "successful for course sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = + post(conn, build_url(course_id), %{ + "sourcecast" => %{ + "title" => "Title", + "description" => "Description", + "playbackData" => + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + "audio" => %Plug.Upload{ + content_type: "audio/wav", + filename: "upload.wav", + path: "test/fixtures/upload.wav" + } + } + }) + + assert response(conn, 200) == "OK" + end + + @tag authenticate: :admin + test "missing parameter", %{conn: conn} do + course_id = conn.assigns[:course_id] + + conn = post(conn, build_url(course_id), %{}) + assert response(conn, 400) =~ "Missing or invalid parameter(s)" + end + end + + describe "DELETE /v2/courses/{course_id}/sourcecast, admin" do + @tag authenticate: :admin + test "successful for public sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + %{sourcecasts: sourcecasts} = seed_db() + sourcecast = List.first(sourcecasts) + + conn = delete(conn, build_url(course_id, sourcecast.id), %{}) + + assert response(conn, 200) =~ "OK" + end + + @tag authenticate: :admin + test "successful for course sourcecast", %{conn: conn} do + course_id = conn.assigns[:course_id] + + %{sourcecasts: sourcecasts} = seed_db(course_id) + sourcecast = List.first(sourcecasts) + + conn = delete(conn, build_url(course_id, sourcecast.id), %{}) + + assert response(conn, 200) =~ "OK" + end + end + + defp build_url(course_id), do: "/v2/courses/#{course_id}/admin/sourcecast/" + defp build_url(course_id, sourcecast_id), do: "#{build_url(course_id)}#{sourcecast_id}/" + + defp seed_db(course_id) do + course = Course |> where(id: ^course_id) |> Repo.one() + + sourcecasts = + for i <- 0..4 do + insert(:sourcecast, %{ + title: "Title#{i}", + description: "Description#{i}", + uid: "unique_id#{i}", + playbackData: + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + audio: %Plug.Upload{ + content_type: "audio/wav", + filename: "upload#{i}.wav", + path: "test/fixtures/upload.wav" + }, + course: course + }) + end + + %{sourcecasts: sourcecasts} + end + + defp seed_db do + sourcecasts = + for i <- 5..9 do + insert(:sourcecast, %{ + title: "Title#{i}", + description: "Description#{i}", + uid: "unique_id#{i}", + playbackData: + "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", + audio: %Plug.Upload{ + content_type: "audio/wav", + filename: "upload#{i}.wav", + path: "test/fixtures/upload.wav" + } + }) + end + + %{sourcecasts: sourcecasts} + end +end diff --git a/test/cadet_web/admin_controllers/admin_stories_controller_test.exs b/test/cadet_web/admin_controllers/admin_stories_controller_test.exs new file mode 100644 index 000000000..8881360b8 --- /dev/null +++ b/test/cadet_web/admin_controllers/admin_stories_controller_test.exs @@ -0,0 +1,173 @@ +defmodule CadetWeb.AdminStoriesControllerTest do + use CadetWeb.ConnCase + use Timex + + import Ecto.Query + + alias Cadet.Courses.Course + alias Cadet.Repo + alias Cadet.Stories.Story + alias CadetWeb.AdminStoriesController + + setup do + valid_params = %{ + open_at: Timex.shift(Timex.now(), days: 1), + close_at: Timex.shift(Timex.now(), days: Enum.random(2..30)), + is_published: false, + filenames: ["mission-1.txt"], + title: "Mission1", + image_url: "http://example.com" + } + + updated_params = %{ + title: "Mission2", + image_url: "http://example.com/new" + } + + {:ok, %{valid_params: valid_params, updated_params: updated_params}} + end + + test "swagger" do + AdminStoriesController.swagger_definitions() + AdminStoriesController.swagger_path_create(nil) + AdminStoriesController.swagger_path_delete(nil) + AdminStoriesController.swagger_path_update(nil) + end + + describe "unauthenticated" do + test "POST /v2/courses/{course_id}/admin/stories/", %{conn: conn} do + course = insert(:course) + conn = post(conn, build_url(course.id), %{}) + assert response(conn, 401) =~ "Unauthorised" + end + + test "DELETE /v2/courses/{course_id}/admin/stories/:storyid", %{conn: conn} do + course = insert(:course) + conn = delete(conn, build_url(course.id, "storyid"), %{}) + assert response(conn, 401) =~ "Unauthorised" + end + + test "POST /v2/courses/{course_id}/admin/stories/:storyid", %{conn: conn} do + course = insert(:course) + conn = post(conn, build_url(course.id, "storyid"), %{}) + assert response(conn, 401) =~ "Unauthorised" + end + end + + describe "DELETE /v2/courses/{course_id}/admin/stories/:storyid" do + @tag authenticate: :student + test "student permission, forbidden", %{conn: conn} do + course_id = conn.assigns[:course_id] + course = Course |> where(id: ^course_id) |> Repo.one() + story = insert(:story, %{course: course}) + + conn = delete(conn, build_url(course_id, story.id), %{}) + assert response(conn, 403) =~ "Forbidden" + end + + @tag authenticate: :staff + test "staff successfully deletes story from own course", %{conn: conn} do + course_id = conn.assigns[:course_id] + course = Course |> where(id: ^course_id) |> Repo.one() + story = insert(:story, %{course: course}) + + resp = delete(conn, build_url(course_id, story.id), %{}) + assert response(resp, 204) == "" + + assert Story + |> where(id: ^story.id) + |> Repo.one() == nil + end + + @tag authenticate: :staff + test "staff fails to delete story from another course", %{conn: conn} do + course_id = conn.assigns[:course_id] + story = insert(:story, %{course: build(:course)}) + + resp = delete(conn, build_url(course_id, story.id), %{}) + + assert response(resp, 403) == "User not allowed to manage stories from another course" + end + end + + describe "POST /v2/courses/{course_id}/admin/stories/" do + @tag authenticate: :student + test "student permission, forbidden", %{conn: conn, valid_params: params} do + course_id = conn.assigns[:course_id] + + conn = post(conn, build_url(course_id), params) + assert response(conn, 403) =~ "Forbidden" + end + + @tag authenticate: :staff + test "creates a new story", %{conn: conn, valid_params: params} do + course_id = conn.assigns[:course_id] + + conn = post(conn, build_url(course_id), %{"story" => stringify_camelise_keys(params)}) + + inserted_story = + Story + |> where(title: ^params.title) + |> Repo.one() + + params = Map.put(params, :course_id, course_id) + assert inserted_story |> Map.take(Map.keys(params)) == params + assert response(conn, 200) == "" + end + end + + describe "POST /v2/courses/{course_id}/admin/stories/:storyid" do + @tag authenticate: :student + test "student permission, forbidden", %{conn: conn, valid_params: params} do + course_id = conn.assigns[:course_id] + + conn = post(conn, build_url(course_id, 1), %{"story" => params}) + assert response(conn, 403) =~ "Forbidden" + end + + @tag authenticate: :staff + test "staff successfully updates a story from own course", %{ + conn: conn, + updated_params: updated_params + } do + course_id = conn.assigns[:course_id] + course = Course |> where(id: ^course_id) |> Repo.one() + story = insert(:story, %{course: course}) + + conn = + post(conn, build_url(course_id, story.id), %{ + "story" => stringify_camelise_keys(updated_params) + }) + + updated_story = Repo.get(Story, story.id) + updated_params = Map.put(updated_params, :course_id, course_id) + + assert updated_story |> Map.take(Map.keys(updated_params)) == updated_params + + assert response(conn, 200) == "" + end + + @tag authenticate: :staff + test "staff fails to update a story from another course", %{ + conn: conn, + updated_params: updated_params + } do + course_id = conn.assigns[:course_id] + story = insert(:story, %{course: build(:course)}) + + resp = + post(conn, build_url(course_id, story.id), %{ + "story" => stringify_camelise_keys(updated_params) + }) + + assert response(resp, 403) == "User not allowed to manage stories from another course" + end + end + + defp build_url(course_id), do: "/v2/courses/#{course_id}/admin/stories" + defp build_url(course_id, story_id), do: "#{build_url(course_id)}/#{story_id}" + + defp stringify_camelise_keys(map) do + for {key, value} <- map, into: %{}, do: {key |> Atom.to_string() |> Recase.to_camel(), value} + end +end diff --git a/test/cadet_web/controllers/sourcecast_controller_test.exs b/test/cadet_web/controllers/sourcecast_controller_test.exs index 6ba74aa20..f0c32f186 100644 --- a/test/cadet_web/controllers/sourcecast_controller_test.exs +++ b/test/cadet_web/controllers/sourcecast_controller_test.exs @@ -10,64 +10,45 @@ defmodule CadetWeb.SourcecastControllerTest do test "swagger" do SourcecastController.swagger_definitions() SourcecastController.swagger_path_index(nil) - SourcecastController.swagger_path_create(nil) - SourcecastController.swagger_path_delete(nil) end - describe "GET /v2/sourcecast, unauthenticated" do - test "renders a list of all sourcecast entries for public (those without course_id)", %{ - conn: conn - } do - %{sourcecasts: sourcecasts} = seed_db() - course = insert(:course) - seed_db(course.id) - - expected = - sourcecasts - |> Enum.map( - &%{ - "id" => &1.id, - "title" => &1.title, - "description" => &1.description, - "uid" => &1.uid, - "playbackData" => &1.playbackData, - "uploader" => %{ - "name" => &1.uploader.name, - "id" => &1.uploader.id - }, - "url" => Cadet.Courses.SourcecastUpload.url({&1.audio, &1}), - "courseId" => nil - } - ) - - res = - conn - |> get(build_url()) - |> json_response(200) - |> Enum.map(&Map.delete(&1, "audio")) - |> Enum.map(&Map.delete(&1, "inserted_at")) - |> Enum.map(&Map.delete(&1, "updated_at")) - - assert expected == res - end - end - - describe "POST /v2/courses/{course_id}/sourcecast, unauthenticated" do - test "unauthorized", %{conn: conn} do - course = insert(:course) - conn = post(conn, build_url(course.id), %{}) - assert response(conn, 401) =~ "Unauthorised" - end - end - - describe "DELETE /v2/courses/{course_id}/sourcecast, unauthenticated" do - test "unauthorized", %{conn: conn} do - course = insert(:course) - seed_db(course.id) - conn = delete(conn, build_url(course.id, 1), %{}) - assert response(conn, 401) =~ "Unauthorised" - end - end + # describe "GET /v2/sourcecast, unauthenticated" do + # test "renders a list of all sourcecast entries for public (those without course_id)", %{ + # conn: conn + # } do + # %{sourcecasts: sourcecasts} = seed_db() + # course = insert(:course) + # seed_db(course.id) + + # expected = + # sourcecasts + # |> Enum.map( + # &%{ + # "id" => &1.id, + # "title" => &1.title, + # "description" => &1.description, + # "uid" => &1.uid, + # "playbackData" => &1.playbackData, + # "uploader" => %{ + # "name" => &1.uploader.name, + # "id" => &1.uploader.id + # }, + # "url" => Cadet.Courses.SourcecastUpload.url({&1.audio, &1}), + # "courseId" => nil + # } + # ) + + # res = + # conn + # |> get(build_url()) + # |> json_response(200) + # |> Enum.map(&Map.delete(&1, "audio")) + # |> Enum.map(&Map.delete(&1, "inserted_at")) + # |> Enum.map(&Map.delete(&1, "updated_at")) + + # assert expected == res + # end + # end describe "GET /v2/courses/{course_id}/sourcecast, returns course sourcecasts" do @tag authenticate: :student @@ -108,260 +89,8 @@ defmodule CadetWeb.SourcecastControllerTest do end end - describe "POST /v2/courses/{course_id}/sourcecast, student" do - @tag authenticate: :student - test "prohibited", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = - post(conn, build_url(course_id), %{ - "sourcecast" => %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "audio" => %Plug.Upload{ - content_type: "audio/wav", - filename: "upload.wav", - path: "test/fixtures/upload.wav" - } - } - }) - - assert response(conn, 403) =~ "User is not permitted to upload" - end - end - - describe "DELETE /v2/courses/{course_id}/sourcecast, student" do - @tag authenticate: :student - test "prohibited", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = delete(conn, build_url(course_id, 1), %{}) - - assert response(conn, 403) =~ "User is not permitted to delete" - end - end - - describe "POST /v2/courses/{course_id}/sourcecast, staff" do - @tag authenticate: :staff - test "successful for public sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - post_conn = - post(conn, build_url(course_id), %{ - "sourcecast" => %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "audio" => %Plug.Upload{ - content_type: "audio/wav", - filename: "upload.wav", - path: "test/fixtures/upload.wav" - } - }, - "public" => true - }) - - assert response(post_conn, 200) == "OK" - - expected = [ - %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "uploader" => %{ - "id" => conn.assigns[:current_user].id, - "name" => conn.assigns[:current_user].name - }, - "courseId" => nil - } - ] - - res = - conn - |> get(build_url()) - |> json_response(200) - |> Enum.map(&Map.delete(&1, "audio")) - |> Enum.map(&Map.delete(&1, "inserted_at")) - |> Enum.map(&Map.delete(&1, "updated_at")) - |> Enum.map(&Map.delete(&1, "id")) - |> Enum.map(&Map.delete(&1, "uid")) - |> Enum.map(&Map.delete(&1, "url")) - - assert expected == res - end - - @tag authenticate: :staff - test "successful for course sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - post_conn = - post(conn, build_url(course_id), %{ - "sourcecast" => %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "audio" => %Plug.Upload{ - content_type: "audio/wav", - filename: "upload.wav", - path: "test/fixtures/upload.wav" - } - } - }) - - assert response(post_conn, 200) == "OK" - - expected = [ - %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "uploader" => %{ - "id" => conn.assigns[:current_user].id, - "name" => conn.assigns[:current_user].name - }, - "courseId" => course_id - } - ] - - res = - conn - |> get(build_url(course_id)) - |> json_response(200) - |> Enum.map(&Map.delete(&1, "audio")) - |> Enum.map(&Map.delete(&1, "inserted_at")) - |> Enum.map(&Map.delete(&1, "updated_at")) - |> Enum.map(&Map.delete(&1, "id")) - |> Enum.map(&Map.delete(&1, "uid")) - |> Enum.map(&Map.delete(&1, "url")) - - assert expected == res - end - - @tag authenticate: :staff - test "missing parameter", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = post(conn, build_url(course_id), %{}) - assert response(conn, 400) =~ "Missing or invalid parameter(s)" - end - end - - describe "DELETE /v2/courses/{course_id}/sourcecast, staff" do - @tag authenticate: :staff - test "successful for public sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - %{sourcecasts: sourcecasts} = seed_db() - sourcecast = List.first(sourcecasts) - - conn = delete(conn, build_url(course_id, sourcecast.id), %{}) - - assert response(conn, 200) =~ "OK" - end - - @tag authenticate: :staff - test "successful for course sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - %{sourcecasts: sourcecasts} = seed_db(course_id) - sourcecast = List.first(sourcecasts) - - conn = delete(conn, build_url(course_id, sourcecast.id), %{}) - - assert response(conn, 200) =~ "OK" - end - end - - describe "POST /v2/courses/{course_id}/sourcecast, admin" do - @tag authenticate: :admin - test "successful for public sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = - post(conn, build_url(course_id), %{ - "sourcecast" => %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "audio" => %Plug.Upload{ - content_type: "audio/wav", - filename: "upload.wav", - path: "test/fixtures/upload.wav" - } - }, - "public" => true - }) - - assert response(conn, 200) == "OK" - end - - @tag authenticate: :admin - test "successful for course sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = - post(conn, build_url(course_id), %{ - "sourcecast" => %{ - "title" => "Title", - "description" => "Description", - "playbackData" => - "{\"init\":{\"editorValue\":\"// Type your program in here!\"},\"inputs\":[]}", - "audio" => %Plug.Upload{ - content_type: "audio/wav", - filename: "upload.wav", - path: "test/fixtures/upload.wav" - } - } - }) - - assert response(conn, 200) == "OK" - end - - @tag authenticate: :admin - test "missing parameter", %{conn: conn} do - course_id = conn.assigns[:course_id] - - conn = post(conn, build_url(course_id), %{}) - assert response(conn, 400) =~ "Missing or invalid parameter(s)" - end - end - - describe "DELETE /v2/courses/{course_id}/sourcecast, admin" do - @tag authenticate: :admin - test "successful for public sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - %{sourcecasts: sourcecasts} = seed_db() - sourcecast = List.first(sourcecasts) - - conn = delete(conn, build_url(course_id, sourcecast.id), %{}) - - assert response(conn, 200) =~ "OK" - end - - @tag authenticate: :admin - test "successful for course sourcecast", %{conn: conn} do - course_id = conn.assigns[:course_id] - - %{sourcecasts: sourcecasts} = seed_db(course_id) - sourcecast = List.first(sourcecasts) - - conn = delete(conn, build_url(course_id, sourcecast.id), %{}) - - assert response(conn, 200) =~ "OK" - end - end - - defp build_url, do: "/v2/sourcecast/" + # defp build_url, do: "/v2/sourcecast/" defp build_url(course_id), do: "/v2/courses/#{course_id}/sourcecast/" - defp build_url(course_id, sourcecast_id), do: "#{build_url(course_id)}#{sourcecast_id}/" defp seed_db(course_id) do course = Course |> where(id: ^course_id) |> Repo.one() diff --git a/test/cadet_web/controllers/stories_controller_test.exs b/test/cadet_web/controllers/stories_controller_test.exs index 9019f0f55..299455618 100644 --- a/test/cadet_web/controllers/stories_controller_test.exs +++ b/test/cadet_web/controllers/stories_controller_test.exs @@ -6,7 +6,6 @@ defmodule CadetWeb.StoriesControllerTest do alias Cadet.Courses.Course alias Cadet.Repo - alias Cadet.Stories.Story alias CadetWeb.StoriesController setup do @@ -30,9 +29,6 @@ defmodule CadetWeb.StoriesControllerTest do test "swagger" do StoriesController.swagger_definitions() StoriesController.swagger_path_index(nil) - StoriesController.swagger_path_create(nil) - StoriesController.swagger_path_delete(nil) - StoriesController.swagger_path_update(nil) end describe "unauthenticated" do @@ -41,24 +37,6 @@ defmodule CadetWeb.StoriesControllerTest do conn = get(conn, build_url(course.id), %{}) assert response(conn, 401) =~ "Unauthorised" end - - test "POST /v2/courses/{course_id}/stories/", %{conn: conn} do - course = insert(:course) - conn = post(conn, build_url(course.id), %{}) - assert response(conn, 401) =~ "Unauthorised" - end - - test "DELETE /v2/courses/{course_id}/stories/:storyid", %{conn: conn} do - course = insert(:course) - conn = delete(conn, build_url(course.id, "storyid"), %{}) - assert response(conn, 401) =~ "Unauthorised" - end - - test "POST /v2/courses/{course_id}/stories/:storyid", %{conn: conn} do - course = insert(:course) - conn = post(conn, build_url(course.id, "storyid"), %{}) - assert response(conn, 401) =~ "Unauthorised" - end end describe "GET /v2/courses/{course_id}/stories" do @@ -153,121 +131,5 @@ defmodule CadetWeb.StoriesControllerTest do end end - describe "DELETE /v2/courses/{course_id}/stories/:storyid" do - @tag authenticate: :student - test "student permission, forbidden", %{conn: conn} do - course_id = conn.assigns[:course_id] - course = Course |> where(id: ^course_id) |> Repo.one() - story = insert(:story, %{course: course}) - - conn = delete(conn, build_url(course_id, story.id), %{}) - assert response(conn, 403) =~ "User not allowed to manage stories" - end - - @tag authenticate: :staff - test "staff successfully deletes story from own course", %{conn: conn} do - course_id = conn.assigns[:course_id] - course = Course |> where(id: ^course_id) |> Repo.one() - story = insert(:story, %{course: course}) - - resp = delete(conn, build_url(course_id, story.id), %{}) - - assert Story - |> where(id: ^story.id) - |> Repo.one() == nil - - assert response(resp, 204) == "" - end - - @tag authenticate: :staff - test "staff fails to delete story from another course", %{conn: conn} do - course_id = conn.assigns[:course_id] - story = insert(:story, %{course: build(:course)}) - - resp = delete(conn, build_url(course_id, story.id), %{}) - - assert response(resp, 403) == "User not allowed to manage stories from another course" - end - end - - describe "POST /v2/courses/{course_id}/stories/" do - @tag authenticate: :student - test "student permission, forbidden", %{conn: conn, valid_params: params} do - course_id = conn.assigns[:course_id] - - conn = post(conn, build_url(course_id), params) - assert response(conn, 403) =~ "User not allowed to manage stories" - end - - @tag authenticate: :staff - test "creates a new story", %{conn: conn, valid_params: params} do - course_id = conn.assigns[:course_id] - - conn = post(conn, build_url(course_id), stringify_camelise_keys(params)) - - inserted_story = - Story - |> where(title: ^params.title) - |> Repo.one() - - params = Map.put(params, :course_id, course_id) - assert inserted_story |> Map.take(Map.keys(params)) == params - assert response(conn, 200) == "" - end - end - - describe "POST /v2/courses/{course_id}/stories/:storyid" do - @tag authenticate: :student - test "student permission, forbidden", %{conn: conn, valid_params: params} do - course_id = conn.assigns[:course_id] - - conn = post(conn, build_url(course_id), %{"story" => params}) - assert response(conn, 403) =~ "User not allowed to manage stories" - end - - @tag authenticate: :staff - test "staff successfully updates a story from own course", %{ - conn: conn, - updated_params: updated_params - } do - course_id = conn.assigns[:course_id] - course = Course |> where(id: ^course_id) |> Repo.one() - story = insert(:story, %{course: course}) - - conn = - post(conn, build_url(course_id, story.id), %{ - "story" => stringify_camelise_keys(updated_params) - }) - - updated_story = Repo.get(Story, story.id) - updated_params = Map.put(updated_params, :course_id, course_id) - - assert updated_story |> Map.take(Map.keys(updated_params)) == updated_params - - assert response(conn, 200) == "" - end - - @tag authenticate: :staff - test "staff fails to update a story from another course", %{ - conn: conn, - updated_params: updated_params - } do - course_id = conn.assigns[:course_id] - story = insert(:story, %{course: build(:course)}) - - resp = - post(conn, build_url(course_id, story.id), %{ - "story" => stringify_camelise_keys(updated_params) - }) - - assert response(resp, 403) == "User not allowed to manage stories from another course" - end - end - defp build_url(course_id), do: "/v2/courses/#{course_id}/stories" - defp build_url(course_id, story_id), do: "#{build_url(course_id)}/#{story_id}" - - defp stringify_camelise_keys(map) do - for {key, value} <- map, into: %{}, do: {key |> Atom.to_string() |> Recase.to_camel(), value} - end end