Hook up user script event to media download
This commit is contained in:
parent
3203e246d8
commit
7a61ac722b
25 changed files with 290 additions and 55 deletions
|
|
@ -8,6 +8,8 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilder do
|
||||||
alias Pinchflat.Media.MediaItem
|
alias Pinchflat.Media.MediaItem
|
||||||
alias Pinchflat.Downloading.OutputPathBuilder
|
alias Pinchflat.Downloading.OutputPathBuilder
|
||||||
|
|
||||||
|
alias Pinchflat.Utils.FilesystemUtils, as: FSUtils
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Builds the options for yt-dlp to download media based on the given media's profile.
|
Builds the options for yt-dlp to download media based on the given media's profile.
|
||||||
|
|
||||||
|
|
@ -154,12 +156,10 @@ defmodule Pinchflat.Downloading.DownloadOptionBuilder do
|
||||||
Enum.reduce(filenames, [], fn filename, acc ->
|
Enum.reduce(filenames, [], fn filename, acc ->
|
||||||
filepath = Path.join(base_dir, filename)
|
filepath = Path.join(base_dir, filename)
|
||||||
|
|
||||||
case File.read(filepath) do
|
if FSUtils.exists_and_nonempty?(filepath) do
|
||||||
{:ok, file_data} ->
|
[filepath | acc]
|
||||||
if String.trim(file_data) != "", do: [filepath | acc], else: acc
|
else
|
||||||
|
acc
|
||||||
{:error, _} ->
|
|
||||||
acc
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Downloading.MediaDownloader
|
alias Pinchflat.Downloading.MediaDownloader
|
||||||
|
|
||||||
|
alias Pinchflat.Lifecycle.UserScripts.CommandRunner, as: UserScriptRunner
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Starts the media_item media download worker and creates a task for the media_item.
|
Starts the media_item media download worker and creates a task for the media_item.
|
||||||
|
|
||||||
|
|
@ -56,11 +58,14 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
|
|
||||||
defp download_media_and_schedule_jobs(media_item, is_redownload) do
|
defp download_media_and_schedule_jobs(media_item, is_redownload) do
|
||||||
case MediaDownloader.download_for_media_item(media_item) do
|
case MediaDownloader.download_for_media_item(media_item) do
|
||||||
{:ok, updated_media_item} ->
|
{:ok, downloaded_media_item} ->
|
||||||
Media.update_media_item(updated_media_item, %{
|
{:ok, updated_media_item} =
|
||||||
media_size_bytes: compute_media_filesize(updated_media_item),
|
Media.update_media_item(downloaded_media_item, %{
|
||||||
media_redownloaded_at: get_redownloaded_at(is_redownload)
|
media_size_bytes: compute_media_filesize(downloaded_media_item),
|
||||||
})
|
media_redownloaded_at: get_redownloaded_at(is_redownload)
|
||||||
|
})
|
||||||
|
|
||||||
|
:ok = run_user_script(updated_media_item)
|
||||||
|
|
||||||
{:ok, updated_media_item}
|
{:ok, updated_media_item}
|
||||||
|
|
||||||
|
|
@ -74,21 +79,13 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
|
|
||||||
defp compute_media_filesize(media_item) do
|
defp compute_media_filesize(media_item) do
|
||||||
case File.stat(media_item.media_filepath) do
|
case File.stat(media_item.media_filepath) do
|
||||||
{:ok, %{size: size}} ->
|
{:ok, %{size: size}} -> size
|
||||||
size
|
_ -> nil
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_redownloaded_at(is_redownload) do
|
defp get_redownloaded_at(true), do: DateTime.utc_now()
|
||||||
if is_redownload do
|
defp get_redownloaded_at(_), do: nil
|
||||||
DateTime.utc_now()
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp action_on_error(message) do
|
defp action_on_error(message) do
|
||||||
# This will attempt re-download at the next indexing, but it won't be retried
|
# This will attempt re-download at the next indexing, but it won't be retried
|
||||||
|
|
@ -103,4 +100,12 @@ defmodule Pinchflat.Downloading.MediaDownloadWorker do
|
||||||
{:error, :download_failed}
|
{:error, :download_failed}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# NOTE: I like this pattern of using the default value so that I don't have to
|
||||||
|
# define it in config.exs (and friends). Consider using this elsewhere.
|
||||||
|
defp run_user_script(media_item) do
|
||||||
|
runner = Application.get_env(:pinchflat, :user_script_runner, UserScriptRunner)
|
||||||
|
|
||||||
|
runner.run(:media_downloaded, media_item)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ defmodule Pinchflat.Lifecycle.Notifications.CommandRunner do
|
||||||
default_opts = [:verbose]
|
default_opts = [:verbose]
|
||||||
parsed_opts = CliUtils.parse_options(default_opts ++ command_opts)
|
parsed_opts = CliUtils.parse_options(default_opts ++ command_opts)
|
||||||
|
|
||||||
{output, return_code} = CliUtils.wrap_cmd(backend_executable(), parsed_opts ++ endpoints)
|
{output, exit_code} = CliUtils.wrap_cmd(backend_executable(), parsed_opts ++ endpoints)
|
||||||
Logger.info("[apprise] response: #{output}")
|
Logger.info("[apprise] response: #{output}")
|
||||||
|
|
||||||
case return_code do
|
case exit_code do
|
||||||
0 -> {:ok, String.trim(output)}
|
0 -> {:ok, String.trim(output)}
|
||||||
_ -> {:error, String.trim(output)}
|
_ -> {:error, String.trim(output)}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
76
lib/pinchflat/lifecycle/user_scripts/command_runner.ex
Normal file
76
lib/pinchflat/lifecycle/user_scripts/command_runner.ex
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
defmodule Pinchflat.Lifecycle.UserScripts.CommandRunner do
|
||||||
|
@moduledoc """
|
||||||
|
Runs custom user commands commands using the `System.cmd/3` function
|
||||||
|
"""
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias Pinchflat.Utils.CliUtils
|
||||||
|
alias Pinchflat.Utils.FilesystemUtils
|
||||||
|
alias Pinchflat.Lifecycle.UserScripts.UserScriptCommandRunner
|
||||||
|
|
||||||
|
@behaviour UserScriptCommandRunner
|
||||||
|
|
||||||
|
@event_types [
|
||||||
|
:media_downloaded,
|
||||||
|
:media_deleted
|
||||||
|
]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Runs the user script command for the given event type. Passes the event
|
||||||
|
and the encoded data to the user script command.
|
||||||
|
|
||||||
|
This function will succeed in almost all cases, even if the user script command
|
||||||
|
failed - this is because I don't want bad scripts to stop the whole process.
|
||||||
|
If something fails, it'll be logged.
|
||||||
|
|
||||||
|
The only things that can cause a true failure are passing in an invalid event
|
||||||
|
type or if the passed data cannot be encoded into JSON - both indicative of
|
||||||
|
failures in the development process.
|
||||||
|
|
||||||
|
Returns :ok
|
||||||
|
"""
|
||||||
|
@impl UserScriptCommandRunner
|
||||||
|
def run(event_type, encodable_data) when event_type in @event_types do
|
||||||
|
case backend_executable() do
|
||||||
|
{:ok, :no_executable} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:ok, executable_path} ->
|
||||||
|
{:ok, encoded_data} = Phoenix.json_library().encode(encodable_data)
|
||||||
|
|
||||||
|
{output, exit_code} =
|
||||||
|
CliUtils.wrap_cmd(
|
||||||
|
executable_path,
|
||||||
|
[to_string(event_type), encoded_data],
|
||||||
|
[],
|
||||||
|
logging_arg_override: "[supressed]"
|
||||||
|
)
|
||||||
|
|
||||||
|
handle_output(output, exit_code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(event_type, _encodable_data) do
|
||||||
|
raise ArgumentError, "Invalid event type: #{inspect(event_type)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_output(output, exit_code) do
|
||||||
|
Logger.debug("Custom lifecycle script had an exit code of #{exit_code} output: #{output}")
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp backend_executable do
|
||||||
|
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
||||||
|
filepath = Path.join([base_dir, "user-scripts", "lifecycle"])
|
||||||
|
|
||||||
|
if FilesystemUtils.exists_and_nonempty?(filepath) do
|
||||||
|
{:ok, filepath}
|
||||||
|
else
|
||||||
|
Logger.warning("User scripts lifecyle file either not present or is empty. Skipping.")
|
||||||
|
|
||||||
|
{:ok, :no_executable}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule Pinchflat.Lifecycle.UserScripts.UserScriptCommandRunner do
|
||||||
|
@moduledoc """
|
||||||
|
A behaviour for running custom user scripts on certain events.
|
||||||
|
|
||||||
|
Used so we can implement Mox for testing without actually running the
|
||||||
|
user's command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@callback run(atom(), map()) :: :ok | {:error, binary()}
|
||||||
|
end
|
||||||
|
|
@ -13,17 +13,22 @@ defmodule Pinchflat.Utils.CliUtils do
|
||||||
commands if the job runner is cancelled.
|
commands if the job runner is cancelled.
|
||||||
|
|
||||||
Delegates to `System.cmd/3` and any options/output
|
Delegates to `System.cmd/3` and any options/output
|
||||||
are passed through.
|
are passed through. Custom options can be passed in.
|
||||||
|
|
||||||
|
Custom options:
|
||||||
|
- logging_arg_override: if set, the passed value will be logged in place of
|
||||||
|
the actual arguments passed to the command
|
||||||
|
|
||||||
Returns {binary(), integer()}
|
Returns {binary(), integer()}
|
||||||
"""
|
"""
|
||||||
def wrap_cmd(command, args, opts \\ []) do
|
def wrap_cmd(command, args, passthrough_opts \\ [], opts \\ []) do
|
||||||
wrapper_command = Path.join(:code.priv_dir(:pinchflat), "cmd_wrapper.sh")
|
wrapper_command = Path.join(:code.priv_dir(:pinchflat), "cmd_wrapper.sh")
|
||||||
actual_command = [command] ++ args
|
actual_command = [command] ++ args
|
||||||
|
logging_arg_override = Keyword.get(opts, :logging_arg_override, Enum.join(args, " "))
|
||||||
|
|
||||||
Logger.info("[command_wrapper]: #{command} called with: #{Enum.join(args, " ")}")
|
Logger.info("[command_wrapper]: #{command} called with: #{logging_arg_override}")
|
||||||
|
|
||||||
System.cmd(wrapper_command, actual_command, opts)
|
System.cmd(wrapper_command, actual_command, passthrough_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,21 @@ defmodule Pinchflat.Utils.FilesystemUtils do
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Utils.StringUtils
|
alias Pinchflat.Utils.StringUtils
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if a file exists and has non-whitespace contents.
|
||||||
|
|
||||||
|
Returns boolean()
|
||||||
|
"""
|
||||||
|
def exists_and_nonempty?(filepath) do
|
||||||
|
case File.read(filepath) do
|
||||||
|
{:ok, contents} ->
|
||||||
|
String.trim(contents) != ""
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Generates a temporary file and returns its path. The file is empty and has the given type.
|
Generates a temporary file and returns its path. The file is empty and has the given type.
|
||||||
Generates all the directories in the path if they don't exist.
|
Generates all the directories in the path if they don't exist.
|
||||||
|
|
|
||||||
|
|
@ -81,16 +81,10 @@ defmodule Pinchflat.YtDlp.CommandRunner do
|
||||||
Enum.reduce(filename_options_map, [], fn {opt_name, filename}, acc ->
|
Enum.reduce(filename_options_map, [], fn {opt_name, filename}, acc ->
|
||||||
filepath = Path.join(base_dir, filename)
|
filepath = Path.join(base_dir, filename)
|
||||||
|
|
||||||
case File.read(filepath) do
|
if FSUtils.exists_and_nonempty?(filepath) do
|
||||||
{:ok, file_data} ->
|
[{opt_name, filepath} | acc]
|
||||||
if String.trim(file_data) != "" do
|
else
|
||||||
[{opt_name, filepath} | acc]
|
acc
|
||||||
else
|
|
||||||
acc
|
|
||||||
end
|
|
||||||
|
|
||||||
{:error, _} ->
|
|
||||||
acc
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,8 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
||||||
setup :verify_on_exit!
|
setup :verify_on_exit!
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
stub(HTTPClientMock, :get, fn _url, _headers, _opts ->
|
stub(UserScriptRunnerMock, :run, fn _event_type, _data -> :ok end)
|
||||||
{:ok, ""}
|
stub(HTTPClientMock, :get, fn _url, _headers, _opts -> {:ok, ""} end)
|
||||||
end)
|
|
||||||
|
|
||||||
media_item =
|
media_item =
|
||||||
%{media_filepath: nil}
|
%{media_filepath: nil}
|
||||||
|
|
@ -185,6 +184,20 @@ defmodule Pinchflat.Downloading.MediaDownloadWorkerTest do
|
||||||
assert media_item.media_redownloaded_at == nil
|
assert media_item.media_redownloaded_at == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "calls the user script runner", %{media_item: media_item} do
|
||||||
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot, _addl ->
|
||||||
|
{:ok, render_metadata(:media_metadata)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
expect(UserScriptRunnerMock, :run, fn :media_downloaded, data ->
|
||||||
|
assert data.id == media_item.id
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end)
|
||||||
|
|
||||||
|
perform_job(MediaDownloadWorker, %{id: media_item.id})
|
||||||
|
end
|
||||||
|
|
||||||
test "does not blow up if the record doesn't exist" do
|
test "does not blow up if the record doesn't exist" do
|
||||||
assert :ok = perform_job(MediaDownloadWorker, %{id: 0})
|
assert :ok = perform_job(MediaDownloadWorker, %{id: 0})
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Downloading.OutputPath.ParserTest do
|
defmodule Pinchflat.Downloading.OutputPath.ParserTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Downloading.OutputPath.Parser
|
alias Pinchflat.Downloading.OutputPath.Parser
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Lifecycle.Notifications.CommandRunnerTest do
|
defmodule Pinchflat.Lifecycle.Notifications.CommandRunnerTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Lifecycle.Notifications.CommandRunner, as: Runner
|
alias Pinchflat.Lifecycle.Notifications.CommandRunner, as: Runner
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
defmodule Pinchflat.Lifecycle.UserScripts.CommandRunnerTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
alias Pinchflat.Utils.FilesystemUtils
|
||||||
|
alias Pinchflat.Lifecycle.UserScripts.CommandRunner, as: Runner
|
||||||
|
|
||||||
|
setup do
|
||||||
|
FilesystemUtils.write_p!(filepath(), "")
|
||||||
|
File.chmod(filepath(), 0o755)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "run/2" do
|
||||||
|
test "runs the provided lifecycle file if present" do
|
||||||
|
# We *love* indirectly testing side effects
|
||||||
|
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||||
|
File.write(filepath(), "#!/bin/bash\ntouch #{tmp_dir}/test_file\n")
|
||||||
|
|
||||||
|
refute File.exists?("#{tmp_dir}/test_file")
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{})
|
||||||
|
assert File.exists?("#{tmp_dir}/test_file")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passes the event name to the script" do
|
||||||
|
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||||
|
File.write(filepath(), "#!/bin/bash\necho $1 > #{tmp_dir}/event_name\n")
|
||||||
|
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{})
|
||||||
|
assert File.read!("#{tmp_dir}/event_name") == "media_downloaded\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passes the encoded data to the script" do
|
||||||
|
tmp_dir = Application.get_env(:pinchflat, :tmpfile_directory)
|
||||||
|
File.write(filepath(), "#!/bin/bash\necho $2 > #{tmp_dir}/encoded_data\n")
|
||||||
|
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{foo: "bar"})
|
||||||
|
assert File.read!("#{tmp_dir}/encoded_data") == "{\"foo\":\"bar\"}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does nothing if the lifecycle file is not present" do
|
||||||
|
:ok = File.rm(filepath())
|
||||||
|
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does nothing if the lifecycle file is empty" do
|
||||||
|
File.write(filepath(), "")
|
||||||
|
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns :ok if the command exits with a non-zero status" do
|
||||||
|
File.write(filepath(), "#!/bin/bash\nexit 1\n")
|
||||||
|
|
||||||
|
assert :ok = Runner.run(:media_downloaded, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gets upset if you pass an invalid event type" do
|
||||||
|
assert_raise ArgumentError, "Invalid event type: :invalid_event", fn ->
|
||||||
|
Runner.run(:invalid_event, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "gets upset if the record cannot be decoded" do
|
||||||
|
File.write(filepath(), "#!/bin/bash")
|
||||||
|
|
||||||
|
assert_raise MatchError, fn ->
|
||||||
|
Runner.run(:media_downloaded, %Ecto.Changeset{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filepath do
|
||||||
|
base_dir = Application.get_env(:pinchflat, :extras_directory)
|
||||||
|
|
||||||
|
Path.join([base_dir, "user-scripts", "lifecycle"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.SlowIndexing.FileFollowerServerTest do
|
defmodule Pinchflat.SlowIndexing.FileFollowerServerTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias alias Pinchflat.Utils.FilesystemUtils
|
alias alias Pinchflat.Utils.FilesystemUtils
|
||||||
alias Pinchflat.SlowIndexing.FileFollowerServer
|
alias Pinchflat.SlowIndexing.FileFollowerServer
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.ChangesetUtilsTest do
|
defmodule Pinchflat.Utils.ChangesetUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
defmodule MockSchema do
|
defmodule MockSchema do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.CliUtilsTest do
|
defmodule Pinchflat.Utils.CliUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.CliUtils
|
alias Pinchflat.Utils.CliUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.DatetimeUtilsTest do
|
defmodule Pinchflat.Utils.DatetimeUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.DatetimeUtils
|
alias Pinchflat.Utils.DatetimeUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,38 @@ defmodule Pinchflat.Utils.FilesystemUtilsTest do
|
||||||
|
|
||||||
alias Pinchflat.Utils.FilesystemUtils
|
alias Pinchflat.Utils.FilesystemUtils
|
||||||
|
|
||||||
|
describe "exists_and_nonempty?" do
|
||||||
|
test "returns true if a file exists and has contents" do
|
||||||
|
filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||||
|
File.write(filepath, "{}")
|
||||||
|
|
||||||
|
assert FilesystemUtils.exists_and_nonempty?(filepath)
|
||||||
|
|
||||||
|
File.rm!(filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns false if a file doesn't exist" do
|
||||||
|
refute FilesystemUtils.exists_and_nonempty?("/nonexistent/file.json")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns false if a file exists but is empty" do
|
||||||
|
filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||||
|
|
||||||
|
refute FilesystemUtils.exists_and_nonempty?(filepath)
|
||||||
|
|
||||||
|
File.rm!(filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "trims the contents before checking" do
|
||||||
|
filepath = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||||
|
File.write(filepath, " \n\n \r\n ")
|
||||||
|
|
||||||
|
refute FilesystemUtils.exists_and_nonempty?(filepath)
|
||||||
|
|
||||||
|
File.rm!(filepath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "generate_metadata_tmpfile/1" do
|
describe "generate_metadata_tmpfile/1" do
|
||||||
test "creates a tmpfile and returns its path" do
|
test "creates a tmpfile and returns its path" do
|
||||||
res = FilesystemUtils.generate_metadata_tmpfile(:json)
|
res = FilesystemUtils.generate_metadata_tmpfile(:json)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.FunctionUtilsTest do
|
defmodule Pinchflat.Utils.FunctionUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.FunctionUtils
|
alias Pinchflat.Utils.FunctionUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.NumberUtilsTest do
|
defmodule Pinchflat.Utils.NumberUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.NumberUtils
|
alias Pinchflat.Utils.NumberUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.StringUtilsTest do
|
defmodule Pinchflat.Utils.StringUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.StringUtils
|
alias Pinchflat.Utils.StringUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.Utils.XmlUtilsTest do
|
defmodule Pinchflat.Utils.XmlUtilsTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.XmlUtils
|
alias Pinchflat.Utils.XmlUtils
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
||||||
use ExUnit.Case, async: true
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
alias Pinchflat.Utils.FilesystemUtils
|
alias Pinchflat.Utils.FilesystemUtils
|
||||||
|
|
||||||
|
|
@ -75,6 +75,9 @@ defmodule Pinchflat.YtDlp.CommandRunnerTest do
|
||||||
|
|
||||||
refute String.contains?(output, "--cookies")
|
refute String.contains?(output, "--cookies")
|
||||||
refute String.contains?(output, cookie_file)
|
refute String.contains?(output, cookie_file)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
FilesystemUtils.write_p!(cookie_file, "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule PinchflatWeb.ErrorHTMLTest do
|
defmodule PinchflatWeb.ErrorHTMLTest do
|
||||||
use PinchflatWeb.ConnCase, async: true
|
use PinchflatWeb.ConnCase, async: false
|
||||||
|
|
||||||
# Bring render_to_string/4 for testing custom views
|
# Bring render_to_string/4 for testing custom views
|
||||||
import Phoenix.Template
|
import Phoenix.Template
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
defmodule PinchflatWeb.ErrorJSONTest do
|
defmodule PinchflatWeb.ErrorJSONTest do
|
||||||
use PinchflatWeb.ConnCase, async: true
|
use PinchflatWeb.ConnCase, async: false
|
||||||
|
|
||||||
test "renders 404" do
|
test "renders 404" do
|
||||||
assert PinchflatWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
|
assert PinchflatWeb.ErrorJSON.render("404.json", %{}) == %{errors: %{detail: "Not Found"}}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ Application.put_env(:pinchflat, :apprise_runner, AppriseRunnerMock)
|
||||||
Mox.defmock(HTTPClientMock, for: Pinchflat.HTTP.HTTPBehaviour)
|
Mox.defmock(HTTPClientMock, for: Pinchflat.HTTP.HTTPBehaviour)
|
||||||
Application.put_env(:pinchflat, :http_client, HTTPClientMock)
|
Application.put_env(:pinchflat, :http_client, HTTPClientMock)
|
||||||
|
|
||||||
|
Mox.defmock(UserScriptRunnerMock, for: Pinchflat.Lifecycle.UserScripts.UserScriptCommandRunner)
|
||||||
|
Application.put_env(:pinchflat, :user_script_runner, UserScriptRunnerMock)
|
||||||
|
|
||||||
ExUnit.start()
|
ExUnit.start()
|
||||||
Ecto.Adapters.SQL.Sandbox.mode(Pinchflat.Repo, :manual)
|
Ecto.Adapters.SQL.Sandbox.mode(Pinchflat.Repo, :manual)
|
||||||
Faker.start()
|
Faker.start()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue