defmodule GitGudWeb.UserRunnersLiveTest do
@moduledoc """
/users/settings/runners: a user mints, lists and removes their own
runners, and only theirs. Their personal repositories' runners pages
show those runners as inherited; the admin page shows whose they are.
"""
use GitGudWeb.ConnCase, async: false
import Ecto.Query
import Phoenix.LiveViewTest
import GitGud.AccountsFixtures
import GitGud.ForgeFixtures
alias GitGud.Audit.Entry
alias GitGud.Repositories.Storage
alias GitGud.Workflows.Runner
alias GitGud.Workflows.Runners
defp entries, do: GitGud.Repo.all(from e in Entry, order_by: e.id)
defp page(conn, user) do
{:ok, lv, _} = live(log_in_user(conn, user), ~p"/users/settings/runners")
lv
end
test "renders, and is linked from the account menu", %{conn: conn} do
user = user_fixture()
lv = page(conn, user)
assert has_element?(lv, "#new-runner-form")
assert has_element?(lv, "#mint-token")
assert has_element?(lv, "#nav-runners[href='/users/settings/runners']")
end
test "mints a registration token for the user, shown once and audited", %{conn: conn} do
user = user_fixture()
lv = page(conn, user)
lv |> element("#mint-token") |> render_click()
token = :sys.get_state(lv.pid).socket.assigns.new_token
assert has_element?(lv, "#new-reg-token", token)
assert has_element?(lv, "#reg-token-terms")
assert {:ok, runner, _} = Runners.register_with_token(token, %{name: "laptop"})
assert runner.owner_id == user.id
assert [minted] = entries()
assert minted.action == "runner.registration_token_minted"
assert minted.subject_user_id == user.id
refute inspect(minted) =~ token
end
test "creates a runner offline, lists it, and removes it", %{conn: conn} do
user = user_fixture()
lv = page(conn, user)
lv
|> form("#new-runner-form", %{
"new_runner" => %{"name" => "laptop", "labels" => "self-hosted"}
})
|> render_submit()
runner = GitGud.Repo.get_by!(Runner, name: "laptop")
assert runner.owner_id == user.id
assert has_element?(lv, "#new-runner-config")
assert has_element?(lv, "#runner-row-#{runner.id}", "user")
lv |> element("#runner-remove-#{runner.id}") |> render_click()
refute GitGud.Repo.get(Runner, runner.id)
refute has_element?(lv, "#runner-row-#{runner.id}")
assert [created, deleted] = entries()
assert {created.action, deleted.action} == {"runner.created", "runner.deleted"}
assert Enum.all?([created, deleted], &(&1.subject_user_id == user.id))
end
test "sets a runner's architecture", %{conn: conn} do
user = user_fixture()
{:ok, runner, _, _} = Runners.create_offline(%{name: "pi"}, user)
lv = page(conn, user)
lv
|> form("#runner-architecture-#{runner.id}", %{"runner" => %{"architecture" => "aarch64"}})
|> render_change()
assert GitGud.Repo.reload!(runner).architecture == "aarch64"
end
test "another user's runners are neither shown nor removable", %{conn: conn} do
owner = user_fixture()
{:ok, theirs, _, _} = Runners.create_offline(%{name: "theirs"}, owner)
{:ok, global, _, _} = Runners.create_offline(%{name: "pool"})
lv = page(conn, user_fixture())
refute has_element?(lv, "#runner-row-#{theirs.id}")
# The global runner is listed, but not this page's to change.
assert has_element?(lv, "#runner-row-#{global.id}")
refute has_element?(lv, "#runner-remove-#{global.id}")
for r <- [theirs, global] do
render_click(lv, "delete_runner", %{"id" => to_string(r.id)})
assert GitGud.Repo.get(Runner, r.id)
render_change(lv, "set_architecture", %{
"runner" => %{"id" => to_string(r.id), "architecture" => "aarch64"}
})
assert GitGud.Repo.reload!(r).architecture == "x86_64"
end
assert entries() == []
end
test "one of the user's runners connecting shows without a reload", %{conn: conn} do
user = user_fixture()
{:ok, runner, _, _} = Runners.create_offline(%{name: "caprica"}, user)
lv = page(conn, user)
{:ok, _} = Runners.touch_last_seen(runner)
assert has_element?(lv, "#runner-row-#{runner.id}", "last seen")
assert has_element?(lv, "#flash-info", "Runner 'caprica' connected.")
end
test "a personal repository's runners page lists its owner's runners, read-only",
%{conn: conn} do
{owner, repo} = repository_fixture()
repo = GitGud.Repo.preload(repo, [:owner, :organization])
{:ok, runner, _, _} = Runners.create_offline(%{name: "laptop"}, owner)
{:ok, lv, _} =
live(
log_in_user(conn, owner),
"/r/#{Storage.repo_handle(repo)}/#{repo.name}/settings/runners"
)
assert has_element?(lv, "#runner-row-#{runner.id}", "user")
refute has_element?(lv, "#runner-architecture-#{runner.id}")
assert has_element?(lv, "#own-runners-link")
render_click(lv, "delete_runner", %{"id" => to_string(runner.id)})
assert GitGud.Repo.get(Runner, runner.id)
# And hears it connect.
{:ok, _} = Runners.touch_last_seen(runner)
assert has_element?(lv, "#flash-info", "Runner 'laptop' connected.")
end
test "the admin page shows a user's runner and whose it is", %{conn: conn} do
admin = user_fixture() |> GitGud.Accounts.User.admin_changeset(true) |> GitGud.Repo.update!()
owner = user_fixture()
{:ok, runner, _, _} = Runners.create_offline(%{name: "laptop"}, owner)
{:ok, lv, _} = live(log_in_user(conn, admin), ~p"/admin/runners")
assert has_element?(lv, "#runner-row-#{runner.id}", "user")
assert has_element?(lv, "#runner-row-#{runner.id} a[href='/u/#{owner.handle}']")
lv |> element("#runner-remove-#{runner.id}") |> render_click()
refute GitGud.Repo.get(Runner, runner.id)
# Recorded in the owner's security log.
assert [%Entry{action: "runner.deleted", subject_user_id: uid}] = entries()
assert uid == owner.id
end
end
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
5.8 KiB · text
History
b868907