diff --git a/hive-forge/src/verbs/subscription.rs b/hive-forge/src/verbs/subscription.rs index 46161cdf..13d77e10 100644 --- a/hive-forge/src/verbs/subscription.rs +++ b/hive-forge/src/verbs/subscription.rs @@ -33,6 +33,30 @@ pub struct Args { list: bool, } +/// Refuse `--ignore`. Forgejo's PUT subscription endpoint takes no body — +/// it always subscribes (watch), with no way to mark a repo ignored +/// without also watching it. The old raw client sent a +/// `{subscribed, ignored}` body which the server ignored, so `--ignore` +/// silently behaved like `--watch` (the opposite of what it promises) +/// instead of failing. Refuse instead of lying about what happened. A +/// free function (rather than inlined in `run`) so the refusal itself — +/// not just a string constant — is unit-testable without a real `Client`: +/// it fires before any API call is made. +/// +/// # Errors +/// Always errors when `args.ignore` is set; otherwise always `Ok`. +fn reject_ignore(args: &Args) -> Result<()> { + if args.ignore { + anyhow::bail!( + "subscription --ignore is not supported: Forgejo's subscription \ + endpoint has no way to mark a repo ignored without also \ + watching it. Use --unwatch to stop watching, or --watch if \ + muted-but-watched is what you actually want." + ); + } + Ok(()) +} + /// Run the `subscription` verb: `--list` all watched repos, set the /// current repo's watch (`--watch` / `--ignore` / `--unwatch`), or get /// its current state. @@ -40,26 +64,8 @@ pub struct Args { /// # Errors /// Propagates transport / non-2xx errors from the Forgejo client calls /// and from `print_json`. -/// Error text for a refused `--ignore`. Forgejo's PUT subscription endpoint -/// takes no body — it always subscribes (watch), with no way to mark a repo -/// ignored without also watching it. The old raw client sent a -/// `{subscribed, ignored}` body which the server ignored, so `--ignore` -/// silently behaved like `--watch` (the opposite of what it promises) -/// instead of failing. Refuse instead of lying about what happened. A free -/// function (rather than inlined in `run`) so it's testable without a real -/// `Client` — the refusal fires before any API call is made. -fn ignore_refusal_message() -> String { - "subscription --ignore is not supported: Forgejo's subscription endpoint \ - has no way to mark a repo ignored without also watching it. Use \ - --unwatch to stop watching, or --watch if muted-but-watched is what \ - you actually want." - .to_owned() -} - pub fn run(client: &Client, args: Args) -> Result<()> { - if args.ignore { - anyhow::bail!(ignore_refusal_message()); - } + reject_ignore(&args)?; if args.list { // List every repo the authed user watches, so an agent can audit // its notification firehose and decide what to `--unwatch`. Not @@ -120,13 +126,29 @@ pub fn run(client: &Client, args: Args) -> Result<()> { #[cfg(test)] mod tests { - use super::ignore_refusal_message; + use super::{Args, reject_ignore}; + + fn args(watch: bool, ignore: bool, unwatch: bool, list: bool) -> Args { + Args { + watch, + ignore, + unwatch, + list, + } + } #[test] fn ignore_is_rejected_pointing_at_unwatch() { - let msg = ignore_refusal_message(); + let err = + reject_ignore(&args(false, true, false, false)).expect_err("--ignore must be refused"); + let msg = err.to_string(); assert!(msg.contains("--unwatch")); assert!(msg.contains("--watch")); assert!(msg.contains("not supported")); } + + #[test] + fn watch_is_not_rejected() { + assert!(reject_ignore(&args(true, false, false, false)).is_ok()); + } }