ai_memory/cli/commands/
list_subscriptions.rs1use crate::models::field_names;
13use anyhow::Result;
14use clap::Args;
15use serde_json::Value;
16
17use crate::cli::CliOutput;
18use crate::storage as db;
19
20#[derive(Args, Debug, Clone)]
22pub struct ListSubscriptionsArgs {
23 #[arg(long)]
25 pub json: bool,
26}
27
28pub fn cmd_list_subscriptions(
36 db_path: &std::path::Path,
37 args: &ListSubscriptionsArgs,
38 out: &mut CliOutput<'_>,
39) -> Result<()> {
40 let conn = db::open(db_path)?;
41 let envelope = crate::mcp::handle_list_subscriptions(&conn, None)
42 .map_err(|e| anyhow::anyhow!("list-subscriptions: {e}"))?;
43
44 if args.json {
45 writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
46 return Ok(());
47 }
48
49 let count = envelope.get("count").and_then(Value::as_u64).unwrap_or(0);
50 writeln!(out.stdout, "list-subscriptions: {count} row(s)")?;
51 if let Some(arr) = envelope
52 .get(field_names::SUBSCRIPTIONS)
53 .and_then(Value::as_array)
54 {
55 for s in arr {
56 let id = s.get("id").and_then(Value::as_str).unwrap_or("?");
57 let url = s.get("url").and_then(Value::as_str).unwrap_or("?");
58 let events = s.get("events").and_then(Value::as_str).unwrap_or("*");
59 writeln!(out.stdout, " {id} events={events} url={url}")?;
60 }
61 }
62 Ok(())
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::cli::test_utils::TestEnv;
69
70 #[test]
71 fn list_subscriptions_cli_empty_db_returns_zero() {
72 let mut env = TestEnv::fresh();
73 let db = env.db_path.clone();
74 let args = ListSubscriptionsArgs { json: true };
75 {
76 let mut out = env.output();
77 cmd_list_subscriptions(&db, &args, &mut out).expect("ok");
78 }
79 let stdout = env.stdout_str();
80 let envelope: Value = serde_json::from_str(stdout.trim()).expect("parse envelope");
81 assert_eq!(envelope["count"].as_u64(), Some(0));
82 }
83
84 #[test]
85 fn list_subscriptions_cli_text_mode_emits_count_line() {
86 let mut env = TestEnv::fresh();
87 let db = env.db_path.clone();
88 let args = ListSubscriptionsArgs { json: false };
89 {
90 let mut out = env.output();
91 cmd_list_subscriptions(&db, &args, &mut out).expect("ok");
92 }
93 let stdout = env.stdout_str();
94 assert!(
95 stdout.starts_with("list-subscriptions: 0 row(s)"),
96 "got: {stdout}"
97 );
98 }
99
100 #[test]
101 fn list_subscriptions_cli_text_mode_lists_rows() {
102 crate::config::set_active_hooks_hmac_secret(None);
103 let mut env = TestEnv::fresh();
104 let db = env.db_path.clone();
105 {
106 let conn = db::open(&db).unwrap();
107 let agent_id = crate::identity::resolve_agent_id(None, None).unwrap();
108 db::register_agent(&conn, &agent_id, "test", &[]).expect("register");
109 crate::mcp::handle_subscribe(
110 &conn,
111 &serde_json::json!({"url": "https://example.com/hook", "secret": "topsecret"}),
112 None,
113 )
114 .expect("subscribe");
115 }
116 let args = ListSubscriptionsArgs { json: false };
117 {
118 let mut out = env.output();
119 cmd_list_subscriptions(&db, &args, &mut out).expect("ok");
120 }
121 let stdout = env.stdout_str();
122 assert!(stdout.contains("1 row(s)"), "got: {stdout}");
123 assert!(
124 stdout.contains("url=https://example.com/hook"),
125 "got: {stdout}"
126 );
127 }
128}