use anyhow::Result;
use clap::Args;
use serde_json::{Value, json};
use crate::cli::CliOutput;
use crate::storage as db;
#[derive(Args, Debug, Clone)]
pub struct UnsubscribeArgs {
#[arg(long, value_name = "ID")]
pub id: String,
#[arg(long)]
pub json: bool,
}
pub fn cmd_unsubscribe(
db_path: &std::path::Path,
args: &UnsubscribeArgs,
out: &mut CliOutput<'_>,
) -> Result<()> {
let conn = db::open(db_path)?;
let params = json!({"id": args.id});
let envelope = crate::mcp::handle_unsubscribe(&conn, ¶ms, None)
.map_err(|e| anyhow::anyhow!(crate::errors::msg::unsubscribe(e)))?;
if args.json {
writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
return Ok(());
}
let removed = envelope
.get("removed")
.and_then(Value::as_bool)
.unwrap_or(false);
writeln!(out.stdout, "unsubscribe: id={} removed={removed}", args.id)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::test_utils::TestEnv;
#[test]
fn unsubscribe_cli_unknown_id_returns_zero_removed() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = UnsubscribeArgs {
id: "nonexistent".into(),
json: true,
};
{
let mut out = env.output();
cmd_unsubscribe(&db, &args, &mut out).expect("ok");
}
let stdout = env.stdout_str();
let envelope: Value = serde_json::from_str(stdout.trim()).expect("parse envelope");
assert_eq!(envelope["removed"].as_bool(), Some(false));
}
#[test]
fn unsubscribe_cli_text_output_unknown_id() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = UnsubscribeArgs {
id: "nonexistent".into(),
json: false,
};
{
let mut out = env.output();
cmd_unsubscribe(&db, &args, &mut out).expect("ok");
}
let stdout = env.stdout_str();
assert!(
stdout.contains("unsubscribe: id=nonexistent"),
"got: {stdout}"
);
assert!(stdout.contains("removed=false"), "got: {stdout}");
}
#[test]
fn unsubscribe_cli_removes_existing_subscription() {
crate::config::set_active_hooks_hmac_secret(None);
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let sub_id = {
let conn = db::open(&db).unwrap();
let agent_id = crate::identity::resolve_agent_id(None, None).unwrap();
db::register_agent(&conn, &agent_id, "test", &[]).expect("register");
let envelope = crate::mcp::handle_subscribe(
&conn,
&json!({"url": "https://example.com/hook", "secret": "topsecret"}),
None,
)
.expect("subscribe");
envelope["id"].as_str().unwrap().to_string()
};
let args = UnsubscribeArgs {
id: sub_id,
json: true,
};
{
let mut out = env.output();
cmd_unsubscribe(&db, &args, &mut out).expect("ok");
}
let envelope: Value = serde_json::from_str(env.stdout_str().trim()).expect("json");
assert_eq!(envelope["removed"].as_bool(), Some(true));
}
}