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 QuotaStatusArgs {
#[arg(long = "agent-id", value_name = "AGENT_ID")]
pub agent_id: Option<String>,
#[arg(long, value_name = "NS")]
pub namespace: Option<String>,
#[arg(long)]
pub json: bool,
}
pub fn cmd_quota_status(
db_path: &std::path::Path,
args: &QuotaStatusArgs,
out: &mut CliOutput<'_>,
) -> Result<()> {
let conn = db::open(db_path)?;
let mut params = json!({});
if let Some(a) = &args.agent_id {
params["agent_id"] = json!(a);
}
if let Some(ns) = &args.namespace {
params["namespace"] = json!(ns);
}
let envelope = crate::mcp::handle_quota_status(&conn, ¶ms)
.map_err(|e| anyhow::anyhow!("quota-status: {e}"))?;
if args.json {
writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
return Ok(());
}
if let Some(count) = envelope.get("count").and_then(Value::as_u64) {
writeln!(out.stdout, "quota-status: {count} row(s)")?;
} else {
let aid = envelope
.get("agent_id")
.and_then(Value::as_str)
.unwrap_or("?");
let ns = envelope
.get("namespace")
.and_then(Value::as_str)
.unwrap_or("?");
writeln!(out.stdout, "quota-status: agent={aid} namespace={ns}")?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::test_utils::TestEnv;
#[test]
fn quota_status_cli_empty_db_returns_zero() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = QuotaStatusArgs {
agent_id: None,
namespace: None,
json: true,
};
{
let mut out = env.output();
cmd_quota_status(&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["count"].as_u64(), Some(0));
}
#[test]
fn quota_status_cli_per_agent_returns_aggregate() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = QuotaStatusArgs {
agent_id: Some("ai:alice".into()),
namespace: None,
json: true,
};
{
let mut out = env.output();
cmd_quota_status(&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["agent_id"].as_str(), Some("ai:alice"));
assert_eq!(envelope["namespace"].as_str(), Some("_global"));
}
#[test]
fn quota_status_cli_text_output_count_branch() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = QuotaStatusArgs {
agent_id: None,
namespace: None,
json: false,
};
{
let mut out = env.output();
cmd_quota_status(&db, &args, &mut out).expect("ok");
}
assert!(env.stdout_str().contains("quota-status: 0 row(s)"));
}
#[test]
fn quota_status_cli_text_output_agent_namespace_branch() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = QuotaStatusArgs {
agent_id: Some("ai:bob".into()),
namespace: Some("proj".into()),
json: false,
};
{
let mut out = env.output();
cmd_quota_status(&db, &args, &mut out).expect("ok");
}
let stdout = env.stdout_str();
assert!(stdout.contains("agent=ai:bob"), "got: {stdout}");
assert!(stdout.contains("namespace=proj"), "got: {stdout}");
}
}