4.8 KiB · text History b868907
defmodule GitGudWeb.RunnerAuditTest do
  @moduledoc """
  Runners leave audit entries where they belong — a repository's or an
  org's log, or the instance's for a global one: a registration token
  minted, a runner created offline (from the settings page or the mix
  task) or deleted. Never a token.
  """
  use GitGudWeb.ConnCase, async: false

  import Ecto.Query
  import Phoenix.LiveViewTest
  import GitGud.AccountsFixtures
  import GitGud.ForgeFixtures

  alias GitGud.Audit.Entry
  alias GitGud.Organizations
  alias GitGud.Repositories.Storage
  alias GitGud.Workflows.Runner

  defp entries, do: GitGud.Repo.all(from e in Entry, order_by: e.id)

  test "a repository's: a runner's architecture changed", %{conn: conn} do
    {owner, repo} = repository_fixture()
    repo = GitGud.Repo.preload(repo, [:owner, :organization])

    {:ok, lv, _} =
      live(
        log_in_user(conn, owner),
        "/r/#{Storage.repo_handle(repo)}/#{repo.name}/settings/runners"
      )

    render_submit(lv, "create_runner", %{"name" => "e52c", "labels" => "ubuntu-24.04-arm"})
    runner = GitGud.Repo.get_by!(Runner, name: "e52c")

    lv
    |> form("#runner-architecture-#{runner.id}", %{"runner" => %{"architecture" => "aarch64"}})
    |> render_change()

    assert GitGud.Repo.reload!(runner).architecture == "aarch64"

    assert [_created, changed] = entries()
    assert {changed.action, changed.target_id} == {"runner.architecture_changed", runner.id}
    assert changed.metadata == %{"from" => "x86_64", "to" => "aarch64"}
    assert changed.repository_id == repo.id
  end

  test "a repository's: token minted, runner created and deleted", %{conn: conn} do
    {owner, repo} = repository_fixture()
    repo = GitGud.Repo.preload(repo, [:owner, :organization])

    {:ok, lv, _} =
      live(
        log_in_user(conn, owner),
        "/r/#{Storage.repo_handle(repo)}/#{repo.name}/settings/runners"
      )

    html = render_click(lv, "mint_token", %{})
    render_submit(lv, "create_runner", %{"name" => "fast", "labels" => "ubuntu-latest, docker"})
    runner = GitGud.Repo.get_by!(Runner, name: "fast")
    render_click(lv, "delete_runner", %{"id" => to_string(runner.id)})

    assert [minted, created, deleted] = entries()
    assert minted.action == "runner.registration_token_minted"
    assert Map.keys(minted.metadata) == ["expires_at"]
    assert {created.action, created.target_label} == {"runner.created", "fast"}
    assert created.metadata == %{"labels" => ["ubuntu-latest", "docker"]}
    assert {deleted.action, deleted.target_id} == {"runner.deleted", runner.id}
    assert Enum.all?(entries(), &(&1.repository_id == repo.id))

    # The minted token is on the page, once, and nowhere in the log.
    token = :sys.get_state(lv.pid).socket.assigns.new_token
    assert html =~ token
    refute inspect(entries()) =~ token
  end

  test "an org's", %{conn: conn} do
    admin = user_fixture()
    handle = "rn#{System.unique_integer([:positive])}"
    {:ok, org} = Organizations.create_organization(admin, %{"handle" => handle, "name" => handle})

    {:ok, lv, _} = live(log_in_user(conn, admin), ~p"/orgs/#{handle}/settings/runners")
    render_click(lv, "mint_token", %{})

    assert [%Entry{action: "runner.registration_token_minted", organization_id: oid}] = entries()
    assert oid == org.id
  end

  test "an org's: a runner's architecture changed, and only its own runners'", %{conn: conn} do
    admin = user_fixture()
    handle = "rn#{System.unique_integer([:positive])}"
    {:ok, org} = Organizations.create_organization(admin, %{"handle" => handle, "name" => handle})

    {:ok, own, _, _} =
      GitGud.Workflows.Runners.create_offline(%{name: "e52c"}, org)

    {:ok, global, _, _} = GitGud.Workflows.Runners.create_offline(%{name: "pool"})

    {:ok, lv, _} = live(log_in_user(conn, admin), ~p"/orgs/#{handle}/settings/runners")

    lv
    |> form("#runner-architecture-#{own.id}", %{"runner" => %{"architecture" => "aarch64"}})
    |> render_change()

    assert GitGud.Repo.reload!(own).architecture == "aarch64"
    assert [%Entry{action: "runner.architecture_changed", organization_id: oid}] = entries()
    assert oid == org.id

    # A global runner is listed, but not this page's to change.
    refute has_element?(lv, "#runner-architecture-#{global.id}")

    render_change(lv, "set_architecture", %{
      "runner" => %{"id" => to_string(global.id), "architecture" => "aarch64"}
    })

    assert GitGud.Repo.reload!(global).architecture == "x86_64"
  end

  test "the mix task, as the console, on the instance's log" do
    Mix.shell(Mix.Shell.Process)
    on_exit(fn -> Mix.shell(Mix.Shell.IO) end)

    Mix.Tasks.Gitgud.Runner.Create.run(~w(--scope global --name house))

    assert [entry] = entries()

    assert {entry.action, entry.actor_type, entry.actor_label} ==
             {"runner.created", "system", "console"}

    assert {entry.organization_id, entry.repository_id} == {nil, nil}
  end
end