From 612fcd51b9c838f9a8b488e328edce486cbdd8cc Mon Sep 17 00:00:00 2001 From: Hoyt Harness <2735828+hoyt-harness@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:04:57 -0500 Subject: [PATCH] fix(executor): warn to stderr when 2xx response has empty body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When execute_method() receives a 2xx HTTP response but the body is empty, it previously fell through to handle_json_response() which silently discarded the input and returned Ok(false) — no output, no error, exit 0. Add a guard before the handle_json_response() call: if body_text is empty, print a Warning to stderr identifying the method and status code, then break out of the pagination loop. The exit code remains 0 because the HTTP call itself succeeded; the warning is the diagnostic for silent responses. Also add two unit tests for handle_json_response() covering the empty-body and valid-JSON paths, and collapse a pre-existing clippy::collapsible_match in helpers/script.rs. Fixes #740 (sub-issue: empty-body diagnostic) --- .changeset/empty-body-diagnostic.md | 5 ++ crates/google-workspace-cli/src/executor.rs | 66 +++++++++++++++++++ .../src/helpers/script.rs | 8 +-- 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 .changeset/empty-body-diagnostic.md diff --git a/.changeset/empty-body-diagnostic.md b/.changeset/empty-body-diagnostic.md new file mode 100644 index 00000000..8084c943 --- /dev/null +++ b/.changeset/empty-body-diagnostic.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Warn to stderr when an API call succeeds but returns an empty response body diff --git a/crates/google-workspace-cli/src/executor.rs b/crates/google-workspace-cli/src/executor.rs index 46f31ac4..a96db7bb 100644 --- a/crates/google-workspace-cli/src/executor.rs +++ b/crates/google-workspace-cli/src/executor.rs @@ -495,6 +495,15 @@ pub async fn execute_method( .await .context("Failed to read response body")?; + if body_text.is_empty() { + eprintln!( + "Warning: {} returned HTTP {} with an empty response body.", + method_id, + status.as_u16() + ); + break; + } + let should_continue = handle_json_response( &body_text, pagination, @@ -2024,6 +2033,63 @@ mod tests { _ => panic!("Expected Api error"), } } + + #[tokio::test] + async fn test_handle_json_response_empty_body_returns_no_continue() { + let pagination = PaginationConfig::default(); + let output_format = crate::formatter::OutputFormat::Json; + let sanitize_mode = crate::helpers::modelarmor::SanitizeMode::Warn; + let mut pages_fetched = 0u32; + let mut page_token: Option = None; + let mut captured: Vec = Vec::new(); + + let result = handle_json_response( + "", + &pagination, + None, + &sanitize_mode, + &output_format, + &mut pages_fetched, + &mut page_token, + false, + &mut captured, + ) + .await; + + assert!(result.is_ok()); + assert!(!result.unwrap(), "empty body: should not continue pagination"); + assert_eq!(pages_fetched, 0, "empty body: pages_fetched should not increment"); + assert!(captured.is_empty()); + } + + #[tokio::test] + async fn test_handle_json_response_valid_json_increments_pages() { + let pagination = PaginationConfig::default(); + let output_format = crate::formatter::OutputFormat::Json; + let sanitize_mode = crate::helpers::modelarmor::SanitizeMode::Warn; + let mut pages_fetched = 0u32; + let mut page_token: Option = None; + let mut captured: Vec = Vec::new(); + + let result = handle_json_response( + r#"{"id": "abc"}"#, + &pagination, + None, + &sanitize_mode, + &output_format, + &mut pages_fetched, + &mut page_token, + true, + &mut captured, + ) + .await; + + assert!(result.is_ok()); + assert!(!result.unwrap(), "single page: should not continue pagination"); + assert_eq!(pages_fetched, 1); + assert_eq!(captured.len(), 1); + assert_eq!(captured[0]["id"], "abc"); + } } #[tokio::test] diff --git a/crates/google-workspace-cli/src/helpers/script.rs b/crates/google-workspace-cli/src/helpers/script.rs index 11bcdebe..4b31db62 100644 --- a/crates/google-workspace-cli/src/helpers/script.rs +++ b/crates/google-workspace-cli/src/helpers/script.rs @@ -169,13 +169,7 @@ fn process_file(path: &Path) -> Result, GwsError> { filename.trim_end_matches(".js").trim_end_matches(".gs"), ), "html" => ("HTML", filename.trim_end_matches(".html")), - "json" => { - if filename == "appsscript.json" { - ("JSON", "appsscript") - } else { - return Ok(None); - } - } + "json" if filename == "appsscript.json" => ("JSON", "appsscript"), _ => return Ok(None), };