3.6 KiB · text 5af55d9
defmodule GitGud.GitNifTest do
@moduledoc """
Exercises the gix NIF and asserts parity with the Cli backend for
operations implemented on both sides.
Skipped if `git` isn't on PATH (we still need it to populate the
fixture repo).
"""
use ExUnit.Case, async: true
alias GitGud.Git
@moduletag :git_required
setup_all do
:ok
end
setup do
if System.find_executable("git") == nil do
{:skip, "git not on PATH"}
else
dir = Path.join(System.tmp_dir!(), "gixparity-#{System.unique_integer([:positive])}")
File.mkdir_p!(dir)
on_exit(fn -> _ = File.rm_rf(dir) end)
{:ok, dir: dir}
end
end
test "primary backend defaults to Nif when the crate is loaded" do
if GitGud.Git.Nif.available?() do
assert Git.current_backend() == GitGud.Git.Nif
else
assert Git.current_backend() == GitGud.Git.Cli
end
end
test "init_bare via the facade works", %{dir: dir} do
assert :ok = Git.init_bare(dir)
assert File.exists?(Path.join(dir, "HEAD"))
end
test "blob round-trip via NIF (with Cli fallback for unsupported ops)", %{dir: dir} do
:ok = Git.init_bare(dir)
# Seed with the Cli backend (uses `git` CLI for hash-object).
tmp = Path.join(System.tmp_dir!(), "blob-#{System.unique_integer([:positive])}")
File.write!(tmp, "hello\n")
{hex, 0} = System.cmd("git", ["-C", dir, "hash-object", "-w", tmp])
File.rm(tmp)
{:ok, blob_sha} = Git.from_hex(String.trim(hex))
assert {:ok, %{size: 6, is_binary: false}} = Git.blob_meta(dir, blob_sha)
assert {:ok, "hello\n"} = Git.blob_bytes(dir, blob_sha)
end
test "list_refs via NIF + Cli parity", %{dir: dir} do
:ok = Git.init_bare(dir)
# Create a blob + tree + commit + ref via Cli plumbing.
tmp = Path.join(System.tmp_dir!(), "blob-#{System.unique_integer([:positive])}")
File.write!(tmp, "x\n")
{hex, 0} = System.cmd("git", ["-C", dir, "hash-object", "-w", tmp])
File.rm(tmp)
{:ok, blob_sha} = Git.from_hex(String.trim(hex))
index = Path.join(System.tmp_dir!(), "idx-#{System.unique_integer([:positive])}")
env = [{"GIT_INDEX_FILE", index}]
{_, 0} =
System.cmd(
"git",
["-C", dir, "update-index", "--add", "--cacheinfo", "100644,#{Git.to_hex(blob_sha)},x"],
env: env
)
{tree_hex, 0} = System.cmd("git", ["-C", dir, "write-tree"], env: env)
File.rm(index)
{:ok, tree_sha} = Git.from_hex(String.trim(tree_hex))
{:ok, commit_sha} =
Git.commit_tree(dir, tree_sha, [], "init", author: {"t", "t@t", DateTime.utc_now(:second)})
:ok = Git.update_ref(dir, "refs/heads/main", commit_sha, nil)
# Compare facade output (whichever backend is primary) against the
# explicit Cli result. They must match — the NIF's list_refs is the
# only currently-fully-NIF-implemented read path, so this checks
# that the encoding matches the Cli backend's shape.
{:ok, facade_refs} = Git.list_refs(dir)
{:ok, cli_refs} = GitGud.Git.Cli.list_refs(dir)
assert Enum.sort_by(facade_refs, & &1.name) == Enum.sort_by(cli_refs, & &1.name)
end
test "unsupported NIF ops transparently fall through to Cli", %{dir: dir} do
:ok = Git.init_bare(dir)
# log/3 isn't yet implemented in gix; facade should fall back.
# On an empty repo there's no HEAD, so resolve will fail — that's
# fine; we're checking that the dispatch DOESN'T raise on
# `{:error, :unsupported}`.
blob_sha = :crypto.strong_rand_bytes(20)
result = Git.log(dir, blob_sha, limit: 1)
# Either {:ok, []} or a Cli-shaped error — never :unsupported
# leaking through.
refute match?({:error, :unsupported}, result)
end
end