Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205defmodule GitSync.Forgejo.ClientTest do use ExUnit.Case, async: true
alias GitSync.Connection alias GitSync.Forgejo.Client
@connection %Connection{base_url: "https://codeberg.org/", token: "tok"}
defp stub(fun), do: Req.Test.stub(GitSync.Http, fun)
test "lists the operator's repositories" do stub(fn conn -> assert conn.request_path == "/api/v1/user/repos" assert ["Bearer tok"] = Plug.Conn.get_req_header(conn, "authorization")
Req.Test.json(conn, [ %{ "full_name" => "adam/git-sync", "clone_url" => "https://codeberg.org/adam/git-sync.git", "private" => false } ]) end)
assert {:ok, [%{full_name: "adam/git-sync", private: false}]} = Client.list_repos(@connection) end
test "follows pagination until a short page comes back" do stub(fn conn -> conn = Plug.Conn.fetch_query_params(conn) assert conn.query_params["limit"] == "50"
case conn.query_params["page"] do "1" -> Req.Test.json(conn, Enum.map(1..50, &forgejo_repo("adam/repo-#{&1}"))) "2" -> Req.Test.json(conn, [forgejo_repo("adam/zulu")]) end end)
assert {:ok, repos} = Client.list_repos(@connection) assert length(repos) == 51 assert List.last(repos).full_name == "adam/zulu" end
test "omits archived repositories" do stub(fn conn -> Req.Test.json(conn, [ forgejo_repo("adam/git-sync"), Map.put(forgejo_repo("adam/retired"), "archived", true) ]) end)
assert {:ok, [%{full_name: "adam/git-sync"}]} = Client.list_repos(@connection) end
test "reports a connection that has not been authorized yet" do assert {:error, "Connect Forgejo to list its repositories"} = Client.list_repos(%Connection{base_url: "https://codeberg.org/"}) end
test "reports an unauthorized response" do stub(fn conn -> Plug.Conn.send_resp(conn, 401, "") end)
assert {:error, "Forgejo returned HTTP 401"} = Client.list_repos(@connection) end
defp forgejo_repo(full_name) do %{ "full_name" => full_name, "clone_url" => "https://codeberg.org/#{full_name}.git", "private" => false, "archived" => false } end
describe "clone_url/3" do test "joins the repository onto the instance base url" do assert Client.clone_url(@connection, "adam/git-sync", :read) == "https://codeberg.org/adam/git-sync" end end
describe "create_webhook/4" do test "registers push and branch lifecycle events on the repository" do stub(fn conn -> assert conn.method == "POST" assert conn.request_path == "/api/v1/repos/adam/git-sync/hooks" {:ok, body, conn} = Plug.Conn.read_body(conn)
assert %{ "type" => "forgejo", "events" => ["push", "create", "delete"], "config" => %{"url" => "https://sync.example/hooks/1", "secret" => "shh"} } = JSON.decode!(body)
conn |> Plug.Conn.put_status(201) |> Req.Test.json(%{"id" => 7}) end)
assert {:ok, 7} = Client.create_webhook( @connection, "adam/git-sync", "https://sync.example/hooks/1", "shh" ) end
test "reports a rejected registration" do stub(fn conn -> Plug.Conn.send_resp(conn, 403, "") end)
assert {:error, "Forgejo returned HTTP 403"} = Client.create_webhook( @connection, "adam/git-sync", "https://sync.example/hooks/1", "shh" ) end end
describe "reconcile_webhook/3" do test "adds missing branch lifecycle events to an existing hook" do stub(fn conn -> assert conn.request_path == "/api/v1/repos/adam/git-sync/hooks/7"
case conn.method do "GET" -> Req.Test.json(conn, %{"events" => ["push"]})
"PATCH" -> {:ok, body, conn} = Plug.Conn.read_body(conn) assert JSON.decode!(body) == %{"events" => ["push", "create", "delete"]} Req.Test.json(conn, %{"id" => 7}) end end)
assert :ok = Client.reconcile_webhook(@connection, "adam/git-sync", "7") end
test "can repeatedly reconcile a complete hook without modifying it" do stub(fn conn -> assert conn.method == "GET" Req.Test.json(conn, %{"events" => ["push", "create", "delete"]}) end)
assert :ok = Client.reconcile_webhook(@connection, "adam/git-sync", "7") assert :ok = Client.reconcile_webhook(@connection, "adam/git-sync", "7") end end
describe "verify_webhook/4" do test "accepts a body matching the signature header" do body = ~s({"ref":"refs/heads/main"}) headers = [{"x-forgejo-signature", signature(body, "shh")}]
assert :ok = Client.verify_webhook(@connection, headers, body, "shh") end
test "accepts the gitea-compatible header" do body = ~s({"ref":"refs/heads/main"}) headers = [{"x-gitea-signature", signature(body, "shh")}]
assert :ok = Client.verify_webhook(@connection, headers, body, "shh") end
test "rejects a body signed with another secret" do body = ~s({"ref":"refs/heads/main"}) headers = [{"x-forgejo-signature", signature(body, "other")}]
assert {:error, :invalid_signature} = Client.verify_webhook(@connection, headers, body, "shh") end
test "rejects an unsigned delivery" do assert {:error, :missing_signature} = Client.verify_webhook(@connection, [], "{}", "shh") end end
defp signature(body, secret), do: Base.encode16(:crypto.mac(:hmac, :sha256, secret, body), case: :lower)
describe "check/1" do test "asks for the account behind the token and nothing more" do stub(fn conn -> assert conn.request_path == "/api/v1/user" assert ["Bearer tok"] = Plug.Conn.get_req_header(conn, "authorization")
Req.Test.json(conn, %{"login" => "adam"}) end)
assert Client.check(@connection) == :ok end
test "reports the status when the account cannot be read" do stub(&Plug.Conn.send_resp(&1, 401, ""))
assert Client.check(@connection) == {:error, "Forgejo returned HTTP 401"} end
test "says so when there is no token to check with" do assert {:error, message} = Client.check(%Connection{base_url: "https://codeberg.org"}) assert message =~ "Connect Forgejo" end endend