defmodule Livebook.TeamsTest do use Livebook.TeamsIntegrationCase, async: true alias Livebook.{Notebook, Teams, Utils} alias Livebook.Teams.Org describe "create_org/1" do test "returns the device flow data to confirm the org creation" do org = build(:org) assert {:ok, %{ "device_code" => _device_code, "expires_in" => 300, "id" => _org_id, "user_code" => _user_code, "verification_uri" => _verification_uri }} = Teams.create_org(org, %{}) end test "returns changeset errors when data is invalid" do org = build(:org) assert {:error, changeset} = Teams.create_org(org, %{name: nil}) assert "can't be blank" in errors_on(changeset).name end end describe "join_org/1" do test "returns the device flow data to confirm the org creation", %{user: user, node: node} do org = build(:org) key_hash = Org.key_hash(org) teams_org = :erpc.call(node, TeamsRPC, :create_org, [[name: org.name]]) :erpc.call(node, TeamsRPC, :create_org_key, [ [org: teams_org, key_hash: key_hash] ]) :erpc.call(node, TeamsRPC, :create_user_org, [[org: teams_org, user: user]]) assert {:ok, %{ "device_code" => _device_code, "expires_in" => 300, "id" => _org_id, "user_code" => _user_code, "verification_uri" => _verification_uri }} = Teams.join_org(org, %{}) end test "returns changeset errors when data is invalid" do org = build(:org) assert {:error, changeset} = Teams.join_org(org, %{name: nil}) assert "can't be blank" in errors_on(changeset).name end test "returns changeset errors when org doesn't exist" do org = build(:org) assert {:error, changeset} = Teams.join_org(org, %{}) assert "does not exist" in errors_on(changeset).name assert "does not match existing key" in errors_on(changeset).teams_key end end describe "get_org_request_completion_data/1" do test "returns the org data when it has been confirmed", %{node: node, user: user} do teams_key = Org.teams_key() key_hash = :crypto.hash(:sha256, teams_key) |> Base.url_encode64(padding: false) org_request = :erpc.call(node, TeamsRPC, :create_org_request, [[key_hash: key_hash]]) org_request = :erpc.call(node, TeamsRPC, :confirm_org_request, [org_request, user]) org = build(:org, id: org_request.id, name: org_request.name, teams_key: teams_key, user_code: org_request.user_code ) %{ token: token, user_org: %{ org: %{ id: id, name: name, keys: [%{id: org_key_id}], key_pair: %{public_key: org_public_key} }, user: %{id: user_id} } } = org_request.user_org_session assert Teams.get_org_request_completion_data(org, org_request.device_code) == {:ok, %{ "id" => id, "name" => name, "org_key_id" => org_key_id, "org_public_key" => org_public_key, "session_token" => token, "user_id" => user_id }} end test "returns the org request awaiting confirmation", %{node: node} do teams_key = Org.teams_key() key_hash = :crypto.hash(:sha256, teams_key) |> Base.url_encode64(padding: false) org_request = :erpc.call(node, TeamsRPC, :create_org_request, [[key_hash: key_hash]]) org = build(:org, id: org_request.id, name: org_request.name, teams_key: teams_key, user_code: org_request.user_code ) assert Teams.get_org_request_completion_data(org, org_request.device_code) == {:ok, :awaiting_confirmation} end test "returns error when org request doesn't exist" do org = build(:org, id: 0) assert {:transport_error, _embarrassing} = Teams.get_org_request_completion_data(org, "") end test "returns error when org request expired", %{node: node} do now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) expires_at = NaiveDateTime.add(now, -5000) teams_key = Org.teams_key() key_hash = :crypto.hash(:sha256, teams_key) |> Base.url_encode64(padding: false) org_request = :erpc.call(node, TeamsRPC, :create_org_request, [ [expires_at: expires_at, key_hash: key_hash] ]) org = build(:org, id: org_request.id, name: org_request.name, teams_key: teams_key, user_code: org_request.user_code ) assert Teams.get_org_request_completion_data(org, org_request.device_code) == {:error, :expired} end end 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) assert {:ok, _id} = Teams.create_deployment_group(team, deployment_group) # Guarantee uniqueness assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group) assert "has already been taken" in errors_on(changeset).name end test "returns changeset errors when the name is invalid", %{user: user, node: node} do team = create_team_hub(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 end test "returns changeset errors when the mode is invalid", %{user: user, node: node} do team = create_team_hub(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 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) 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()} notebook = %{ Notebook.new() | app_settings: app_settings, name: "MyNotebook", hub_id: team.id, deployment_group_id: to_string(id) } files_dir = Livebook.FileSystem.File.local(tmp_dir) assert {:ok, app_deployment} = Teams.AppDeployment.new(notebook, files_dir) assert Teams.deploy_app(team, app_deployment, app_settings) == :ok assert {:error, %{errors: [slug: {"should only contain alphanumeric characters and dashes", []}]}} = Teams.deploy_app(team, %{app_deployment | slug: "@abc"}, app_settings) # Since the fields below belongs to AppSettings, we're mapping the errors to `:file` field. assert {:error, %{errors: [file: {"can't be blank", []}]}} = Teams.deploy_app(team, app_deployment, %{app_settings | multi_session: nil}) assert {:error, %{errors: [file: {"can't be blank", []}]}} = Teams.deploy_app(team, app_deployment, %{app_settings | access_type: nil}) assert {:error, %{errors: [file: {"is invalid", []}]}} = Teams.deploy_app(team, app_deployment, %{app_settings | access_type: :abc}) end end end