Improve integration tests infrastructure (#2583)

This commit is contained in:
Alexandre de Souza 2024-04-29 11:06:57 -03:00 committed by GitHub
parent fdac5cfe4f
commit 3d289448cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 218 additions and 497 deletions

View file

@ -6,168 +6,37 @@ defmodule Livebook.Hubs.TeamClientTest do
setup do
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
Livebook.Teams.Broadcasts.subscribe([:clients, :deployment_groups, :app_deployments, :agents])
:ok
end
test "rejects the web socket connection with invalid credentials", %{user: user, token: token} do
team =
build(:team,
user_id: user.id,
org_id: 123_456,
org_key_id: 123_456,
session_token: token
)
id = team.id
TeamClient.start_link(team)
assert_receive {:hub_server_error, ^id, error}
assert error ==
"#{team.hub_name}: Your session is out-of-date. Please re-join the organization."
refute Livebook.Hubs.hub_exists?(team.id)
end
describe "handle events" do
setup %{user: user, node: node} do
team = create_team_hub(user, node)
describe "connect" do
test "successfully authenticates the websocket connection", %{user: user, node: node} do
team = build_team_hub(user, node)
id = team.id
TeamClient.start_link(team)
assert_receive {:hub_connected, ^id}
assert_receive {:client_connected, ^id}
{:ok, team: team}
end
test "receives secret events", %{team: team} do
secret = build(:secret, name: "SECRET", value: "BAR")
assert Livebook.Hubs.create_secret(team, secret) == :ok
name = secret.name
value = secret.value
# receives `{:secret_created, secret}` event
# with the value decrypted
assert_receive {:secret_created, %{name: ^name, value: ^value}}
# updates the secret
updated_secret = Map.replace!(secret, :value, "BAZ")
assert Livebook.Hubs.update_secret(team, updated_secret) == :ok
new_value = updated_secret.value
# receives `{:secret_updated, secret}` event
# with the value decrypted
assert_receive {:secret_updated, %{name: ^name, value: ^new_value}}
# deletes the secret
assert Livebook.Hubs.delete_secret(team, updated_secret) == :ok
# receives `{:secret_deleted, secret}` event
assert_receive {:secret_deleted, %{name: ^name, value: ^new_value}}
end
test "receives file system events", %{team: team} do
file_system =
build(:fs_s3, bucket_url: "https://file_system.s3.amazonaws.com", region: "us-east-1")
assert Livebook.Hubs.create_file_system(team, file_system) == :ok
bucket_url = file_system.bucket_url
region = file_system.region
# receives `{:file_system_created, file_system}` event
assert_receive {:file_system_created,
%{external_id: id, bucket_url: ^bucket_url, region: ^region}}
# updates the file system
updated_file_system = %{file_system | region: "eu-central-1", external_id: id}
assert Livebook.Hubs.update_file_system(team, updated_file_system) == :ok
new_region = updated_file_system.region
# receives `{:file_system_updated, file_system}` event
assert_receive {:file_system_updated,
%{external_id: ^id, bucket_url: ^bucket_url, region: ^new_region}}
# deletes the file system
assert Livebook.Hubs.delete_file_system(team, updated_file_system) == :ok
# receives `{:file_system_deleted, file_system}` event
assert_receive {:file_system_deleted, %{external_id: ^id, bucket_url: ^bucket_url}}
end
test "receives deployment group events", %{team: team} do
deployment_group =
build(:deployment_group, name: "DEPLOYMENT_GROUP_#{team.id}", mode: :online)
assert {:ok, _} = Livebook.Teams.create_deployment_group(team, deployment_group)
%{name: name, mode: mode} = deployment_group
# receives `{:event, :deployment_group_created, deployment_group}` event
assert_receive {:deployment_group_created, %{name: ^name, mode: ^mode}}
end
@tag :tmp_dir
test "receives app events", %{team: team, node: node, tmp_dir: tmp_dir} do
deployment_group = build(:deployment_group, name: team.id, mode: :online)
assert {:ok, id} = Livebook.Teams.create_deployment_group(team, deployment_group)
id = to_string(id)
assert_receive {:deployment_group_created, %{id: ^id}}
# creates the app deployment
slug = Livebook.Utils.random_short_id()
title = "MyNotebook-#{slug}"
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
notebook = %{
Livebook.Notebook.new()
| app_settings: app_settings,
name: title,
hub_id: team.id,
deployment_group_id: id
}
files_dir = Livebook.FileSystem.File.local(tmp_dir)
{:ok, app_deployment} = Livebook.Teams.AppDeployment.new(notebook, files_dir)
:ok = Livebook.Teams.deploy_app(team, app_deployment)
sha = app_deployment.sha
multi_session = app_settings.multi_session
access_type = app_settings.access_type
assert_receive {:app_deployment_started,
%Livebook.Teams.AppDeployment{
slug: ^slug,
sha: ^sha,
title: ^title,
multi_session: ^multi_session,
access_type: ^access_type,
deployment_group_id: ^id
} = app_deployment}
# force app deployment to be deleted
erpc_call(node, :toggle_app_deployment, [app_deployment.id, team.org_id])
assert_receive {:app_deployment_stopped, ^app_deployment}
end
test "receives the user events", %{team: team, node: node} do
Livebook.Hubs.Broadcasts.subscribe([:crud])
# force user to be deleted from org
erpc_call(node, :delete_user_org, [team.user_id, team.org_id])
test "rejects the web socket connection with invalid credentials", %{user: user, token: token} do
team =
build(:team,
user_id: user.id,
org_id: 123_456,
org_key_id: 123_456,
session_token: token
)
id = team.id
reason = "#{team.hub_name}: you were removed from the org"
assert_receive {:hub_server_error, ^id, ^reason}
assert_receive {:hub_deleted, ^id}
refute team in Livebook.Hubs.get_hubs()
start_supervised!({TeamClient, team})
assert_receive {:hub_server_error, ^id, error}
assert error ==
"#{team.hub_name}: Your session is out-of-date. Please re-join the organization."
end
end
@ -187,6 +56,20 @@ defmodule Livebook.Hubs.TeamClientTest do
{:ok, team: team, user_connected: user_connected}
end
test "receives the user events", %{team: team, node: node} do
Livebook.Hubs.Broadcasts.subscribe([:crud])
# force user to be deleted from org
erpc_call(node, :delete_user_org, [team.user_id, team.org_id])
id = team.id
reason = "#{team.hub_name}: you were removed from the org"
assert_receive {:hub_server_error, ^id, ^reason}
assert_receive {:hub_deleted, ^id}
refute team in Livebook.Hubs.get_hubs()
end
test "dispatches the secrets list", %{team: team, user_connected: user_connected} do
secret =
build(:secret,

View file

@ -1,30 +0,0 @@
defmodule Livebook.Hubs.TeamTest do
use Livebook.TeamsIntegrationCase, async: true
alias Livebook.Hubs.Provider
describe "stamping" do
test "generates and verifies stamp for a notebook", %{user: user, node: node} do
team = create_team_hub(user, node)
notebook_source = """
# Team notebook
# Intro
```elixir
IO.puts("Hello!")
```
"""
metadata = %{"key" => "value"}
assert {:ok, stamp} = Provider.notebook_stamp(team, notebook_source, metadata)
assert {:ok, ^metadata} = Provider.verify_notebook_stamp(team, notebook_source, stamp)
assert {:error, :invalid} =
Provider.verify_notebook_stamp(team, notebook_source <> "change\n", stamp)
end
end
end

View file

@ -3,8 +3,15 @@ defmodule Livebook.HubsTest do
alias Livebook.Hubs
setup do
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
Livebook.Teams.Broadcasts.subscribe([:clients])
:ok
end
test "get_hubs/0 returns a list of persisted hubs", %{user: user, node: node} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
assert team in Hubs.get_hubs()
Hubs.delete_hub(team.id)
@ -12,7 +19,7 @@ defmodule Livebook.HubsTest do
end
test "get_metadata/0 returns a list of persisted hubs normalized", %{user: user, node: node} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
metadata = Hubs.Provider.to_metadata(team)
assert metadata in Hubs.get_metadata()
@ -28,7 +35,7 @@ defmodule Livebook.HubsTest do
Hubs.fetch_hub!("nonexistent")
end
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
assert Hubs.fetch_hub!(team.id) == team
end
@ -49,7 +56,7 @@ defmodule Livebook.HubsTest do
end
test "updates hub", %{user: user, node: node} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
Hubs.save_hub(%{team | hub_emoji: "🐈"})
assert Hubs.fetch_hub!(team.id).hub_emoji == "🐈"
@ -58,10 +65,13 @@ defmodule Livebook.HubsTest do
describe "create_secret/2" do
test "creates a new secret", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "FOO", value: "BAR")
hub = connect_to_teams(user, node)
name = secret_name(hub)
value = hub.id
secret = build(:secret, name: name, value: value, hub_id: hub.id)
assert Hubs.create_secret(hub, secret) == :ok
assert secret in Hubs.get_secrets(hub)
# Guarantee uniqueness
assert {:error, changeset} = Hubs.create_secret(hub, secret)
@ -69,69 +79,83 @@ defmodule Livebook.HubsTest do
end
test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "LB_FOO", value: "BAR")
hub = connect_to_teams(user, node)
secret = build(:secret, name: "LB_FOO", value: "BAR", hub_id: hub.id)
assert {:error, changeset} = Hubs.create_secret(hub, secret)
assert "cannot start with the LB_ prefix" in errors_on(changeset).name
refute secret in Hubs.get_secrets(hub)
end
end
describe "update_secret/2" do
test "updates a secret", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "UPDATE_ME", value: "BAR")
hub = connect_to_teams(user, node)
name = secret_name(hub)
value = hub.id
secret = build(:secret, name: name, value: value, hub_id: hub.id)
assert Hubs.create_secret(hub, secret) == :ok
assert_receive {:secret_created, %{name: ^name, value: ^value}}
assert secret in Hubs.get_secrets(hub)
update_secret = Map.replace!(secret, :value, "BAZ")
assert Hubs.update_secret(hub, update_secret) == :ok
new_value = "BAZ"
updated_secret = Map.replace!(secret, :value, new_value)
assert Hubs.update_secret(hub, updated_secret) == :ok
assert_receive {:secret_updated, %{name: ^name, value: ^new_value}}
refute secret in Hubs.get_secrets(hub)
assert updated_secret in Hubs.get_secrets(hub)
end
test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "FIX_ME", value: "BAR")
hub = connect_to_teams(user, node)
name = secret_name(hub)
value = hub.id
secret = build(:secret, name: name, value: value, hub_id: hub.id)
assert Hubs.create_secret(hub, secret) == :ok
assert_receive {:secret_created, %{name: ^name, value: ^value}}
update_secret = Map.replace!(secret, :value, "")
updated_secret = Map.replace!(secret, :value, "")
assert {:error, changeset} = Hubs.update_secret(hub, update_secret)
assert {:error, changeset} = Hubs.update_secret(hub, updated_secret)
assert "can't be blank" in errors_on(changeset).value
end
end
describe "delete_secret/2" do
test "deletes a secret", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "DELETE_ME", value: "BAR")
test "delete_secret/2 deletes a secret", %{user: user, node: node} do
hub = connect_to_teams(user, node)
name = secret_name(hub)
value = hub.id
secret = build(:secret, name: name, value: value, hub_id: hub.id)
assert Hubs.create_secret(hub, secret) == :ok
assert Hubs.delete_secret(hub, secret) == :ok
assert Hubs.create_secret(hub, secret) == :ok
assert_receive {:secret_created, %{name: ^name, value: ^value}}
assert secret in Hubs.get_secrets(hub)
# Guarantee it's been removed and will return HTTP status 404
assert Hubs.delete_secret(hub, secret) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
end
assert Hubs.delete_secret(hub, secret) == :ok
assert_receive {:secret_deleted, %{name: ^name, value: ^value}}
refute secret in Hubs.get_secrets(hub)
test "returns transport errors when secret doesn't exists", %{user: user, node: node} do
hub = create_team_hub(user, node)
secret = build(:secret, name: "I_CANT_EXIST", value: "BAR")
# Guarantee it's been removed and will return HTTP status 404
assert Hubs.delete_secret(hub, secret) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
# Guarantee it doesn't exists and will return HTTP status 404
assert Hubs.delete_secret(hub, secret) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
end
refute_receive {:secret_deleted, _}
end
describe "create_file_system/2" do
test "creates a new file system", %{user: user, node: node} do
hub = create_team_hub(user, node)
file_system = build(:fs_s3, bucket_url: "https://file_system_created.s3.amazonaws.com")
hub = connect_to_teams(user, node)
bucket_url = "https://#{hub.id}.s3.amazonaws.com"
file_system = build(:fs_s3, bucket_url: bucket_url)
region = file_system.region
assert Hubs.create_file_system(hub, file_system) == :ok
assert_receive {:file_system_created, %{bucket_url: ^bucket_url, region: ^region}}
# Guarantee uniqueness
assert {:error, changeset} = Hubs.create_file_system(hub, file_system)
@ -139,32 +163,35 @@ defmodule Livebook.HubsTest do
end
test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = create_team_hub(user, node)
hub = connect_to_teams(user, node)
file_system = build(:fs_s3, bucket_url: nil)
assert {:error, changeset} = Hubs.create_file_system(hub, file_system)
assert "can't be blank" in errors_on(changeset).bucket_url
refute_receive {:file_system_created, _}
end
end
describe "update_file_system/2" do
test "updates a file system", %{user: user, node: node} do
hub = create_team_hub(user, node)
teams_file_system = create_teams_file_system(hub, node)
hub = connect_to_teams(user, node)
bucket_url = "https://#{hub.id}.s3.amazonaws.com"
file_system = build(:fs_s3, bucket_url: bucket_url)
file_system =
build(:fs_s3,
bucket_url: teams_file_system.name,
region: "us-east-1",
external_id: to_string(teams_file_system.id)
)
assert Hubs.create_file_system(hub, file_system) == :ok
assert_receive {:file_system_created, %{external_id: external_id} = file_system}
assert file_system in Hubs.get_file_systems(hub)
update_file_system = Map.replace!(file_system, :region, "eu-central-1")
assert Hubs.update_file_system(hub, update_file_system) == :ok
updated_file_system = Map.replace!(file_system, :region, "eu-central-1")
assert Hubs.update_file_system(hub, updated_file_system) == :ok
assert_receive {:file_system_updated, %{external_id: ^external_id, region: "eu-central-1"}}
refute file_system in Hubs.get_file_systems(hub)
assert updated_file_system in Hubs.get_file_systems(hub)
end
test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = create_team_hub(user, node)
hub = connect_to_teams(user, node)
teams_file_system = create_teams_file_system(hub, node)
file_system =
@ -177,42 +204,64 @@ defmodule Livebook.HubsTest do
assert {:error, changeset} = Hubs.update_file_system(hub, update_file_system)
assert "can't be blank" in errors_on(changeset).bucket_url
refute_receive {:file_system_updated, _}
end
end
describe "delete_file_system/2" do
test "deletes a file system", %{user: user, node: node} do
hub = create_team_hub(user, node)
teams_file_system = create_teams_file_system(hub, node)
test "delete_file_system/2 deletes a file system", %{user: user, node: node} do
hub = connect_to_teams(user, node)
bucket_url = "https://#{hub.id}.s3.amazonaws.com"
file_system = build(:fs_s3, bucket_url: bucket_url)
file_system =
build(:fs_s3,
bucket_url: teams_file_system.name,
region: "us-east-1",
external_id: to_string(teams_file_system.id)
)
assert Hubs.create_file_system(hub, file_system) == :ok
assert_receive {:file_system_created, %{external_id: external_id} = file_system}
assert file_system in Hubs.get_file_systems(hub)
assert Hubs.delete_file_system(hub, file_system) == :ok
assert Hubs.delete_file_system(hub, file_system) == :ok
assert_receive {:file_system_deleted, %{external_id: ^external_id}}
refute file_system in Hubs.get_file_systems(hub)
# Guarantee it's been removed and will return HTTP status 404
assert Hubs.delete_file_system(hub, file_system) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
end
# Guarantee it's been removed and will return HTTP status 404
assert Hubs.delete_file_system(hub, file_system) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
test "returns transport errors when file system doesn't exists", %{user: user, node: node} do
hub = create_team_hub(user, node)
refute_receive {:file_system_deleted, _}
end
file_system =
build(:fs_s3,
bucket_url: "https://i_cant_exist.s3.amazonaws.com",
external_id: "123456789"
)
test "generates and verifies stamp for a notebook", %{user: user, node: node} do
team = connect_to_teams(user, node)
# Guarantee it doesn't exists and will return HTTP status 404
assert Hubs.delete_file_system(hub, file_system) ==
{:transport_error,
"Something went wrong, try again later or please file a bug if it persists"}
end
notebook_source = """
# Team notebook
# Intro
```elixir
IO.puts("Hello!")
```
"""
metadata = %{"key" => "value"}
assert {:ok, stamp} = Hubs.Provider.notebook_stamp(team, notebook_source, metadata)
assert {:ok, ^metadata} = Hubs.Provider.verify_notebook_stamp(team, notebook_source, stamp)
assert {:error, :invalid} =
Hubs.Provider.verify_notebook_stamp(team, notebook_source <> "change\n", stamp)
end
defp connect_to_teams(user, node) do
%{id: id} = team = create_team_hub(user, node)
assert_receive {:hub_connected, ^id}
assert_receive {:client_connected, ^id}
team
end
defp secret_name(%{id: id}) do
id
|> String.replace("-", "_")
|> String.upcase()
end
end

