use agentstategraph::{CommitOptions, RepoError, Repository};
use agentstategraph_core::{IntentCategory, Object};
use agentstategraph_storage::SqliteStorage;
fn repo() -> Repository {
let r = Repository::new(Box::new(
SqliteStorage::in_memory().expect("in-memory sqlite"),
));
r.init().unwrap();
r
}
fn opts(desc: &str) -> CommitOptions {
CommitOptions::new("agent/test", IntentCategory::Checkpoint, desc)
}
fn commit(r: &Repository, path: &str, v: i64, desc: &str) -> agentstategraph_core::ObjectId {
r.set("main", path, &Object::int(v), opts(desc)).unwrap()
}
#[test]
fn branch_from_full_commit_hash() {
let r = repo();
let c = commit(&r, "/x", 1, "c1");
commit(&r, "/y", 2, "c2");
r.branch("recovery", &format!("{c}")).unwrap();
assert_eq!(r.get("recovery", "/x").unwrap(), Object::int(1));
assert!(r.get("recovery", "/y").is_err());
}
#[test]
fn branch_from_unique_prefix() {
let r = repo();
let c = commit(&r, "/x", 1, "c1");
commit(&r, "/y", 2, "c2");
let short = c.short(); r.branch("recovery", &short).unwrap();
assert_eq!(r.get("recovery", "/x").unwrap(), Object::int(1));
let raw = &c.to_hex()[..10];
r.branch("recovery2", raw).unwrap();
assert_eq!(r.get("recovery2", "/x").unwrap(), Object::int(1));
}
#[test]
fn branch_from_orphaned_commit() {
let r = repo();
r.branch("temp", "main").unwrap();
let c = r
.set("temp", "/orphan", &Object::int(9), opts("on temp"))
.unwrap();
assert!(r.delete_branch("temp").unwrap());
r.branch("recovery", &format!("{c}")).unwrap();
assert_eq!(r.get("recovery", "/orphan").unwrap(), Object::int(9));
}
#[test]
fn unknown_branch_name_is_branch_not_found() {
let r = repo();
let err = r.branch("feat", "definitely-not-a-ref").unwrap_err();
assert!(matches!(err, RepoError::BranchNotFound(_)), "got {err:?}");
}
#[test]
fn hex_shaped_miss_is_commit_not_found() {
let r = repo();
let err = r.branch("feat", "sg_deadbeef").unwrap_err();
assert!(matches!(err, RepoError::CommitNotFound(_)), "got {err:?}");
}
#[test]
fn ambiguous_prefix_errors() {
let r = repo();
for i in 0..24 {
commit(&r, &format!("/k{i}"), i, "c");
}
let mut saw_ambiguous = false;
for digit in "0123456789abcdef".chars() {
if let Err(RepoError::AmbiguousCommitPrefix { count, .. }) =
r.branch(&format!("b_{digit}"), &digit.to_string())
{
assert!(count >= 2);
saw_ambiguous = true;
break;
}
}
assert!(
saw_ambiguous,
"expected some 1-hex-digit prefix to be ambiguous"
);
}
#[test]
fn branch_refs_only_refuses_commit_ids() {
let r = repo().with_branch_refs_only();
let c = commit(&r, "/x", 1, "c1");
let hex = c.to_hex();
for spec in [hex.clone(), format!("sg_{hex}"), hex[..10].to_string()] {
match r.get_json(&spec, "/x") {
Err(RepoError::CommitRefNotAllowed(s)) => assert_eq!(s, spec),
other => panic!("{spec}: {other:?}"),
}
}
assert!(matches!(
r.get_json("no-such-branch", "/x"),
Err(RepoError::BranchNotFound(_))
));
assert_eq!(r.get_json("main", "/x").unwrap(), serde_json::json!(1));
r.branch("abc123", "main").unwrap();
assert_eq!(r.get_json("abc123", "/x").unwrap(), serde_json::json!(1));
let fork = r.fork_namespace(agentstategraph_core::Namespace::default_ns());
assert!(matches!(
fork.get_json(&hex, "/x"),
Err(RepoError::CommitRefNotAllowed(_))
));
let open = repo();
let c = commit(&open, "/x", 1, "c1");
assert_eq!(
open.get_json(&c.to_hex(), "/x").unwrap(),
serde_json::json!(1)
);
}