diff --git a/test/jellyfish/component/file_test.exs b/test/jellyfish/component/file_test.exs deleted file mode 100644 index 0140bb3e..00000000 --- a/test/jellyfish/component/file_test.exs +++ /dev/null @@ -1,147 +0,0 @@ -defmodule Jellyfish.Component.FileTest do - @moduledoc false - - use ExUnit.Case, async: true - - alias ExSDP.Attribute.FMTP - - alias Jellyfish.Component - alias Membrane.RTC.Engine.Endpoint - - @engine_pid "placeholder" - - @fixtures_location "test/fixtures" - @video_filename "video.h264" - @audio_filename "audio.ogg" - - @properties %{} - @jellyfish_opts %{engine_pid: @engine_pid, room_id: "example-room-id"} - @files_location "file_component_sources" - - setup_all do - base_path = - Application.fetch_env!(:jellyfish, :media_files_path) - |> Path.join(@files_location) - |> Path.expand() - - File.mkdir_p!(base_path) - - video_src = Path.join(@fixtures_location, @video_filename) - video_dst = Path.join(base_path, @video_filename) - File.cp!(video_src, video_dst) - - audio_src = Path.join(@fixtures_location, @audio_filename) - audio_dst = Path.join(base_path, @audio_filename) - File.cp!(audio_src, audio_dst) - - on_exit(fn -> :file.del_dir_r(base_path) end) - - %{ - base_path: base_path, - video_path: Path.expand(video_dst), - audio_path: Path.expand(audio_dst) - } - end - - test "video file", %{video_path: video_path} do - options = Map.put(@jellyfish_opts, "filePath", @video_filename) - - expected = get_video_endpoint(video_path) - - {:ok, %{endpoint: ^expected, properties: @properties}} = Component.File.config(options) - end - - test "audio file", %{audio_path: audio_path} do - options = Map.put(@jellyfish_opts, "filePath", @audio_filename) - - expected = get_audio_endpoint(audio_path) - - {:ok, %{endpoint: ^expected, properties: @properties}} = Component.File.config(options) - end - - test "file in subdirectory", %{base_path: base_path} do - subdir_name = "subdirectory" - [base_path, subdir_name] |> Path.join() |> File.mkdir_p!() - - video_relative_path = Path.join(subdir_name, @video_filename) - video_absolute_path = Path.join(base_path, video_relative_path) - File.touch!(video_absolute_path) - - options = Map.put(@jellyfish_opts, "filePath", video_relative_path) - - expected = get_video_endpoint(video_absolute_path) - - {:ok, %{endpoint: ^expected, properties: @properties}} = Component.File.config(options) - end - - test "path for non-existent file" do - options = Map.put(@jellyfish_opts, "filePath", "nosuchfile.opus") - - {:error, :file_does_not_exist} = Component.File.config(options) - end - - test "invalid extension", %{base_path: base_path} do - filename = "sounds.aac" - base_path |> Path.join(filename) |> File.touch!() - - options = Map.put(@jellyfish_opts, "filePath", filename) - - {:error, :unsupported_file_type} = Component.File.config(options) - end - - test "no extension", %{base_path: base_path} do - filename = "h264" - base_path |> Path.join(filename) |> File.touch!() - - options = Map.put(@jellyfish_opts, "filePath", filename) - - {:error, :unsupported_file_type} = Component.File.config(options) - end - - test "file outside of media files directory", %{base_path: base_path} do - filename = "../restricted_audio.opus" - outside_path = base_path |> Path.join(filename) - File.touch!(outside_path) - - options = Map.put(@jellyfish_opts, "filePath", filename) - - {:error, :invalid_file_path} = Component.File.config(options) - File.rm(outside_path) - end - - test "missing filePath" do - {:error, {:missing_parameter, :filePath}} = Component.File.config(@jellyfish_opts) - end - - defp get_audio_endpoint(audio_path) do - %Endpoint.File{ - rtc_engine: @engine_pid, - file_path: audio_path, - track_config: %Endpoint.File.TrackConfig{ - type: :audio, - encoding: :OPUS, - clock_rate: 48_000, - fmtp: %FMTP{pt: 108}, - opts: [] - }, - payload_type: 108 - } - end - - defp get_video_endpoint(video_path) do - %Endpoint.File{ - rtc_engine: @engine_pid, - file_path: video_path, - track_config: %Endpoint.File.TrackConfig{ - type: :video, - encoding: :H264, - clock_rate: 90_000, - fmtp: %FMTP{pt: 96}, - opts: [ - framerate: {30, 1} - ] - }, - payload_type: 96 - } - end -end diff --git a/test/jellyfish/component/rtsp_test.exs b/test/jellyfish/component/rtsp_test.exs deleted file mode 100644 index d5091e86..00000000 --- a/test/jellyfish/component/rtsp_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -defmodule Jellyfish.Component.RTSPTest do - @moduledoc false - - use ExUnit.Case, async: true - - alias Jellyfish.Component - alias Membrane.RTC.Engine.Endpoint - - @engine_pid "placeholder" - @source_uri "rtsp://ef36c6dff23ecc5bbe311cc880d95dc8.se:2137/does/not/matter" - @properties %{} - @jellyfish_opts %{engine_pid: @engine_pid, room_id: "example-room-id"} - - test "sourceUri, default opts" do - options = Map.put(@jellyfish_opts, "sourceUri", @source_uri) - - expected = %Endpoint.RTSP{ - rtc_engine: @engine_pid, - source_uri: @source_uri, - max_reconnect_attempts: :infinity - } - - {:ok, %{endpoint: ^expected, properties: @properties}} = Component.RTSP.config(options) - end - - test "sourceUri, custom opts" do - custom_opts = %{ - "sourceUri" => @source_uri, - "rtpPort" => 34_567, - "reconnectDelay" => 500, - "keepAliveInterval" => 20_000, - "pierceNat" => false - } - - options = Map.merge(@jellyfish_opts, custom_opts) - - expected = - Map.new(custom_opts, fn {k, v} -> {Macro.underscore(k) |> String.to_atom(), v} end) - |> Map.put(:rtc_engine, @engine_pid) - |> Map.put(:max_reconnect_attempts, :infinity) - |> then(&struct(Endpoint.RTSP, &1)) - - {:ok, %{endpoint: ^expected, properties: @properties}} = Component.RTSP.config(options) - end - - test "missing required sourceUri" do - {:error, {:missing_parameter, :sourceUri}} = Component.RTSP.config(@jellyfish_opts) - end -end diff --git a/test/jellyfish_web/controllers/component/file_component_test.exs b/test/jellyfish_web/controllers/component/file_component_test.exs index c9070736..7e9862d7 100644 --- a/test/jellyfish_web/controllers/component/file_component_test.exs +++ b/test/jellyfish_web/controllers/component/file_component_test.exs @@ -3,9 +3,10 @@ defmodule JellyfishWeb.Component.FileComponentTest do use JellyfishWeb.ComponentCase @file_component_directory "file_component_sources" - @file_component_source "video.h264" + @video_source "video.h264" + @audio_source "audio.ogg" - setup _tags do + setup_all _tags do media_sources_directory = Application.fetch_env!(:jellyfish, :media_files_path) |> Path.join(@file_component_directory) @@ -14,18 +15,71 @@ defmodule JellyfishWeb.Component.FileComponentTest do File.mkdir_p!(media_sources_directory) media_sources_directory - |> Path.join(@file_component_source) + |> Path.join(@video_source) |> File.touch!() - {:ok, %{}} + media_sources_directory + |> Path.join(@audio_source) + |> File.touch!() + + on_exit(fn -> :file.del_dir_r(media_sources_directory) end) + + {:ok, %{media_sources_directory: media_sources_directory}} end describe "Create File Component" do - test "renders component with required options", %{conn: conn, room_id: room_id} do + test "renders component with video as source", %{conn: conn, room_id: room_id} do + conn = + post(conn, ~p"/room/#{room_id}/component", + type: "file", + options: %{filePath: @video_source} + ) + + assert %{ + "data" => %{ + "id" => id, + "type" => "file", + "properties" => %{} + } + } = + model_response(conn, :created, "ComponentDetailsResponse") + + assert_component_created(conn, room_id, id, "file") + end + + test "renders component wiht audio as source", %{conn: conn, room_id: room_id} do + conn = + post(conn, ~p"/room/#{room_id}/component", + type: "file", + options: %{filePath: @audio_source} + ) + + assert %{ + "data" => %{ + "id" => id, + "type" => "file", + "properties" => %{} + } + } = + model_response(conn, :created, "ComponentDetailsResponse") + + assert_component_created(conn, room_id, id, "file") + end + + test "file in subdirectory", %{ + conn: conn, + room_id: room_id, + media_sources_directory: media_sources_directory + } do + subdir_name = "subdirectory" + video_relative_path = Path.join(subdir_name, @video_source) + [media_sources_directory, subdir_name] |> Path.join() |> File.mkdir_p!() + media_sources_directory |> Path.join(video_relative_path) |> File.touch!() + conn = post(conn, ~p"/room/#{room_id}/component", type: "file", - options: %{filePath: @file_component_source} + options: %{filePath: video_relative_path} ) assert %{ @@ -63,5 +117,48 @@ defmodule JellyfishWeb.Component.FileComponentTest do assert model_response(conn, :not_found, "Error")["errors"] == "File not found" end + + test "renders error when file path is outside of media files directory", %{ + conn: conn, + room_id: room_id + } do + filepath = "../restricted_audio.opus" + + conn = + post(conn, ~p"/room/#{room_id}/component", type: "file", options: %{filePath: filepath}) + + assert model_response(conn, :bad_request, "Error")["errors"] == + "Invalid file path" + end + + test "renders error when file has no extension", %{ + conn: conn, + room_id: room_id, + media_sources_directory: media_sources_directory + } do + filepath = "h264" + media_sources_directory |> Path.join(filepath) |> File.touch!() + + conn = + post(conn, ~p"/room/#{room_id}/component", type: "file", options: %{filePath: filepath}) + + assert model_response(conn, :bad_request, "Error")["errors"] == + "Unsupported file type" + end + + test "renders error when file has invalid extension", %{ + conn: conn, + room_id: room_id, + media_sources_directory: media_sources_directory + } do + filepath = "sounds.aac" + media_sources_directory |> Path.join(filepath) |> File.touch!() + + conn = + post(conn, ~p"/room/#{room_id}/component", type: "file", options: %{filePath: filepath}) + + assert model_response(conn, :bad_request, "Error")["errors"] == + "Unsupported file type" + end end end