defmodule GitGud.Workflows.RunnerJwtTest do
use GitGud.DataCase, async: false
alias GitGud.Workflows.RunnerJwt
alias GitGud.Workflows.WorkflowJob
alias GitGud.Workflows.WorkflowRun
import GitGud.ForgeFixtures
@moduletag :git_required
setup do
if System.find_executable("git") == nil do
{:skip, "git not on PATH"}
else
{_user, repo} = repository_fixture()
run =
%WorkflowRun{repository_id: repo.id}
|> WorkflowRun.create_changeset(%{
number: 1,
workflow_path: ".github/workflows/ci.yml",
trigger: "push",
head_sha: :crypto.strong_rand_bytes(20)
})
|> GitGud.Repo.insert!()
job =
%WorkflowJob{workflow_run_id: run.id}
|> WorkflowJob.create_changeset(%{job_id: "build", definition: %{}})
|> GitGud.Repo.insert!()
{:ok, repo: repo, run: run, job: job}
end
end
test "mint + verify round-trip", %{job: job, run: run} do
token = RunnerJwt.mint(job, run)
assert is_binary(token)
assert {:ok, claims} = RunnerJwt.verify(token)
assert claims["tid"] == job.id
assert claims["rid"] == run.id
assert claims["aud"] == "runner"
assert claims["iss"] == "gitgud"
end
test "tampered token fails verify", %{job: job, run: run} do
token = RunnerJwt.mint(job, run)
[h, p, s] = String.split(token, ".")
# Flip one byte of the signature
tampered = h <> "." <> p <> "." <> String.slice(s, 0..-2//1) <> "X"
assert {:error, _} = RunnerJwt.verify(tampered)
end
test "expired token is rejected", %{job: job, run: run} do
# Swap the TTL to negative — generates a token that's instantly expired.
cfg = Application.get_env(:git_gud, RunnerJwt, [])
Application.put_env(:git_gud, RunnerJwt, Keyword.put(cfg, :ttl_seconds, -60))
on_exit(fn -> Application.put_env(:git_gud, RunnerJwt, cfg) end)
token = RunnerJwt.mint(job, run)
assert {:error, _} = RunnerJwt.verify(token)
end
test "garbage tokens fail verify cleanly" do
assert {:error, _} = RunnerJwt.verify("not.a.jwt")
assert {:error, _} = RunnerJwt.verify(nil)
assert {:error, _} = RunnerJwt.verify("")
end
end
2.1 KiB · text
5af55d9