4.6 KiB · text History b868907
defmodule GitGud.Packages.Fdroid.BackfillTest do
  @moduledoc """
  `Fdroid.backfill_icons/1` — the walk behind both
  `GitGud.Release.backfill_fdroid_icons/1`, which is what a release can
  call, and `mix gitgud.backfill_fdroid_icons`.

  The situation it exists for: an APK published before the adapter
  could read `resources.arsc` carries no icon and no label, and nothing
  re-reads an APK afterwards — so the app shows as its package id
  forever, or until it publishes again. These publish a file with the
  metadata that era produced and assert the task repairs it in place.
  """
  use GitGud.DataCase, async: false

  import GitGud.ForgeFixtures

  alias GitGud.ApkFixtures
  alias GitGud.Packages
  alias GitGud.Packages.Formats.Fdroid
  alias GitGud.Packages.PackageFile
  alias GitGud.Repo

  setup do
    {_user, repo} = repository_fixture()
    %{repo: repo}
  end

  # What a publish stored before icons and labels were read: the
  # manifest's own fields, and nothing from the resource table.
  defp publish_legacy(repo, version_code) do
    body = ApkFixtures.signed_apk()

    metadata = %{
      "package" => "org.neiam.gitgud.app",
      "version_code" => version_code,
      "version_name" => "0.1.0"
    }

    {:ok, _package, _version, file} =
      Packages.publish_file(repo, "fdroid", "org.neiam.gitgud.app", "0.1.0 (#{version_code})", %{
        filename: "org.neiam.gitgud.app_#{version_code}.apk",
        body: body,
        content_type: "application/vnd.android.package-archive",
        metadata: metadata,
        version_metadata: metadata
      })

    file
  end

  defp reload(file), do: Repo.get!(PackageFile, file.id)

  # Reports are collected rather than printed: the callback is what
  # both entry points render, so asserting on it tests the thing they
  # share instead of one of their two formatters.
  defp run(opts \\ []) do
    {:ok, reports} = Agent.start_link(fn -> [] end)

    on_file = fn report -> Agent.update(reports, &[report | &1]) end

    {read, written} = Fdroid.backfill_icons(Keyword.put(opts, :on_file, on_file))

    %{read: read, written: written, reports: Agent.get(reports, &Enum.reverse/1)}
  end

  test "fills in the label and the icon", %{repo: repo} do
    file = publish_legacy(repo, 224)

    refute Map.has_key?(file.metadata, "label")
    refute Map.has_key?(file.metadata, "icon_key")

    assert %{read: 1, written: 1, reports: [report]} = run()

    assert report.label == "GitGud"
    assert {_size, "png"} = report.icon

    metadata = reload(file).metadata

    assert metadata["label"] == "GitGud"
    assert metadata["icon_key"] =~ "fdroid-icons/"
    assert metadata["icon_ext"] == "png"
    assert metadata["icon_size"] == byte_size(ApkFixtures.icon_png())

    # And the icon is where the metadata says it is.
    assert {:ok, bytes} = GitGud.Storage.get(Packages.artifact_bucket(), metadata["icon_key"])
    assert bytes == ApkFixtures.icon_png()
  end

  test "adds a name and an icon and touches nothing else", %{repo: repo} do
    # The stored versionCode is 224; the APK's own manifest says 1. A
    # backfill that trusted the re-read would renumber a published
    # version, which the index orders by and the filename encodes.
    file = publish_legacy(repo, 224)

    run()

    metadata = reload(file).metadata

    assert metadata["package"] == "org.neiam.gitgud.app"
    assert metadata["version_code"] == 224
    assert metadata["version_name"] == "0.1.0"
    assert metadata["label"] == "GitGud"
  end

  test "reads only the newest APK of each app by default", %{repo: repo} do
    old = publish_legacy(repo, 1)
    new = publish_legacy(repo, 224)

    run()

    # The index takes an app's name and icon from its highest
    # versionCode, so the older builds are twenty APKs of storage read
    # to change nothing anyone can see.
    assert reload(new).metadata["label"] == "GitGud"
    refute Map.has_key?(reload(old).metadata, "label")
  end

  test "--all reads every version", %{repo: repo} do
    old = publish_legacy(repo, 1)
    new = publish_legacy(repo, 224)

    run(all: true)

    assert reload(old).metadata["label"] == "GitGud"
    assert reload(new).metadata["label"] == "GitGud"
  end

  test "--dry-run reports without writing", %{repo: repo} do
    file = publish_legacy(repo, 224)

    assert %{read: 1, written: 0, reports: [report]} = run(dry_run: true)

    assert report.label == "GitGud"
    refute report.written?
    refute Map.has_key?(reload(file).metadata, "label")
  end

  test "is safe to run twice", %{repo: repo} do
    file = publish_legacy(repo, 224)

    run()
    first = reload(file).metadata
    run()

    assert reload(file).metadata == first
  end
end