@@ -1,0 +1,153 @@
# 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 ExGitObjectstore.TelemetryTest do
use ExUnit.Case, async: false
alias ExGitObjectstore.{Object, Ref}
alias ExGitObjectstore.Object.{Blob, Commit, Tree}
alias ExGitObjectstore.Test.RepoHelper
setup do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
%{repo: repo}
end
defp attach(event_name) do
test_pid = self()
handler_id = "test-#{inspect(event_name)}-#{System.unique_integer()}"
:telemetry.attach(
handler_id,
event_name,
fn event, measurements, metadata, _config ->
send(test_pid, {:telemetry_event, event, measurements, metadata})
end,
nil
)
on_exit(fn -> :telemetry.detach(handler_id) end)
end
describe "object write telemetry" do
test "emits start and stop events", %{repo: repo} do
attach([:ex_git_objectstore, :object, :write, :start])
attach([:ex_git_objectstore, :object, :write, :stop])
blob = Blob.from_content("telemetry test\n")
{:ok, sha} = Object.write(repo, blob)
assert_received {:telemetry_event, [:ex_git_objectstore, :object, :write, :start],
%{system_time: _}, %{object_type: :blob, repo_id: "test-repo"}}
assert_received {:telemetry_event, [:ex_git_objectstore, :object, :write, :stop],
%{duration: duration}, %{object_type: :blob, sha: ^sha}}
assert is_integer(duration) and duration >= 0
end
end
describe "object read telemetry" do
test "emits start and stop events", %{repo: repo} do
blob = Blob.from_content("read test\n")
{:ok, sha} = Object.write(repo, blob)
attach([:ex_git_objectstore, :object, :read, :start])
attach([:ex_git_objectstore, :object, :read, :stop])
{:ok, _} = Object.read(repo, sha)
assert_received {:telemetry_event, [:ex_git_objectstore, :object, :read, :start],
%{system_time: _}, %{sha: ^sha, repo_id: "test-repo"}}
assert_received {:telemetry_event, [:ex_git_objectstore, :object, :read, :stop],
%{duration: _}, %{sha: ^sha}}
end
end
describe "ref update telemetry" do
test "emits start and stop events", %{repo: repo} do
blob = Blob.from_content("ref test\n")
{:ok, blob_sha} = Object.write(repo, blob)
tree = Tree.new([%{mode: "100644", name: "f.txt", sha: blob_sha}])
{:ok, tree_sha} = Object.write(repo, tree)
commit = %Commit{
tree: tree_sha,
parents: [],
author: "T <t@t> 1 +0000",
committer: "T <t@t> 1 +0000",
message: "m\n"
}
{:ok, sha} = Object.write(repo, commit)
attach([:ex_git_objectstore, :ref, :update, :start])
attach([:ex_git_objectstore, :ref, :update, :stop])
:ok = Ref.put(repo, "refs/heads/main", sha, nil)
assert_received {:telemetry_event, [:ex_git_objectstore, :ref, :update, :start],
%{system_time: _},
%{ref_path: "refs/heads/main", repo_id: "test-repo", new_sha: ^sha}}
assert_received {:telemetry_event, [:ex_git_objectstore, :ref, :update, :stop],
%{duration: _}, %{ref_path: "refs/heads/main"}}
end
end
describe "protocol receive_pack telemetry" do
test "emits start and stop events on push", %{repo: repo} do
alias ExGitObjectstore.Protocol.{PktLine, ReceivePack}
blob = Blob.from_content("push test\n")
{:ok, blob_sha} = Object.write(repo, blob)
tree = Tree.new([%{mode: "100644", name: "f.txt", sha: blob_sha}])
{:ok, tree_sha} = Object.write(repo, tree)
commit = %Commit{
tree: tree_sha,
parents: [],
author: "T <t@t> 1 +0000",
committer: "T <t@t> 1 +0000",
message: "m\n"
}
{:ok, sha} = Object.write(repo, commit)
attach([:ex_git_objectstore, :protocol, :receive_pack, :start])
attach([:ex_git_objectstore, :protocol, :receive_pack, :stop])
zero = String.duplicate("0", 40)
{_advert, state} = ReceivePack.init(repo)
cmd = PktLine.encode("#{zero} #{sha} refs/heads/main\0report-status") <> PktLine.flush()
header = <<"PACK", 2::unsigned-big-32, 0::unsigned-big-32>>
checksum = :crypto.hash(:sha, header)
pack = header <> checksum
{_response, _state} = ReceivePack.feed(state, cmd <> pack)
assert_received {:telemetry_event, [:ex_git_objectstore, :protocol, :receive_pack, :start],
%{system_time: _}, %{repo_id: "test-repo", command_count: 1}}
assert_received {:telemetry_event, [:ex_git_objectstore, :protocol, :receive_pack, :stop],
%{duration: _}, %{repo_id: "test-repo", command_count: 1}}
end
end
end