defmodule GitGudWeb.RawRegistryTest do
@moduledoc """
The raw registry, over HTTP.
The client this format exists for is a microcontroller with a few
hundred lines of C and no package manager, so the assertions are the
ones such a client depends on and cannot work around: the manifest
names a `latest`, every file carries a URL that can be fetched
verbatim and a SHA-256 that matches the bytes on the other end, and
published bytes never change under a filename that was already served.
"""
use GitGudWeb.ConnCase, async: false
alias GitGud.Packages
alias GitGud.Repositories.Storage
import GitGud.ForgeFixtures
@firmware <<0xE9, 0x06, 0x02, 0x1F>> <> :crypto.strong_rand_bytes(1024)
setup do
{user, repo} = repository_fixture(%{visibility: "public"})
{:ok, _t, token} = Packages.create_registry_token(repo, "ci", ["pull", "push"])
{:ok,
user: user,
repo: repo,
token: token,
root: "/pkg/raw/#{Storage.owner_handle(user)}/#{repo.name}"}
end
defp publish(conn, root, token, version, filename, body) do
conn = if token, do: put_req_header(conn, "authorization", token), else: conn
conn
|> put_req_header("content-type", "application/octet-stream")
|> put("#{root}/#{URI.encode(version, &URI.char_unreserved?/1)}/#{filename}", body)
end
defp manifest(conn, root), do: Jason.decode!(get(conn, root <> "/manifest.json").resp_body)
describe "publishing" do
test "stores the bytes and reports the digest", %{conn: conn, root: root, token: token} do
conn = publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
assert conn.status == 200
body = Jason.decode!(conn.resp_body)
assert body["version"] == "1.0.0"
assert body["filename"] == "firmware.bin"
assert body["size"] == byte_size(@firmware)
assert body["sha256"] == Base.encode16(:crypto.hash(:sha256, @firmware), case: :lower)
end
test "needs a token", %{conn: conn, root: root} do
assert publish(conn, root, nil, "1.0.0", "firmware.bin", @firmware).status == 401
end
test "the same bytes twice is a no-op, not a conflict", %{
conn: conn,
root: root,
token: token
} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
assert publish(build_conn(), root, token, "1.0.0", "firmware.bin", @firmware).status == 200
end
test "different bytes under a published filename are refused", %{
conn: conn,
root: root,
token: token
} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
conn = publish(build_conn(), root, token, "1.0.0", "firmware.bin", @firmware <> "more")
assert conn.status == 409
end
test "'latest' is reserved in the version slot", %{conn: conn, root: root, token: token} do
conn = publish(conn, root, token, "latest", "firmware.bin", @firmware)
assert conn.status == 400
assert conn.resp_body =~ "reserved"
end
test "an empty body is refused", %{conn: conn, root: root, token: token} do
assert publish(conn, root, token, "1.0.0", "firmware.bin", "").status == 400
end
end
describe "the manifest" do
test "is empty but well-formed before anything is published", %{conn: conn, root: root} do
body = manifest(conn, root)
assert body["latest"] == nil
assert body["versions"] == %{}
end
test "lists every version, with a fetchable url and a checksum", %{
conn: conn,
root: root,
token: token,
user: user,
repo: repo
} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
body = manifest(build_conn(), root)
assert body["name"] == repo.name
assert body["latest"] == "1.0.0"
version = body["versions"]["1.0.0"]
refute version["yanked"]
assert version["published_at"] =~ ~r/^\d{4}-/
assert [file] = version["files"]
assert file["name"] == "firmware.bin"
assert file["size"] == byte_size(@firmware)
assert file["sha256"] == Base.encode16(:crypto.hash(:sha256, @firmware), case: :lower)
assert file["content_type"] == "application/octet-stream"
# The URL is the contract: a device follows it verbatim.
handle = Storage.owner_handle(user)
assert file["url"] =~ "/pkg/raw/#{handle}/#{repo.name}/1.0.0/firmware.bin"
assert %URI{path: path} = URI.parse(file["url"])
assert get(build_conn(), path).resp_body == @firmware
end
test "'latest' is the most recently published, not the highest string", %{
conn: conn,
root: root,
token: token
} do
publish(conn, root, token, "2.0.0", "firmware.bin", @firmware)
publish(build_conn(), root, token, "1.0.0", "firmware.bin", @firmware <> "older")
assert manifest(build_conn(), root)["latest"] == "1.0.0"
end
test "skips a yanked version when choosing latest", %{
conn: conn,
root: root,
token: token,
repo: repo
} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
publish(build_conn(), root, token, "1.1.0", "firmware.bin", @firmware <> "bad")
package = Packages.get_package(repo, "raw", repo.name)
{:ok, _} = package |> Packages.get_version("1.1.0") |> Packages.set_yanked(true, "bricks")
body = manifest(build_conn(), root)
assert body["latest"] == "1.0.0"
assert body["versions"]["1.1.0"]["yanked"]
end
test "is served at the registry root as well", %{conn: conn, root: root, token: token} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
assert Jason.decode!(get(build_conn(), root).resp_body)["latest"] == "1.0.0"
end
test "is not cached, or a fleet never sees an update", %{conn: conn, root: root} do
conn = get(conn, root <> "/manifest.json")
assert get_resp_header(conn, "cache-control") == ["no-cache"]
end
end
describe "downloads" do
setup %{conn: conn, root: root, token: token} do
publish(conn, root, token, "1.0.0", "firmware.bin", @firmware)
:ok
end
test "a public repository serves bytes to anyone", %{root: root} do
conn = get(build_conn(), root <> "/1.0.0/firmware.bin")
assert conn.status == 200
assert conn.resp_body == @firmware
end
test "'latest' resolves without the client knowing a version", %{root: root, token: token} do
publish(build_conn(), root, token, "1.1.0", "firmware.bin", @firmware <> "newer")
assert get(build_conn(), root <> "/latest/firmware.bin").resp_body == @firmware <> "newer"
end
test "an unknown version or filename is a 404", %{root: root} do
assert get(build_conn(), root <> "/9.9.9/firmware.bin").status == 404
assert get(build_conn(), root <> "/1.0.0/nothing.bin").status == 404
end
test "a version with a build-metadata '+' round-trips", %{root: root, token: token} do
version = "0.1.0+ci20260916.g1a2b3c4"
publish(build_conn(), root, token, version, "firmware.bin", @firmware <> "ci")
[file] = manifest(build_conn(), root)["versions"][version]["files"]
assert %URI{path: path} = URI.parse(file["url"])
assert get(build_conn(), path).resp_body == @firmware <> "ci"
end
end
describe "a private repository" do
test "challenges an anonymous reader", %{} do
{user, private} = repository_fixture(%{visibility: "private"})
root = "/pkg/raw/#{Storage.owner_handle(user)}/#{private.name}"
conn = get(build_conn(), root <> "/manifest.json")
assert conn.status == 401
assert [_challenge] = get_resp_header(conn, "www-authenticate")
end
test "serves the manifest to a token", %{} do
{user, private} = repository_fixture(%{visibility: "private"})
{:ok, _t, token} = Packages.create_registry_token(private, "ci", ["pull"])
root = "/pkg/raw/#{Storage.owner_handle(user)}/#{private.name}"
conn =
build_conn()
|> put_req_header("authorization", token)
|> get(root <> "/manifest.json")
assert conn.status == 200
end
end
end
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
7.9 KiB · text
History
b0551cd