fangorn/ex_git_objectstore
public
ref:main
# Copyright 2026 Cole Christensen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
defmodule ExGitObjectstoreTest do
use ExUnit.Case, async: true
alias ExGitObjectstore.Object.{Blob, Commit, Tree}
alias ExGitObjectstore.Test.RepoHelper
describe "init and exists?" do
test "new repo does not exist" do
repo = RepoHelper.memory_repo()
refute ExGitObjectstore.exists?(repo)
end
test "init creates repo with HEAD" do
repo = RepoHelper.memory_repo()
assert :ok = ExGitObjectstore.init(repo)
assert ExGitObjectstore.exists?(repo)
end
test "default_branch returns main after init" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
assert {:ok, "main"} = ExGitObjectstore.default_branch(repo)
end
end
describe "branch management" do
test "create and list branches" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
sha = String.duplicate("a", 40)
assert :ok = ExGitObjectstore.create_branch(repo, "feature", sha)
{:ok, branches} = ExGitObjectstore.branches(repo)
branch_names = Enum.map(branches, &elem(&1, 0))
assert "refs/heads/feature" in branch_names
end
test "delete branch" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
sha = String.duplicate("a", 40)
ExGitObjectstore.create_branch(repo, "feature", sha)
assert :ok = ExGitObjectstore.delete_branch(repo, "feature")
{:ok, branches} = ExGitObjectstore.branches(repo)
branch_names = Enum.map(branches, &elem(&1, 0))
refute "refs/heads/feature" in branch_names
end
end
describe "tag management" do
test "create and list tags" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
sha = String.duplicate("a", 40)
assert :ok = ExGitObjectstore.create_tag(repo, "v1.0", sha)
{:ok, tags} = ExGitObjectstore.tags(repo)
tag_names = Enum.map(tags, &elem(&1, 0))
assert "refs/tags/v1.0" in tag_names
end
test "delete tag" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
sha = String.duplicate("a", 40)
ExGitObjectstore.create_tag(repo, "v1.0", sha)
assert :ok = ExGitObjectstore.delete_tag(repo, "v1.0")
{:ok, tags} = ExGitObjectstore.tags(repo)
assert tags == []
end
end
describe "object read/write" do
test "write and read blob" do
repo = RepoHelper.memory_repo()
blob = Blob.from_content("hello")
{:ok, sha} = ExGitObjectstore.write_object(repo, blob)
{:ok, decoded} = ExGitObjectstore.cat_object(repo, sha)
assert %Blob{content: "hello"} = decoded
end
test "write and read tree with blob" do
repo = RepoHelper.memory_repo()
blob = Blob.from_content("content")
{:ok, blob_sha} = ExGitObjectstore.write_object(repo, blob)
tree = Tree.new([%{mode: "100644", name: "file.txt", sha: blob_sha}])
{:ok, tree_sha} = ExGitObjectstore.write_object(repo, tree)
{:ok, decoded} = ExGitObjectstore.cat_object(repo, tree_sha)
assert %Tree{entries: [entry]} = decoded
assert entry.sha == blob_sha
end
test "write full commit chain" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
# Create a blob
blob = Blob.from_content("hello world\n")
{:ok, blob_sha} = ExGitObjectstore.write_object(repo, blob)
# Create a tree
tree = Tree.new([%{mode: "100644", name: "README.md", sha: blob_sha}])
{:ok, tree_sha} = ExGitObjectstore.write_object(repo, tree)
# Create initial commit
commit = %Commit{
tree: tree_sha,
parents: [],
author: "Test <test@test.com> 1234567890 +0000",
committer: "Test <test@test.com> 1234567890 +0000",
message: "Initial commit\n"
}
{:ok, commit_sha} = ExGitObjectstore.write_object(repo, commit)
# Point main branch at commit
ExGitObjectstore.create_branch(repo, "main", commit_sha)
# Verify resolve
assert {:ok, ^commit_sha} = ExGitObjectstore.resolve(repo, "main")
# Read back the commit
{:ok, decoded} = ExGitObjectstore.cat_object(repo, commit_sha)
assert %Commit{} = decoded
assert decoded.tree == tree_sha
end
end
describe "resolve" do
test "resolve SHA returns SHA directly" do
repo = RepoHelper.memory_repo()
sha = String.duplicate("a", 40)
assert {:ok, ^sha} = ExGitObjectstore.resolve(repo, sha)
end
test "resolve branch name" do
repo = RepoHelper.memory_repo()
sha = String.duplicate("a", 40)
ExGitObjectstore.create_branch(repo, "main", sha)
assert {:ok, ^sha} = ExGitObjectstore.resolve(repo, "main")
end
test "resolve tag name" do
repo = RepoHelper.memory_repo()
sha = String.duplicate("a", 40)
ExGitObjectstore.create_tag(repo, "v1.0", sha)
assert {:ok, ^sha} = ExGitObjectstore.resolve(repo, "v1.0")
end
test "resolve HEAD" do
repo = RepoHelper.memory_repo()
sha = String.duplicate("a", 40)
ExGitObjectstore.init(repo)
ExGitObjectstore.create_branch(repo, "main", sha)
assert {:ok, ^sha} = ExGitObjectstore.resolve(repo, "HEAD")
end
test "resolve non-existent ref returns error" do
repo = RepoHelper.memory_repo()
assert {:error, :ref_not_found} = ExGitObjectstore.resolve(repo, "nonexistent")
end
end
end