Files
agent-manager/tests/annotations_test.rs

121 lines
4.4 KiB
Rust

//! Favorites, notes and personal tags (issue #39).
mod common;
use agent_manager::commands::annotations_cmd;
use agent_manager::commands::search_cmd;
use agent_manager::events::{self, EventKind};
use agent_manager::state::{Annotation, InstalledEntry};
fn install(app: &agent_manager::app::App, name: &str) {
let entry = InstalledEntry {
name: name.to_string(),
version: Some("1.0.0".to_string()),
method: "npm".to_string(),
install_dir: format!("/tmp/{name}"),
bins: vec![],
run: name.to_string(),
installed_at: "2026-08-01T00:00:00Z".to_string(),
updated_at: None,
pid: None,
started_at: None,
};
app.state.set(&entry).unwrap();
}
#[test]
fn favorite_persists_in_state_and_survives_reload() {
let app = common::test_app("fav", &["list"]);
install(&app, "claude-code");
let code = annotations_cmd::run_favorite(&app, "claude-code").unwrap();
assert_eq!(code, 0);
assert!(annotations_cmd::is_favorite(&app, "claude-code"));
// A fresh StateStore over the same file sees the star (restart).
let store = agent_manager::state::StateStore::new(app.state.path().to_path_buf());
let ann = store.annotation("claude-code").unwrap();
assert!(ann.favorite, "favorite must survive a reload");
let code = annotations_cmd::run_unfavorite(&app, "claude-code").unwrap();
assert_eq!(code, 0);
assert!(!annotations_cmd::is_favorite(&app, "claude-code"));
}
#[test]
fn favorite_is_journalized() {
let app = common::test_app("favlog", &["list"]);
install(&app, "claude-code");
annotations_cmd::run_favorite(&app, "claude-code").unwrap();
let evs = events::read_events(&app.events_dir(), 10);
assert!(
evs.iter().any(|e| e.kind == EventKind::Annotate && e.agent.as_deref() == Some("claude-code")),
"an annotate event must be journaled: {evs:?}"
);
}
#[test]
fn note_is_stored_and_shown() {
let app = common::test_app("note", &["list"]);
install(&app, "claude-code");
let code = annotations_cmd::run_note(
&app,
"claude-code",
&["use".to_string(), "the".to_string(), "sonnet".to_string(), "model".to_string()],
)
.unwrap();
assert_eq!(code, 0);
let ann = app.state.annotation("claude-code").unwrap();
assert_eq!(ann.note.as_deref(), Some("use the sonnet model"));
// Showing the note is a no-error operation too.
assert_eq!(annotations_cmd::run_note(&app, "claude-code", &[]).unwrap(), 0);
}
#[test]
fn tags_are_filterable_in_search() {
let app = common::test_app("tag", &["list"]);
install(&app, "claude-code");
install(&app, "gemini-cli");
annotations_cmd::run_tag(&app, "claude-code", &["python".to_string(), "Frontend".to_string()]).unwrap();
annotations_cmd::run_tag(&app, "gemini-cli", &["python".to_string()]).unwrap();
// Personal tags are lowercased and de-duplicated.
let ann: Annotation = app.state.annotation("claude-code").unwrap();
assert_eq!(ann.tags, vec!["frontend", "python"]);
// search --tag python returns both tagged agents.
assert_eq!(
annotations_cmd::agents_with_tag(&app, "python"),
vec!["claude-code".to_string(), "gemini-cli".to_string()]
);
let code = search_cmd::run(&app, Some(""), None, Some("python")).unwrap();
assert_eq!(code, 0);
// A tag matching nothing is reported cleanly.
let code = search_cmd::run(&app, Some(""), None, Some("nope")).unwrap();
assert_eq!(code, 0);
// untag removes one tag; tags command lists the rest.
annotations_cmd::run_untag(&app, "claude-code", "frontend").unwrap();
let ann = app.state.annotation("claude-code").unwrap();
assert_eq!(ann.tags, vec!["python"]);
let code = annotations_cmd::run_tags(&app, None).unwrap();
assert_eq!(code, 0);
let code = annotations_cmd::run_tags(&app, Some("claude-code")).unwrap();
assert_eq!(code, 0);
}
#[test]
fn tag_requires_at_least_one_word() {
let app = common::test_app("tagempty", &["list"]);
install(&app, "claude-code");
let err = annotations_cmd::run_tag(&app, "claude-code", &[]).unwrap_err().to_string();
assert!(err.contains("usage"), "{err}");
}
#[test]
fn unknown_agent_is_rejected() {
let app = common::test_app("favbad", &["list"]);
let err = annotations_cmd::run_favorite(&app, "does-not-exist").unwrap_err().to_string();
assert!(err.contains("unknown agent"), "{err}");
}