View file

@ -1,226 +0,0 @@
defmodule Livebook.Teams.ConnectionTest do
alias Livebook.FileSystem
use Livebook.TeamsIntegrationCase, async: true
alias Livebook.Teams.Connection
describe "connect" do
test "successfully authenticates the websocket connection", %{user: user, node: node} do
{_, headers} = build_team_headers(user, node)
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
end
test "rejects the websocket connection with invalid credentials", %{user: user} do
headers = [
{"x-user", to_string(user.id)},
{"x-org", to_string(user.id)},
{"x-org-key", to_string(user.id)},
{"x-session-token", "foo"}
]
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive {:server_error,
"Your session is out-of-date. Please re-join the organization."}
assert {:ok, _conn} = Connection.start_link(self(), [])
assert_receive {:server_error,
"Invalid request. Please re-join the organization and update Livebook if the issue persists."}
end
end
describe "handle events" do
test "receives the secret_created event", %{user: user, node: node} do
{hub, headers} = build_team_headers(user, node)
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
# creates a new secret
secret = build(:secret, name: "FOO", value: "BAR")
assert Livebook.Hubs.create_secret(hub, secret) == :ok
# receives `{:event, :secret_created, secret_created}` event
# without decrypting the value
assert_receive {:event, :secret_created, secret_created}
assert secret_created.name == secret.name
refute secret_created.value == secret.value
end
test "receives the file_system_created event", %{user: user, node: node} do
{hub, headers} = build_team_headers(user, node)
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
# creates a new file system
file_system = build(:fs_s3, bucket_url: "https://file_system_created.s3.amazonaws.com")
assert Livebook.Hubs.create_file_system(hub, file_system) == :ok
type = Livebook.FileSystems.type(file_system)
%{name: name} = FileSystem.external_metadata(file_system)
# receives `{:event, :file_system_created, file_system_created}` event
# without decrypting the value
assert_receive {:event, :file_system_created, file_system_created}
assert file_system_created.name == name
assert file_system_created.type == to_string(type)
refute file_system_created.value == FileSystem.dump(file_system)
assert is_binary(file_system_created.value)
end
test "receives the deployment_group_created event", %{user: user, node: node} do
{hub, headers} = build_team_headers(user, node)
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
# creates a new deployment group with offline mode
deployment_group = build(:deployment_group, name: "FOO", mode: :offline, clustering: :dns)
assert {:ok, _id} =
Livebook.Teams.create_deployment_group(hub, deployment_group)
# deployment_group name and mode are not encrypted
assert_receive {:event, :deployment_group_created, deployment_group_created}
assert deployment_group_created.name == deployment_group.name
assert String.to_existing_atom(deployment_group_created.mode) == deployment_group.mode
assert String.to_existing_atom(deployment_group_created.clustering) ==
deployment_group.clustering
# since the deployment group is with offline mode, the agent key shouldn't exists
assert deployment_group_created.agent_keys == []
# creates a new deployment group with online mode
deployment_group = build(:deployment_group, name: "BAR", mode: :online, clustering: :dns)
{:ok, _id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
# deployment_group name and mode are not encrypted
assert_receive {:event, :deployment_group_created, deployment_group_created}
assert deployment_group_created.name == deployment_group.name
assert String.to_existing_atom(deployment_group_created.mode) == deployment_group.mode
assert String.to_existing_atom(deployment_group_created.clustering) ==
deployment_group.clustering
# receives the built-in agent key
assert [agent_key] = deployment_group_created.agent_keys
assert is_binary(agent_key.key)
assert agent_key.deployment_group_id == deployment_group_created.id
end
@tag :tmp_dir
test "receives the app deployments list from user_connected event",
%{user: user, node: node, tmp_dir: tmp_dir} do
{hub, headers} = build_team_headers(user, node)
# creates a new deployment group
deployment_group = build(:deployment_group, name: "BAZ", mode: :online)
{:ok, id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
# creates a new app deployment
deployment_group_id = to_string(id)
slug = Livebook.Utils.random_short_id()
title = "MyNotebook3-#{slug}"
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
notebook = %{
Livebook.Notebook.new()
| app_settings: app_settings,
name: title,
hub_id: hub.id,
deployment_group_id: deployment_group_id
}
files_dir = Livebook.FileSystem.File.local(tmp_dir)
{:ok, %Livebook.Teams.AppDeployment{} = app_deployment} =
Livebook.Teams.AppDeployment.new(notebook, files_dir)
# since we want to fetch the app deployment from connection event,
# we need to persist it before we connect to the WebSocket
:ok = Livebook.Teams.deploy_app(hub, app_deployment)
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
assert_receive {:event, :user_connected, user_connected}
assert [app_deployment2] = user_connected.app_deployments
assert app_deployment2.title == title
assert app_deployment2.slug == slug
assert app_deployment2.sha == app_deployment.sha
assert app_deployment2.deployment_group_id == deployment_group_id
end
@tag :tmp_dir
test "receives the app deployments list from agent_connected event",
%{user: user, node: node, tmp_dir: tmp_dir} do
# To create a new app deployment, we need use the User connection
{hub, _headers} = build_team_headers(user, node)
# creates a new deployment group
deployment_group = build(:deployment_group, name: "BAZ", mode: :online)
{:ok, id} = Livebook.Teams.create_deployment_group(hub, deployment_group)
teams_deployment_group = erpc_call(node, :get_deployment_group!, [id])
[teams_agent_key] = teams_deployment_group.agent_keys
# creates a new app deployment
slug = Livebook.Utils.random_short_id()
title = "MyNotebook3-#{slug}"
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
notebook = %{
Livebook.Notebook.new()
| app_settings: app_settings,
name: title,
hub_id: hub.id,
deployment_group_id: to_string(id)
}
files_dir = Livebook.FileSystem.File.local(tmp_dir)
{:ok, %Livebook.Teams.AppDeployment{} = app_deployment} =
Livebook.Teams.AppDeployment.new(notebook, files_dir)
# since we want to fetch the app deployment from connection event,
# we need to persist it before we connect to the WebSocket
:ok = Livebook.Teams.deploy_app(hub, app_deployment)
# As we need to be Agent to receive the app deployments list to be deployed,
# we will create another connection here
public_key = hub.org_public_key
hub = %{
hub
| user_id: nil,
org_public_key: nil,
session_token: teams_agent_key.key
}
agent_name = Livebook.Config.agent_name()
headers = [
{"x-lb-version", Livebook.Config.app_version()},
{"x-org", to_string(hub.org_id)},
{"x-org-key", to_string(hub.org_key_id)},
{"x-agent-name", agent_name},
{"x-agent-key", hub.session_token}
]
assert {:ok, _conn} = Connection.start_link(self(), headers)
assert_receive :connected
assert_receive {:event, :agent_connected, agent_connected}
assert agent_connected.name == agent_name
assert agent_connected.public_key == public_key
assert [app_deployment2] = agent_connected.app_deployments
assert app_deployment2.title == title
assert app_deployment2.slug == slug
assert app_deployment2.sha == app_deployment.sha
assert app_deployment2.deployment_group_id == to_string(id)
end
end
end

View file

@ -1,9 +1,16 @@
defmodule Livebook.TeamsTest do
use Livebook.TeamsIntegrationCase, async: true
alias Livebook.{Notebook, Teams, Utils}
alias Livebook.{FileSystem, Notebook, Teams, Utils}
alias Livebook.Teams.Org
setup do
Livebook.Hubs.Broadcasts.subscribe([:connection, :file_systems, :secrets])
Livebook.Teams.Broadcasts.subscribe([:clients, :deployment_groups, :app_deployments, :agents])
:ok
end
describe "create_org/1" do
test "returns the device flow data to confirm the org creation" do
org = build(:org)
@ -161,10 +168,17 @@ defmodule Livebook.TeamsTest do
describe "create_deployment_group/2" do
test "creates a new deployment group when the data is valid", %{user: user, node: node} do
team = create_team_hub(user, node)
deployment_group = build(:deployment_group)
team = connect_to_teams(user, node)
assert {:ok, _id} = Teams.create_deployment_group(team, deployment_group)
deployment_group =
build(:deployment_group, name: "DEPLOYMENT_GROUP_#{team.id}", mode: :online)
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group)
%{name: name, mode: mode} = deployment_group
id = to_string(id)
assert_receive {:deployment_group_created, %{id: ^id, name: ^name, mode: ^mode}}
# Guarantee uniqueness
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
@ -172,50 +186,69 @@ defmodule Livebook.TeamsTest do
end
test "returns changeset errors when the name is invalid", %{user: user, node: node} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
deployment_group = %{build(:deployment_group) | name: ""}
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
assert "can't be blank" in errors_on(changeset).name
refute_receive {:deployment_group_created, _}
end
test "returns changeset errors when the mode is invalid", %{user: user, node: node} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
deployment_group = %{build(:deployment_group) | mode: "invalid"}
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group)
assert "is invalid" in errors_on(changeset).mode
refute_receive {:deployment_group_created, _}
end
end
describe "deploy_app/2" do
@tag :tmp_dir
test "deploys app to Teams from a notebook", %{user: user, node: node, tmp_dir: tmp_dir} do
team = create_team_hub(user, node)
team = connect_to_teams(user, node)
deployment_group = build(:deployment_group, name: "BAZ", mode: :online)
{:ok, id} = Teams.create_deployment_group(team, deployment_group)
app_settings = %{Notebook.AppSettings.new() | slug: Utils.random_short_id()}
id = to_string(id)
assert_receive {:deployment_group_created, %{id: ^id}}
# creates the app deployment
slug = Utils.random_short_id()
title = "MyNotebook-#{slug}"
app_settings = %{Notebook.AppSettings.new() | slug: slug}
notebook = %{
Notebook.new()
| app_settings: app_settings,
name: "MyNotebook",
name: title,
hub_id: team.id,
deployment_group_id: to_string(id)
deployment_group_id: id
}
files_dir = Livebook.FileSystem.File.local(tmp_dir)
files_dir = FileSystem.File.local(tmp_dir)
assert {:ok, app_deployment} = Teams.AppDeployment.new(notebook, files_dir)
assert Teams.deploy_app(team, app_deployment) == :ok
sha = app_deployment.sha
multi_session = app_settings.multi_session
access_type = app_settings.access_type
assert_receive {:app_deployment_started,
%Livebook.Teams.AppDeployment{
slug: ^slug,
sha: ^sha,
title: ^title,
multi_session: ^multi_session,
access_type: ^access_type,
deployment_group_id: ^id
} = app_deployment2}
assert {:error,
%{errors: [slug: {"should only contain alphanumeric characters and dashes", []}]}} =
Teams.deploy_app(team, %{app_deployment | slug: "@abc"})
# Since the fields below belongs to AppSettings, we're mapping the errors to `:file` field.
assert {:error, %{errors: [multi_session: {"can't be blank", []}]}} =
Teams.deploy_app(team, %{app_deployment | multi_session: nil})
@ -224,6 +257,18 @@ defmodule Livebook.TeamsTest do
assert {:error, %{errors: [access_type: {"is invalid", []}]}} =
Teams.deploy_app(team, %{app_deployment | access_type: :abc})
# force app deployment to be stopped
erpc_call(node, :toggle_app_deployment, [app_deployment2.id, team.org_id])
assert_receive {:app_deployment_stopped, ^app_deployment2}
end
end
defp connect_to_teams(user, node) do
%{id: id} = team = create_team_hub(user, node)
assert_receive {:hub_connected, ^id}
assert_receive {:client_connected, ^id}
team
end
end