Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
180 lines
6.4 KiB
Elixir
180 lines
6.4 KiB
Elixir
defmodule TarakanWeb.InfestationLiveTest do
|
|
use TarakanWeb.ConnCase, async: true
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Tarakan.FindingMemory
|
|
|
|
test "index and show render multi-repo patterns", %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
other = github_account_fixture()
|
|
repo_a = listed_github_repository_fixture(submitter)
|
|
{:ok, repo_b} = Tarakan.Repositories.register_github_repository("acme/widget", other)
|
|
repo_b = listed_repository_fixture(repo_b)
|
|
|
|
title = "Live infestation #{System.unique_integer([:positive])}"
|
|
|
|
findings = fn path ->
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => path,
|
|
"severity" => "critical",
|
|
"title" => title,
|
|
"description" => "Infestation liveview fixture."
|
|
}
|
|
]
|
|
})
|
|
end
|
|
|
|
scan_fixture(repo_a, submitter, %{"findings_json" => findings.("a.ex")})
|
|
scan_fixture(repo_b, other, %{"findings_json" => findings.("b.ex")})
|
|
# repo_a is hit twice by the same pattern at different paths, so the
|
|
# per-repo rollup and the per-finding ledger must disagree on row count.
|
|
scan_fixture(repo_a, submitter, %{"findings_json" => findings.("a2.ex")})
|
|
|
|
pattern = FindingMemory.pattern_key(title)
|
|
|
|
{:ok, index, html} = live(conn, ~p"/infestations")
|
|
assert html =~ "Infestations"
|
|
assert has_element?(index, "#infestations-constellation")
|
|
assert has_element?(index, "#infestation-#{pattern}")
|
|
|
|
{:ok, show, show_html} = live(conn, ~p"/infestations/#{pattern}")
|
|
assert show_html =~ title
|
|
assert has_element?(show, "#infestation-repo-count", "2")
|
|
assert has_element?(show, "#infestation-graph")
|
|
assert has_element?(show, "#infestation-instances")
|
|
|
|
# The repo table is a rollup: one row per repository, carrying the counts
|
|
# that distinguish it from the instance ledger below.
|
|
graph_rows = show |> element("#infestation-graph tbody") |> render()
|
|
assert graph_rows =~ "#{repo_a.owner}/#{repo_a.name}"
|
|
assert graph_rows =~ "#{repo_b.owner}/#{repo_b.name}"
|
|
|
|
row_a = show |> element("#infestation-graph-node-#{repo_a.id}") |> render() |> squish()
|
|
assert row_a =~ ">2</td>", "repo_a is hit twice and must report 2, not a blank cell"
|
|
|
|
row_b = show |> element("#infestation-graph-node-#{repo_b.id}") |> render() |> squish()
|
|
assert row_b =~ ">1</td>", "repo_b is hit once and must report 1"
|
|
|
|
# 2 repository rows, 3 instance rows: the two views are different grains.
|
|
ledger = show |> element("#infestation-ledger") |> render()
|
|
assert length(Regex.scan(~r/<li[^>]+id="ledger_instances-/, ledger)) == 3
|
|
end
|
|
|
|
describe "fix propagation" do
|
|
setup %{conn: conn} do
|
|
submitter = github_account_fixture()
|
|
other = github_account_fixture()
|
|
|
|
source = listed_github_repository_fixture(submitter)
|
|
{:ok, target} = Tarakan.Repositories.register_github_repository("acme/widget", other)
|
|
target = listed_repository_fixture(target)
|
|
|
|
title = "Propagatable finding #{System.unique_integer([:positive])}"
|
|
|
|
findings =
|
|
Jason.encode!(%{
|
|
"tarakan_scan_format" => 1,
|
|
"findings" => [
|
|
%{
|
|
"file" => "lib/handler.ex",
|
|
"severity" => "critical",
|
|
"title" => title,
|
|
"description" => "Shared across both repositories."
|
|
}
|
|
]
|
|
})
|
|
|
|
source_scan = scan_fixture(source, submitter, %{"findings_json" => findings})
|
|
scan_fixture(target, other, %{"findings_json" => findings})
|
|
|
|
# Settle the source instance as fixed so a template can be captured.
|
|
[occurrence] = source_scan.findings
|
|
|
|
source_finding =
|
|
Tarakan.Repo.get!(Tarakan.Scans.CanonicalFinding, occurrence.canonical_finding_id)
|
|
|> Ecto.Changeset.change(%{
|
|
status: "fixed",
|
|
fixed_at: DateTime.utc_now(),
|
|
fixed_commit_sha: source_scan.commit_sha
|
|
})
|
|
|> Tarakan.Repo.update!()
|
|
|
|
%Tarakan.Scans.FindingCheck{}
|
|
|> Tarakan.Scans.FindingCheck.changeset(%{
|
|
commit_sha: source_scan.commit_sha,
|
|
verdict: "fixed",
|
|
provenance: "human",
|
|
notes: "Escaped the interpolated value and added a regression test."
|
|
})
|
|
|> Ecto.Changeset.put_change(:canonical_finding_id, source_finding.id)
|
|
|> Ecto.Changeset.put_change(:account_id, submitter.id)
|
|
|> Tarakan.Repo.insert!()
|
|
|
|
moderator = moderator_account_fixture()
|
|
|
|
{:ok, template} =
|
|
Tarakan.Fixes.capture_fix(
|
|
Tarakan.Accounts.Scope.for_account(moderator),
|
|
source_finding
|
|
)
|
|
|
|
%{
|
|
conn: log_in_account(conn, moderator),
|
|
pattern: FindingMemory.pattern_key(title),
|
|
source: source,
|
|
target: target,
|
|
template: template
|
|
}
|
|
end
|
|
|
|
test "shows the captured fix and carries it to the remaining repository", %{
|
|
conn: conn,
|
|
pattern: pattern,
|
|
source: source,
|
|
target: target,
|
|
template: template
|
|
} do
|
|
{:ok, view, html} = live(conn, ~p"/infestations/#{pattern}")
|
|
|
|
assert html =~ "Fixed on"
|
|
assert html =~ "#{source.owner}/#{source.name}"
|
|
assert html =~ "Escaped the interpolated value"
|
|
assert has_element?(view, "#infestation-propagate-fix", "Propagate to 1")
|
|
|
|
html = view |> element("#infestation-propagate-fix") |> render_click()
|
|
|
|
assert html =~ "Opened 1 fix job"
|
|
assert html =~ "carried to 1 repo"
|
|
|
|
# A real, public job now exists on the other repository, carrying the fix.
|
|
# Scan submission auto-opens a verification job too, so reach the fix job
|
|
# through the propagation rather than by repository alone.
|
|
assert [propagation] = Tarakan.Fixes.list_propagations(template)
|
|
assert propagation.repository_id == target.id
|
|
|
|
task = propagation.review_task
|
|
assert task.status == "open"
|
|
assert task.visibility == "public"
|
|
assert task.description =~ "Escaped the interpolated value"
|
|
end
|
|
|
|
test "anonymous visitors see the fix but cannot propagate it", %{pattern: pattern} do
|
|
{:ok, view, html} = live(build_conn(), ~p"/infestations/#{pattern}")
|
|
|
|
assert html =~ "Fixed on"
|
|
refute has_element?(view, "#infestation-propagate-fix")
|
|
end
|
|
end
|
|
|
|
# Collapse the whitespace HEEx leaves around interpolated cell values so
|
|
# assertions can match on the value itself.
|
|
defp squish(html) do
|
|
html
|
|
|> String.replace(~r/>\s+/, ">")
|
|
|> String.replace(~r/\s+</, "<")
|
|
end
|
|
end
|