Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions crates/gmeta-core/tests/crud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]

mod helpers;

use gmeta_core::*;
use helpers::*;

#[test]
fn set_and_get_string_value() {
let (_dir, repo) = setup_repo();
let sha = head_sha(&repo);
let session = open_session(repo);

let target = Target::commit(&sha).unwrap();
let handle = session.target(&target);

handle.set("agent:model", "claude-4.6").unwrap();

let value = handle.get_value("agent:model").unwrap();
assert!(value.is_some(), "expected a value for agent:model");
let value = value.unwrap();
assert_eq!(value, MetaValue::String("claude-4.6".to_string()));
assert_eq!(value.value_type(), ValueType::String);
}

#[test]
fn set_and_get_list_value() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::branch("feature-x");
let handle = session.target(&target);

handle.list_push("review:comments", "first").unwrap();
handle.list_push("review:comments", "second").unwrap();
handle.list_push("review:comments", "third").unwrap();

let value = handle.get_value("review:comments").unwrap();
assert!(value.is_some());
let value = value.unwrap();
assert_eq!(value.value_type(), ValueType::List);

if let MetaValue::List(entries) = &value {
assert_eq!(entries.len(), 3);
assert_eq!(entries[0].value, "first");
assert_eq!(entries[1].value, "second");
assert_eq!(entries[2].value, "third");
} else {
panic!("expected MetaValue::List, got {value:?}");
}
}

#[test]
fn set_and_get_set_value() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::path("src/metrics");
let handle = session.target(&target);

handle.set_add("owners", "alice").unwrap();
handle.set_add("owners", "bob").unwrap();
handle.set_add("owners", "charlie").unwrap();
// Duplicate -- should not increase count
handle.set_add("owners", "alice").unwrap();

let value = handle.get_value("owners").unwrap();
assert!(value.is_some());
let value = value.unwrap();
assert_eq!(value.value_type(), ValueType::Set);

if let MetaValue::Set(members) = &value {
assert_eq!(members.len(), 3);
assert!(members.contains("alice"));
assert!(members.contains("bob"));
assert!(members.contains("charlie"));
} else {
panic!("expected MetaValue::Set, got {value:?}");
}
}

#[test]
fn remove_key() {
let (_dir, repo) = setup_repo();
let sha = head_sha(&repo);
let session = open_session(repo);

let target = Target::commit(&sha).unwrap();
let handle = session.target(&target);

handle.set("agent:model", "claude-4.6").unwrap();
assert!(handle.get_value("agent:model").unwrap().is_some());

let removed = handle.remove("agent:model").unwrap();
assert!(removed, "remove should return true for existing key");

let value = handle.get_value("agent:model").unwrap();
assert!(value.is_none(), "value should be gone after remove");
}

#[test]
fn all_target_types() {
let (_dir, repo) = setup_repo();
let sha = head_sha(&repo);
let session = open_session(repo);

// Commit target
let commit_target = Target::commit(&sha).unwrap();
session
.target(&commit_target)
.set("provenance", "ai-generated")
.unwrap();

// Path target
let path_target = Target::path("src/main.rs");
session.target(&path_target).set("owner", "teamA").unwrap();

// Branch target
let branch_target = Target::branch("feature-branch");
session
.target(&branch_target)
.set("ci:status", "green")
.unwrap();

// Project target
let project_target = Target::project();
session
.target(&project_target)
.set("version", "1.0.0")
.unwrap();

// Change-id target
let change_target = Target::change_id("jj-change-abc123");
session
.target(&change_target)
.set("review:status", "approved")
.unwrap();

// Verify each independently
assert_eq!(
session
.target(&commit_target)
.get_value("provenance")
.unwrap(),
Some(MetaValue::String("ai-generated".to_string()))
);
assert_eq!(
session.target(&path_target).get_value("owner").unwrap(),
Some(MetaValue::String("teamA".to_string()))
);
assert_eq!(
session
.target(&branch_target)
.get_value("ci:status")
.unwrap(),
Some(MetaValue::String("green".to_string()))
);
assert_eq!(
session
.target(&project_target)
.get_value("version")
.unwrap(),
Some(MetaValue::String("1.0.0".to_string()))
);
assert_eq!(
session
.target(&change_target)
.get_value("review:status")
.unwrap(),
Some(MetaValue::String("approved".to_string()))
);
}
189 changes: 189 additions & 0 deletions crates/gmeta-core/tests/edge_cases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#![allow(clippy::unwrap_used, clippy::expect_used)]

mod helpers;

