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), };