7.2 KiB · text History b868907
defmodule GitGud.PackagesSubstrateTest do
  @moduledoc """
  Phase 0: the parts every registry format sits on, exercised without
  any format adapter in play.
  """
  use GitGud.DataCase, async: true

  alias GitGud.Packages
  alias GitGud.Packages.Auth
  alias GitGud.Packages.Format

  import GitGud.ForgeFixtures

  setup do
    {user, repo} = repository_fixture()
    {:ok, user: user, repo: repo}
  end

  describe "publish_file/5" do
    test "creates the package, version and file rows", %{repo: repo, user: user} do
      assert {:ok, pkg, ver, file} =
               Packages.publish_file(repo, "pypi", "Widget", "1.0.0", %{
                 filename: "widget-1.0.0.tar.gz",
                 body: "sdist bytes",
                 content_type: "application/x-tar",
                 uploader: user
               })

      assert pkg.format == "pypi"
      assert pkg.name == "Widget"
      assert pkg.latest_version == "1.0.0"
      assert ver.version == "1.0.0"
      assert file.size == byte_size("sdist bytes")

      assert file.sha256 ==
               Base.encode16(:crypto.hash(:sha256, "sdist bytes"), case: :lower)

      assert {:ok, "sdist bytes"} = Packages.read_file(file)
    end

    test "a second file joins the same version", %{repo: repo} do
      {:ok, _, _, _} =
        Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
          filename: "widget-1.0.0.tar.gz",
          body: "sdist"
        })

      {:ok, pkg, ver, _} =
        Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
          filename: "widget-1.0.0-py3-none-any.whl",
          body: "wheel"
        })

      assert [_, _] = Packages.list_files(ver)
      assert [ver_only] = Packages.list_versions(pkg)
      assert ver_only.id == ver.id
    end

    test "re-uploading identical bytes is a no-op, not a conflict", %{repo: repo} do
      attrs = %{filename: "widget-1.0.0.tar.gz", body: "sdist"}
      {:ok, _, _, first} = Packages.publish_file(repo, "pypi", "widget", "1.0.0", attrs)
      {:ok, _, _, second} = Packages.publish_file(repo, "pypi", "widget", "1.0.0", attrs)

      assert first.id == second.id
    end

    test "a published filename can never point at different bytes", %{repo: repo} do
      {:ok, _, _, _} =
        Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
          filename: "widget-1.0.0.tar.gz",
          body: "sdist"
        })

      assert {:error, :file_exists} =
               Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
                 filename: "widget-1.0.0.tar.gz",
                 body: "different sdist"
               })
    end

    test "the same name under two formats is two packages", %{repo: repo} do
      {:ok, pypi, _, _} =
        Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
          filename: "a.tar.gz",
          body: "a"
        })

      {:ok, cargo, _, _} =
        Packages.publish_file(repo, "cargo", "widget", "1.0.0", %{
          filename: "b.crate",
          body: "b"
        })

      refute pypi.id == cargo.id
      assert [^pypi] = Packages.list_packages(repo, "pypi")
      assert [^cargo] = Packages.list_packages(repo, "cargo")
    end
  end

  describe "lookup" do
    test "goes through the normalized name", %{repo: repo} do
      {:ok, pkg, _, _} =
        Packages.publish_file(repo, "cargo", "Serde", "1.0.0", %{
          filename: "serde-1.0.0.crate",
          body: "crate"
        })

      assert Packages.get_package(repo, "cargo", "serde").id == pkg.id
      assert Packages.get_package(repo, "cargo", "SERDE").id == pkg.id
      assert Packages.get_package(repo, "cargo", "nope") == nil
    end

    test "list_package_files/1 flattens versions into downloadables", %{repo: repo} do
      for v <- ["1.0.0", "1.1.0"] do
        {:ok, _, _, _} =
          Packages.publish_file(repo, "pypi", "widget", v, %{
            filename: "widget-#{v}.tar.gz",
            body: "sdist #{v}"
          })
      end

      pkg = Packages.get_package(repo, "pypi", "widget")
      files = Packages.list_package_files(pkg)

      assert length(files) == 2
      assert Enum.all?(files, &(&1.package_version.version in ["1.0.0", "1.1.0"]))
    end
  end

  describe "yanking" do
    test "flags the version without removing its bytes", %{repo: repo} do
      {:ok, pkg, ver, file} =
        Packages.publish_file(repo, "pypi", "widget", "1.0.0", %{
          filename: "widget-1.0.0.tar.gz",
          body: "sdist"
        })

      {:ok, _} = Packages.set_yanked(ver, true, "broken metadata")

      assert %{yanked: true, yanked_reason: "broken metadata"} =
               Packages.get_version(pkg, "1.0.0")

      assert {:ok, "sdist"} = Packages.read_file(file)
    end
  end

  describe "Format" do
    test "an unregistered format has no adapter" do
      assert Format.adapter("oci") == :error
      assert Format.adapter("nonsense") == :error
    end

    test "normalization falls back to case folding" do
      assert Format.normalize("oci", "Foo.Bar") == "foo.bar"
    end
  end

  describe "Auth" do
    test "a registry token authenticates and carries its own scopes", %{repo: repo} do
      {:ok, _token, plaintext} = Packages.create_registry_token(repo, "ci", ["pull", "push"])

      conn = conn_with_auth("Basic " <> Base.encode64("__token__:#{plaintext}"))

      assert {:token, repo_id, scopes} = Auth.authenticate(conn)
      assert repo_id == repo.id
      assert "push" in scopes
    end

    test "a bare Authorization value works too — mix and cargo send that", %{repo: repo} do
      {:ok, _token, plaintext} = Packages.create_registry_token(repo, "ci", ["pull"])

      assert {:token, _, ["pull"]} = Auth.authenticate(conn_with_auth(plaintext))
    end

    test "a pull-only token may not write", %{repo: repo} do
      {:ok, _token, plaintext} = Packages.create_registry_token(repo, "ro", ["pull"])
      principal = Auth.authenticate(conn_with_auth(plaintext))

      assert Auth.authorize(principal, repo, :read)
      refute Auth.authorize(principal, repo, :write)
    end

    test "a token minted for another repo is worth no more than anonymous", %{repo: repo} do
      {_other_user, other_repo} = repository_fixture()
      {:ok, _t, plaintext} = Packages.create_registry_token(other_repo, "ci", ["pull", "push"])

      principal = Auth.authenticate(conn_with_auth(plaintext))

      refute Auth.authorize(principal, %{repo | visibility: "private"}, :read)
      refute Auth.authorize(principal, repo, :write)
    end

    test "anonymous reads public repositories only", %{repo: repo} do
      assert Auth.authorize(:anonymous, %{repo | visibility: "public"}, :read)
      refute Auth.authorize(:anonymous, %{repo | visibility: "internal"}, :read)
      refute Auth.authorize(:anonymous, %{repo | visibility: "private"}, :read)
      refute Auth.authorize(:anonymous, %{repo | visibility: "public"}, :write)
    end

    test "the owner may write", %{repo: repo, user: user} do
      assert Auth.authorize({:user, user, :all}, repo, :write)
    end

    test "no credentials at all is anonymous" do
      assert Auth.authenticate(Plug.Test.conn(:get, "/")) == :anonymous
      assert Auth.authenticate(conn_with_auth("Basic not-base64")) == :anonymous
    end
  end

  defp conn_with_auth(value) do
    Plug.Test.conn(:get, "/") |> Plug.Conn.put_req_header("authorization", value)
  end
end