use std::collections::BTreeSet;

use gmeta_core::*;
use helpers::*;

#[test]
fn get_nonexistent_key_returns_none() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

let value = handle.get_value("does:not:exist").unwrap();
assert!(value.is_none());
}

#[test]
fn remove_nonexistent_key_returns_false() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

let removed = handle.remove("nonexistent").unwrap();
assert!(!removed, "removing a nonexistent key should return false");
}

#[test]
fn set_remove_then_re_add() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::path("src/metrics");
let handle = session.target(&target);

handle.set_add("owners", "alice").unwrap();
handle.set_add("owners", "bob").unwrap();

handle.set_remove("owners", "alice").unwrap();

let value = handle.get_value("owners").unwrap().unwrap();
if let MetaValue::Set(members) = &value {
assert_eq!(members.len(), 1);
assert!(members.contains("bob"));
} else {
panic!("expected Set");
}

// Re-add alice
handle.set_add("owners", "alice").unwrap();
let value = handle.get_value("owners").unwrap().unwrap();
if let MetaValue::Set(members) = &value {
assert_eq!(members.len(), 2);
assert!(members.contains("alice"));
assert!(members.contains("bob"));
} else {
panic!("expected Set");
}
}

#[test]
fn authorship_tracks_writer() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

handle.set("tracked:key", "some-value").unwrap();

let authorship = handle.get_authorship("tracked:key").unwrap();
assert!(authorship.is_some(), "authorship should exist after set");
let authorship = authorship.unwrap();
assert_eq!(authorship.email, "test@example.com");
}

#[test]
fn key_prefix_matching_returns_subkeys() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

handle.set("agent:model", "claude").unwrap();
handle.set("agent:provider", "anthropic").unwrap();
handle.set("other:key", "unrelated").unwrap();

// Filter by "agent" prefix
let agent_values = handle.get_all_values(Some("agent")).unwrap();
assert_eq!(
agent_values.len(),
2,
"should return only agent:* keys, got: {:?}",
agent_values
.iter()
.map(|(k, _)| k.as_str())
.collect::<Vec<_>>()
);

let keys: BTreeSet<String> = agent_values.iter().map(|(k, _)| k.clone()).collect();
assert!(keys.contains("agent:model"));
assert!(keys.contains("agent:provider"));
assert!(!keys.contains("other:key"));

// Filter by more specific prefix "agent:model"
let model_values = handle.get_all_values(Some("agent:model")).unwrap();
assert_eq!(model_values.len(), 1);
assert_eq!(model_values[0].0, "agent:model");

// No filter returns everything
let all_values = handle.get_all_values(None).unwrap();
assert_eq!(all_values.len(), 3);
}

#[test]
fn namespaced_keys_work_correctly() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

// Deeply namespaced keys
handle.set("agent:claude:session:id", "sess-123").unwrap();
handle.set("agent:claude:session:model", "opus").unwrap();
handle
.set("agent:claude:prompt", "make a sandwich")
.unwrap();

// Get all under "agent:claude:session" prefix
let session_values = handle.get_all_values(Some("agent:claude:session")).unwrap();
assert_eq!(session_values.len(), 2);
let keys: BTreeSet<String> = session_values.iter().map(|(k, _)| k.clone()).collect();
assert!(keys.contains("agent:claude:session:id"));
assert!(keys.contains("agent:claude:session:model"));

// Get all under "agent" prefix
let agent_values = handle.get_all_values(Some("agent")).unwrap();
assert_eq!(agent_values.len(), 3);
}

#[test]
fn multiple_targets_are_independent() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target_a = Target::branch("branch-a");
let target_b = Target::branch("branch-b");

session.target(&target_a).set("status", "draft").unwrap();
session.target(&target_b).set("status", "ready").unwrap();

assert_eq!(
session.target(&target_a).get_value("status").unwrap(),
Some(MetaValue::String("draft".to_string()))
);
assert_eq!(
session.target(&target_b).get_value("status").unwrap(),
Some(MetaValue::String("ready".to_string()))
);
}

#[test]
fn list_remove_by_index() {
let (_dir, repo) = setup_repo();
let session = open_session(repo);

let target = Target::project();
let handle = session.target(&target);

handle.list_push("items", "a").unwrap();
handle.list_push("items", "b").unwrap();
handle.list_push("items", "c").unwrap();

// Remove the middle element by index
handle.list_remove("items", 1).unwrap();

let entries = handle.list_entries("items").unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].value, "a");
assert_eq!(entries[1].value, "c");
}
Loading
Loading