2021-04-05 00:55:51 +08:00
|
|
|
defmodule LivebookWeb.SessionControllerTest do
|
|
|
|
use LivebookWeb.ConnCase, async: true
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
alias Livebook.{Sessions, Session, Notebook, FileSystem}
|
2021-04-05 00:55:51 +08:00
|
|
|
|
|
|
|
describe "show_image" do
|
|
|
|
test "returns not found when the given session does not exist", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
id = Livebook.Utils.random_node_aware_id()
|
|
|
|
conn = get(conn, Routes.session_path(conn, :show_image, id, "image.jpg"))
|
2021-04-05 00:55:51 +08:00
|
|
|
|
|
|
|
assert conn.status == 404
|
|
|
|
assert conn.resp_body == "Not found"
|
|
|
|
end
|
|
|
|
|
|
|
|
test "returns not found when the given image does not exist", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session()
|
2021-04-05 00:55:51 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :show_image, session.id, "nonexistent.jpg"))
|
2021-04-05 00:55:51 +08:00
|
|
|
|
|
|
|
assert conn.status == 404
|
2021-08-14 03:17:43 +08:00
|
|
|
assert conn.resp_body == "No such file or directory"
|
2021-04-05 00:55:51 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
Session.close(session.pid)
|
2021-04-05 00:55:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
test "returns the image when it does exist", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session()
|
|
|
|
%{images_dir: images_dir} = session
|
2021-08-14 03:17:43 +08:00
|
|
|
:ok = FileSystem.File.resolve(images_dir, "test.jpg") |> FileSystem.File.write("")
|
2021-04-05 00:55:51 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :show_image, session.id, "test.jpg"))
|
2021-04-05 00:55:51 +08:00
|
|
|
|
|
|
|
assert conn.status == 200
|
|
|
|
assert get_resp_header(conn, "content-type") == ["image/jpeg"]
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
Session.close(session.pid)
|
2021-04-05 00:55:51 +08:00
|
|
|
end
|
|
|
|
end
|
2021-07-23 07:18:40 +08:00
|
|
|
|
|
|
|
describe "download_source" do
|
|
|
|
test "returns not found when the given session does not exist", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
id = Livebook.Utils.random_node_aware_id()
|
|
|
|
conn = get(conn, Routes.session_path(conn, :download_source, id, "livemd"))
|
2021-07-23 07:18:40 +08:00
|
|
|
|
|
|
|
assert conn.status == 404
|
|
|
|
assert conn.resp_body == "Not found"
|
|
|
|
end
|
|
|
|
|
2021-07-28 19:40:36 +08:00
|
|
|
test "returns bad request when given an invalid format", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session()
|
2021-07-23 07:18:40 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :download_source, session.id, "invalid"))
|
2021-07-28 19:40:36 +08:00
|
|
|
|
|
|
|
assert conn.status == 400
|
|
|
|
assert conn.resp_body == "Invalid format, supported formats: livemd, exs"
|
|
|
|
end
|
|
|
|
|
|
|
|
test "handles live markdown notebook source", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session()
|
2021-07-28 19:40:36 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :download_source, session.id, "livemd"))
|
2021-07-23 07:18:40 +08:00
|
|
|
|
|
|
|
assert conn.status == 200
|
|
|
|
assert get_resp_header(conn, "content-type") == ["text/plain"]
|
|
|
|
|
|
|
|
assert conn.resp_body == """
|
|
|
|
# Untitled notebook
|
2021-08-30 22:33:40 +08:00
|
|
|
|
|
|
|
## Section
|
|
|
|
|
|
|
|
```elixir
|
|
|
|
|
|
|
|
```
|
2021-07-23 07:18:40 +08:00
|
|
|
"""
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
Session.close(session.pid)
|
2021-07-23 07:18:40 +08:00
|
|
|
end
|
2021-07-28 19:40:36 +08:00
|
|
|
|
2021-07-30 00:41:34 +08:00
|
|
|
test "includes output in markdown when include_outputs parameter is set", %{conn: conn} do
|
|
|
|
notebook = %{
|
|
|
|
Notebook.new()
|
|
|
|
| name: "My Notebook",
|
|
|
|
sections: [
|
|
|
|
%{
|
|
|
|
Notebook.Section.new()
|
|
|
|
| name: "Section 1",
|
|
|
|
cells: [
|
|
|
|
%{
|
|
|
|
Notebook.Cell.new(:elixir)
|
|
|
|
| source: """
|
|
|
|
IO.puts("hey")\
|
|
|
|
""",
|
2022-01-17 03:37:00 +08:00
|
|
|
outputs: [{0, {:stdout, "hey"}}]
|
2021-07-30 00:41:34 +08:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session(notebook: notebook)
|
2021-07-30 00:41:34 +08:00
|
|
|
|
|
|
|
query = [include_outputs: "true"]
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :download_source, session.id, "livemd", query))
|
2021-07-30 00:41:34 +08:00
|
|
|
|
|
|
|
assert conn.status == 200
|
|
|
|
assert get_resp_header(conn, "content-type") == ["text/plain"]
|
|
|
|
|
|
|
|
assert conn.resp_body == """
|
|
|
|
# My Notebook
|
|
|
|
|
|
|
|
## Section 1
|
|
|
|
|
|
|
|
```elixir
|
|
|
|
IO.puts("hey")
|
|
|
|
```
|
|
|
|
|
|
|
|
```output
|
|
|
|
hey
|
|
|
|
```
|
|
|
|
"""
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
Session.close(session.pid)
|
2021-07-30 00:41:34 +08:00
|
|
|
end
|
|
|
|
|
2021-07-28 19:40:36 +08:00
|
|
|
test "handles elixir notebook source", %{conn: conn} do
|
2021-09-05 01:16:01 +08:00
|
|
|
{:ok, session} = Sessions.create_session()
|
2021-07-28 19:40:36 +08:00
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
conn = get(conn, Routes.session_path(conn, :download_source, session.id, "exs"))
|
2021-07-28 19:40:36 +08:00
|
|
|
|
|
|
|
assert conn.status == 200
|
|
|
|
assert get_resp_header(conn, "content-type") == ["text/plain"]
|
|
|
|
|
|
|
|
assert conn.resp_body == """
|
|
|
|
# Title: Untitled notebook
|
2021-08-30 22:33:40 +08:00
|
|
|
|
|
|
|
# ── Section ──
|
2021-07-28 19:40:36 +08:00
|
|
|
"""
|
|
|
|
|
2021-09-05 01:16:01 +08:00
|
|
|
Session.close(session.pid)
|
2021-07-28 19:40:36 +08:00
|
|
|
end
|
2021-07-23 07:18:40 +08:00
|
|
|
end
|
2021-12-24 21:18:34 +08:00
|
|
|
|
|
|
|
describe "show_asset" do
|
|
|
|
test "fetches assets and redirects to the session-less path", %{conn: conn} do
|
|
|
|
%{notebook: notebook, hash: hash} = notebook_with_js_output()
|
|
|
|
|
|
|
|
conn = start_session_and_request_asset(conn, notebook, hash)
|
|
|
|
|
|
|
|
assert redirected_to(conn, 301) ==
|
|
|
|
Routes.session_path(conn, :show_cached_asset, hash, ["main.js"])
|
|
|
|
end
|
|
|
|
|
|
|
|
test "skips the session if assets are in cache", %{conn: conn} do
|
|
|
|
%{notebook: notebook, hash: hash} = notebook_with_js_output()
|
|
|
|
# Fetch the assets for the first time
|
|
|
|
conn = start_session_and_request_asset(conn, notebook, hash)
|
|
|
|
|
|
|
|
# Use nonexistent session, so any communication would fail
|
|
|
|
random_session_id = Livebook.Utils.random_node_aware_id()
|
|
|
|
|
|
|
|
conn =
|
|
|
|
get(conn, Routes.session_path(conn, :show_asset, random_session_id, hash, ["main.js"]))
|
|
|
|
|
|
|
|
assert redirected_to(conn, 301) ==
|
|
|
|
Routes.session_path(conn, :show_cached_asset, hash, ["main.js"])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "show_cached_asset" do
|
|
|
|
test "returns not found when no matching assets are in the cache", %{conn: conn} do
|
|
|
|
%{notebook: _notebook, hash: hash} = notebook_with_js_output()
|
|
|
|
|
|
|
|
conn = get(conn, Routes.session_path(conn, :show_cached_asset, hash, ["main.js"]))
|
|
|
|
|
|
|
|
assert conn.status == 404
|
|
|
|
assert conn.resp_body == "Not found"
|
|
|
|
end
|
|
|
|
|
|
|
|
test "returns the requestes asset if available in cache", %{conn: conn} do
|
|
|
|
%{notebook: notebook, hash: hash} = notebook_with_js_output()
|
|
|
|
# Fetch the assets for the first time
|
|
|
|
conn = start_session_and_request_asset(conn, notebook, hash)
|
|
|
|
|
|
|
|
conn = get(conn, Routes.session_path(conn, :show_cached_asset, hash, ["main.js"]))
|
|
|
|
|
|
|
|
assert conn.status == 200
|
|
|
|
assert "export function init(" <> _ = conn.resp_body
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp start_session_and_request_asset(conn, notebook, hash) do
|
|
|
|
{:ok, session} = Sessions.create_session(notebook: notebook)
|
|
|
|
# We need runtime in place to actually copy the archive
|
|
|
|
Session.connect_runtime(session.pid, Livebook.Runtime.NoopRuntime.new())
|
|
|
|
|
|
|
|
conn = get(conn, Routes.session_path(conn, :show_asset, session.id, hash, ["main.js"]))
|
|
|
|
|
|
|
|
Session.close(session.pid)
|
|
|
|
|
|
|
|
conn
|
|
|
|
end
|
|
|
|
|
|
|
|
defp notebook_with_js_output() do
|
|
|
|
archive_path = Path.expand("../../support/assets.tar.gz", __DIR__)
|
|
|
|
hash = "test-" <> Livebook.Utils.random_id()
|
|
|
|
assets_info = %{archive_path: archive_path, hash: hash, js_path: "main.js"}
|
2022-01-06 23:31:26 +08:00
|
|
|
output = {:js, %{assets: assets_info}}
|
2021-12-24 21:18:34 +08:00
|
|
|
|
|
|
|
notebook = %{
|
|
|
|
Notebook.new()
|
|
|
|
| sections: [
|
2022-01-17 03:37:00 +08:00
|
|
|
%{
|
|
|
|
Notebook.Section.new()
|
|
|
|
| cells: [%{Notebook.Cell.new(:elixir) | outputs: [{0, output}]}]
|
|
|
|
}
|
2021-12-24 21:18:34 +08:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
%{notebook: notebook, hash: hash}
|
|
|
|
end
|
2021-04-05 00:55:51 +08:00
|
|
|
end
|