crates/gunnar-server/tests/log_all_refs.rs
//! **`Refs.Log` from every ref tip, and from a named branch.**
//!
//! The History page walked one start (the default branch), so a commit that
//! lives only on a side branch never showed, while its ref chip did. Owner
//! order 2026-10-18: History gets a branch picker and an "all branches" view,
//! and the ref walk is the server's job. This drives `CoreControlPlane` on a
//! real store with three refs and asserts the EXACT oids each walk returns:
//!
//! ```text
//! S (t=3000) refs/heads/side — only on side
//! M (t=2000) refs/heads/main, HEAD
//! T (t=1500) refs/tags/v1 — only on the tag
//! R (t=1000) the root, shared
//! ```
use std::sync::Arc;
use gix_object::Kind;
use gunnar_core::{HashKind, NewRepo, RepoRef};
use gunnar_grpc::ControlPlane as _;
use gunnar_proto::RepoPath;
use gunnar_server::control_plane::CoreControlPlane;
use gunnar_server::stores::ServerStores;
use gunnar_store::conformance::pack::PackBuilder;
use gunnar_store::git_storage_trait::GitServe;
type Oid = gix_hash::ObjectId;
fn compute(hash: gix_hash::Kind, kind: Kind, content: &[u8]) -> Oid {
gix_object::compute_hash(hash, kind, content).expect("hashing an object")
}
fn commit_bytes(tree: Oid, parents: &[Oid], when: i64, message: &str) -> Vec<u8> {
let mut body = format!("tree {}\n", tree.to_hex());
for parent in parents {
body.push_str(&format!("parent {}\n", parent.to_hex()));
}
let who = format!("Alice <alice@example.com> {when} +0000");
body.push_str(&format!("author {who}\n"));
body.push_str(&format!("committer {who}\n\n"));
body.push_str(message);
body.push('\n');
body.into_bytes()
}
struct Planted {
root: Oid,
main: Oid,
side: Oid,
tag: Oid,
}
fn plant(git: &dyn GitServe, hash: gix_hash::Kind) -> Planted {
// One empty tree serves every commit: the walk is about topology.
let tree = Vec::new();
let tree_id = compute(hash, Kind::Tree, &tree);
let r = commit_bytes(tree_id, &[], 1000, "root");
let root = compute(hash, Kind::Commit, &r);
let m = commit_bytes(tree_id, &[root], 2000, "on main");
let main = compute(hash, Kind::Commit, &m);
let s = commit_bytes(tree_id, &[root], 3000, "only on side");
let side = compute(hash, Kind::Commit, &s);
let t = commit_bytes(tree_id, &[root], 1500, "only on the tag");
let tag = compute(hash, Kind::Commit, &t);
let mut pb = PackBuilder::new(hash);
pb.object(Kind::Tree, tree);
for body in [r, m, s, t] {
pb.object(Kind::Commit, body);
}
let fixture = pb.build().expect("building the pack");
git.put_pack(&fixture.bytes).expect("storing the pack");
git.update_ref("refs/heads/main", None, Some(main.as_bytes()))
.expect("main");
git.update_ref("refs/heads/side", None, Some(side.as_bytes()))
.expect("side");
git.update_ref("refs/tags/v1", None, Some(tag.as_bytes()))
.expect("v1");
git.set_head("refs/heads/main").expect("HEAD → main");
Planted {
root,
main,
side,
tag,
}
}
fn oids(commits: &[gunnar_proto::Commit]) -> Vec<String> {
commits.iter().map(|c| c.oid.clone()).collect()
}
fn world() -> (tempfile::TempDir, CoreControlPlane, RepoPath, Planted) {
let dir = tempfile::tempdir().unwrap();
let stores = Arc::new(ServerStores::open(dir.path()).expect("stores"));
stores
.catalog()
.create_namespace("alice")
.expect("namespace");
let repo_ref = RepoRef::new("alice", "branches").expect("legal repo path");
let repo = stores
.create_repo(NewRepo::new(repo_ref).hash_kind(HashKind::Sha256), false)
.expect("create repo");
let store = stores.repo_store(&repo).expect("open repo store");
let planted = plant(store.serve(), store.hash_kind());
drop(store);
let plane = CoreControlPlane::new(Arc::clone(&stores));
(dir, plane, RepoPath::new("alice", "branches"), planted)
}
#[test]
fn the_default_walk_misses_the_side_branch_and_all_refs_finds_it() {
let (_dir, plane, path, p) = world();
// Today's walk: the default branch only.
let default = plane.log(&path, "", 100, "").expect("default log");
assert_eq!(
oids(&default),
vec![p.main.to_string(), p.root.to_string()],
"an empty start walks HEAD (main) only"
);
// Every ref tip at once, newest committer time first, the shared root once.
let all = plane.log_all_refs(&path, 100, "").expect("all-refs log");
assert_eq!(
oids(&all),
vec![
p.side.to_string(),
p.main.to_string(),
p.tag.to_string(),
p.root.to_string()
],
"all_refs must show the side-branch and tag-only commits, root once"
);
// Paged like a single-start walk: the limit caps the page.
let page = plane.log_all_refs(&path, 2, "").expect("a short page");
assert_eq!(oids(&page), vec![p.side.to_string(), p.main.to_string()]);
}
#[test]
fn a_named_branch_starts_the_walk_at_that_branch() {
let (_dir, plane, path, p) = world();
let side = plane
.log(&path, "refs/heads/side", 100, "")
.expect("side log");
assert_eq!(oids(&side), vec![p.side.to_string(), p.root.to_string()]);
}