mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-10 15:04:25 +08:00
Add tests
This commit is contained in:
parent
da4b367ccb
commit
0d62b507b3
3 changed files with 572 additions and 18 deletions
|
@ -4,28 +4,165 @@ defmodule LivebookWeb.Integration.AdminLiveTest do
|
|||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
setup %{teams_auth: teams_auth} do
|
||||
Application.put_env(:livebook, :teams_auth, teams_auth)
|
||||
on_exit(fn -> Application.delete_env(:livebook, :teams_auth) end)
|
||||
describe "topbar" do
|
||||
setup %{teams_auth: teams_auth} do
|
||||
Application.put_env(:livebook, :teams_auth, teams_auth)
|
||||
on_exit(fn -> Application.delete_env(:livebook, :teams_auth) end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
for page <- ["/", "/settings", "/learn", "/hub", "/apps-dashboard"] do
|
||||
@tag page: page, teams_auth: :online
|
||||
test "GET #{page} shows the app server instance topbar warning", %{conn: conn, page: page} do
|
||||
{:ok, view, _} = live(conn, page)
|
||||
|
||||
assert render(view) =~
|
||||
"This Livebook instance has been configured for notebook deployment and is in read-only mode."
|
||||
:ok
|
||||
end
|
||||
|
||||
@tag page: page, teams_auth: :offline
|
||||
test "GET #{page} shows the offline hub topbar warning", %{conn: conn, page: page} do
|
||||
{:ok, view, _} = live(conn, page)
|
||||
for page <- ["/", "/settings", "/learn", "/hub", "/apps-dashboard"] do
|
||||
@tag page: page, teams_auth: :online
|
||||
test "GET #{page} shows the app server instance topbar warning", %{conn: conn, page: page} do
|
||||
{:ok, view, _} = live(conn, page)
|
||||
|
||||
assert render(view) =~
|
||||
"You are running an offline Workspace for deployment. You cannot modify its settings."
|
||||
assert render(view) =~
|
||||
"This Livebook instance has been configured for notebook deployment and is in read-only mode."
|
||||
end
|
||||
|
||||
@tag page: page, teams_auth: :offline
|
||||
test "GET #{page} shows the offline hub topbar warning", %{conn: conn, page: page} do
|
||||
{:ok, view, _} = live(conn, page)
|
||||
|
||||
assert render(view) =~
|
||||
"You are running an offline Workspace for deployment. You cannot modify its settings."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "authorization" do
|
||||
setup %{conn: conn, node: node} do
|
||||
Livebook.Teams.Broadcasts.subscribe([:agents, :app_server])
|
||||
Livebook.Apps.subscribe()
|
||||
|
||||
{_agent_key, org, deployment_group, team} = create_agent_team_hub(node)
|
||||
|
||||
# we wait until the agent_connected is received by livebook
|
||||
hub_id = team.id
|
||||
deployment_group_id = to_string(deployment_group.id)
|
||||
org_id = to_string(org.id)
|
||||
|
||||
assert_receive {:agent_joined,
|
||||
%{
|
||||
hub_id: ^hub_id,
|
||||
org_id: ^org_id,
|
||||
deployment_group_id: ^deployment_group_id
|
||||
}}
|
||||
|
||||
start_supervised!(
|
||||
{Livebook.ZTA.LivebookTeams, name: LivebookWeb.ZTA, identity_key: team.id}
|
||||
)
|
||||
|
||||
{conn, code} = authenticate_user_on_teams(conn, node, team)
|
||||
|
||||
{:ok, conn: conn, code: code, deployment_group: deployment_group, org: org, team: team}
|
||||
end
|
||||
|
||||
test "renders unauthorized admin page if user doesn't have full access",
|
||||
%{conn: conn, node: node, code: code} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :apps,
|
||||
prefixes: ["dev-"],
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
assert conn
|
||||
|> get(~p"/settings")
|
||||
|> html_response(401) =~ "Not authorized"
|
||||
end
|
||||
|
||||
test "shows admin page if user have full access",
|
||||
%{conn: conn, node: node, code: code} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :app_server,
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/settings")
|
||||
assert html =~ "System settings"
|
||||
end
|
||||
|
||||
test "renders unauthorized if loses the access in real-time",
|
||||
%{conn: conn, node: node, code: code} = context do
|
||||
{:ok, deployment_group} =
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :app_server,
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/settings")
|
||||
assert render(view) =~ "System settings"
|
||||
|
||||
erpc_call(node, :update_authorization_group, [
|
||||
authorization_group,
|
||||
%{access_type: :apps, prefixes: ["ops-"]}
|
||||
])
|
||||
|
||||
id = to_string(deployment_group.id)
|
||||
assert_receive {:server_authorization_updated, %{id: ^id}}
|
||||
|
||||
# If you lose access to the app server, we will redirect to "/"
|
||||
assert_redirect view, ~p"/"
|
||||
|
||||
# And it will redirect to "/apps"
|
||||
{:ok, view, _html} = live(conn, ~p"/apps")
|
||||
assert render(view) =~ "No apps running."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
205
test/livebook_teams/web/app_session_live_test.exs
Normal file
205
test/livebook_teams/web/app_session_live_test.exs
Normal file
|
@ -0,0 +1,205 @@
|
|||
defmodule LivebookWeb.Integration.AppSessionLiveTest do
|
||||
use Livebook.TeamsIntegrationCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
describe "authorized apps" do
|
||||
setup %{conn: conn, node: node} do
|
||||
Livebook.Teams.Broadcasts.subscribe([:agents, :app_deployments, :app_server])
|
||||
Livebook.Apps.subscribe()
|
||||
|
||||
{_agent_key, org, deployment_group, team} = create_agent_team_hub(node)
|
||||
|
||||
# we wait until the agent_connected is received by livebook
|
||||
hub_id = team.id
|
||||
deployment_group_id = to_string(deployment_group.id)
|
||||
org_id = to_string(org.id)
|
||||
|
||||
assert_receive {:agent_joined,
|
||||
%{
|
||||
hub_id: ^hub_id,
|
||||
org_id: ^org_id,
|
||||
deployment_group_id: ^deployment_group_id
|
||||
}}
|
||||
|
||||
start_supervised!(
|
||||
{Livebook.ZTA.LivebookTeams, name: LivebookWeb.ZTA, identity_key: team.id}
|
||||
)
|
||||
|
||||
{conn, code} = authenticate_user_on_teams(conn, node, team)
|
||||
|
||||
{:ok, conn: conn, code: code, deployment_group: deployment_group, org: org, team: team}
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "shows app if user doesn't have full access",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :apps,
|
||||
prefixes: ["dev-"],
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slug = "dev-app"
|
||||
pid = deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
session_id = Livebook.App.get_session_id(pid, user: Livebook.Users.User.new())
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/apps/#{slug}/sessions/#{session_id}")
|
||||
assert html =~ "LivebookApp:#{slug}"
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "shows app if user have full access",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :app_server,
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slugs = ~w(mkt-app sales-app opt-app)
|
||||
|
||||
for slug <- slugs do
|
||||
pid = deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
session_id = Livebook.App.get_session_id(pid, user: Livebook.Users.User.new())
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/apps/#{slug}/sessions/#{session_id}")
|
||||
assert html =~ "LivebookApp:#{slug}"
|
||||
end
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "renders unauthorized if loses the access in real-time",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
{:ok, deployment_group} =
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :apps,
|
||||
prefixes: ["mkt-"],
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slug = "mkt-analytics"
|
||||
pid = deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
session_id = Livebook.App.get_session_id(pid, user: Livebook.Users.User.new())
|
||||
path = ~p"/apps/#{slug}/sessions/#{session_id}"
|
||||
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
assert render(view) =~ "LivebookApp:#{slug}"
|
||||
|
||||
erpc_call(node, :update_authorization_group, [authorization_group, %{prefixes: ["ops-"]}])
|
||||
|
||||
id = to_string(deployment_group.id)
|
||||
|
||||
assert_receive {:server_authorization_updated, %{id: ^id}}
|
||||
assert_receive {:app_deployment_updated, %{slug: ^slug}}
|
||||
assert_redirect view, path
|
||||
|
||||
{:ok, view, _html} = live(conn, path)
|
||||
assert render(view) =~ "Not authorized"
|
||||
end
|
||||
end
|
||||
|
||||
defp deploy_app(slug, team, org, deployment_group, tmp_dir, node) do
|
||||
source = """
|
||||
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{slug}"},"hub_id":"#{team.id}","deployment_group_id":"#{deployment_group.id}"} -->
|
||||
|
||||
# LivebookApp:#{slug}
|
||||
|
||||
```elixir
|
||||
```
|
||||
"""
|
||||
|
||||
{notebook, %{warnings: []}} = Livebook.LiveMarkdown.notebook_from_livemd(source)
|
||||
|
||||
files_dir = Livebook.FileSystem.File.local(tmp_dir)
|
||||
|
||||
{:ok, %Livebook.Teams.AppDeployment{file: zip_content} = app_deployment} =
|
||||
Livebook.Teams.AppDeployment.new(notebook, files_dir)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
encrypted_content = Livebook.Teams.encrypt(zip_content, secret_key)
|
||||
|
||||
app_deployment_id =
|
||||
erpc_call(node, :upload_app_deployment, [
|
||||
org,
|
||||
deployment_group,
|
||||
app_deployment,
|
||||
encrypted_content,
|
||||
# broadcast?
|
||||
true
|
||||
]).id
|
||||
|
||||
app_deployment_id = to_string(app_deployment_id)
|
||||
assert_receive {:app_deployment_started, %{id: ^app_deployment_id}}
|
||||
|
||||
assert_receive {:app_created, %{pid: pid, slug: ^slug}}
|
||||
|
||||
assert_receive {:app_updated,
|
||||
%{
|
||||
slug: ^slug,
|
||||
sessions: [%{app_status: %{execution: :executed, lifecycle: :active}}]
|
||||
}}
|
||||
|
||||
on_exit(fn ->
|
||||
if Process.alive?(pid) do
|
||||
Livebook.App.close(pid)
|
||||
end
|
||||
end)
|
||||
|
||||
pid
|
||||
end
|
||||
end
|
212
test/livebook_teams/web/apps_live_test.exs
Normal file
212
test/livebook_teams/web/apps_live_test.exs
Normal file
|
@ -0,0 +1,212 @@
|
|||
defmodule LivebookWeb.Integration.AppsLiveTest do
|
||||
use Livebook.TeamsIntegrationCase, async: false
|
||||
|
||||
describe "authorized apps" do
|
||||
setup %{conn: conn, node: node} do
|
||||
Livebook.Teams.Broadcasts.subscribe([:agents, :app_deployments, :app_server])
|
||||
Livebook.Apps.subscribe()
|
||||
|
||||
{_agent_key, org, deployment_group, team} = create_agent_team_hub(node)
|
||||
|
||||
# we wait until the agent_connected is received by livebook
|
||||
hub_id = team.id
|
||||
deployment_group_id = to_string(deployment_group.id)
|
||||
org_id = to_string(org.id)
|
||||
|
||||
assert_receive {:agent_joined,
|
||||
%{
|
||||
hub_id: ^hub_id,
|
||||
org_id: ^org_id,
|
||||
deployment_group_id: ^deployment_group_id
|
||||
}}
|
||||
|
||||
start_supervised!(
|
||||
{Livebook.ZTA.LivebookTeams, name: LivebookWeb.ZTA, identity_key: team.id}
|
||||
)
|
||||
|
||||
{conn, code} = authenticate_user_on_teams(conn, node, team)
|
||||
|
||||
{:ok, conn: conn, code: code, deployment_group: deployment_group, org: org, team: team}
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "shows one app if user doesn't have full access",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :apps,
|
||||
prefixes: ["dev-"],
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slug = "dev-app"
|
||||
|
||||
deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
|
||||
html =
|
||||
conn
|
||||
|> get(~p"/apps")
|
||||
|> html_response(200)
|
||||
|
||||
refute html =~ "No apps running."
|
||||
assert html =~ slug
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "shows all apps if user have full access",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :app_server,
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: context.deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slugs = ~w(mkt-app sales-app opt-app)
|
||||
|
||||
for slug <- slugs do
|
||||
deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
end
|
||||
|
||||
html =
|
||||
conn
|
||||
|> get(~p"/apps")
|
||||
|> html_response(200)
|
||||
|
||||
refute html =~ "No apps running."
|
||||
|
||||
for slug <- slugs do
|
||||
assert html =~ slug
|
||||
end
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "updates the apps list in real-time",
|
||||
%{conn: conn, node: node, code: code, tmp_dir: tmp_dir} = context do
|
||||
{:ok, deployment_group} =
|
||||
erpc_call(node, :toggle_groups_authorization, [context.deployment_group])
|
||||
|
||||
oidc_provider = erpc_call(node, :create_oidc_provider, [context.org])
|
||||
|
||||
authorization_group =
|
||||
erpc_call(node, :create_authorization_group, [
|
||||
%{
|
||||
group_name: "marketing",
|
||||
access_type: :apps,
|
||||
prefixes: ["mkt-"],
|
||||
oidc_provider: oidc_provider,
|
||||
deployment_group: deployment_group
|
||||
}
|
||||
])
|
||||
|
||||
erpc_call(node, :update_user_info_groups, [
|
||||
code,
|
||||
[
|
||||
%{
|
||||
"provider_id" => to_string(oidc_provider.id),
|
||||
"group_name" => authorization_group.group_name
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
slug = "marketing-app"
|
||||
|
||||
deploy_app(slug, context.team, context.org, context.deployment_group, tmp_dir, node)
|
||||
|
||||
assert conn
|
||||
|> get(~p"/apps")
|
||||
|> html_response(200) =~ "No apps running."
|
||||
|
||||
{:ok, %{groups_auth: false} = deployment_group} =
|
||||
erpc_call(node, :toggle_groups_authorization, [deployment_group])
|
||||
|
||||
id = to_string(deployment_group.id)
|
||||
assert_receive {:server_authorization_updated, %{id: ^id, groups_auth: false}}
|
||||
|
||||
assert conn
|
||||
|> get(~p"/apps")
|
||||
|> html_response(200) =~ slug
|
||||
end
|
||||
end
|
||||
|
||||
defp deploy_app(slug, team, org, deployment_group, tmp_dir, node) do
|
||||
source = """
|
||||
<!-- livebook:{"app_settings":{"access_type":"public","slug":"#{slug}"},"hub_id":"#{team.id}","deployment_group_id":"#{deployment_group.id}"} -->
|
||||
|
||||
# LivebookApp:#{slug}
|
||||
|
||||
```elixir
|
||||
```
|
||||
"""
|
||||
|
||||
{notebook, %{warnings: []}} = Livebook.LiveMarkdown.notebook_from_livemd(source)
|
||||
|
||||
files_dir = Livebook.FileSystem.File.local(tmp_dir)
|
||||
|
||||
{:ok, %Livebook.Teams.AppDeployment{file: zip_content} = app_deployment} =
|
||||
Livebook.Teams.AppDeployment.new(notebook, files_dir)
|
||||
|
||||
secret_key = Livebook.Teams.derive_key(team.teams_key)
|
||||
encrypted_content = Livebook.Teams.encrypt(zip_content, secret_key)
|
||||
|
||||
app_deployment_id =
|
||||
erpc_call(node, :upload_app_deployment, [
|
||||
org,
|
||||
deployment_group,
|
||||
app_deployment,
|
||||
encrypted_content,
|
||||
# broadcast?
|
||||
true
|
||||
]).id
|
||||
|
||||
app_deployment_id = to_string(app_deployment_id)
|
||||
assert_receive {:app_deployment_started, %{id: ^app_deployment_id}}
|
||||
|
||||
assert_receive {:app_created, %{pid: pid, slug: ^slug}}
|
||||
|
||||
assert_receive {:app_updated,
|
||||
%{
|
||||
slug: ^slug,
|
||||
sessions: [%{app_status: %{execution: :executed, lifecycle: :active}}]
|
||||
}}
|
||||
|
||||
on_exit(fn ->
|
||||
if Process.alive?(pid) do
|
||||
Livebook.App.close(pid)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue