Files
codex/codex-rs/cli/src/plugin_cmd_tests.rs
T
Felipe Coury a07c8ebd57 Handle broken pipes gracefully in plugin list output (#53109)
## Why

Piping `codex plugin list` into a consumer such as `head` can close stdout before the full catalog is written, causing a panic.

## What changed

Write both table and JSON output through a fallible writer and treat broken pipes during writing or flushing as success. Continue propagating other output errors.

## Testing

Add unit tests for write and flush failures in both output formats, plus CLI tests that close stdout early and verify a successful exit without a panic.

GitOrigin-RevId: cc4a64e10ad723d26b6de9e731efdc534dbee492
2026-10-11 12:38:29 +00:00

77 lines
2.1 KiB
Rust

//! Verify plugin-list output error handling at the writer boundary.
use super::ListPluginsArgs;
use super::write_plugin_list;
use pretty_assertions::assert_eq;
use std::io;
use std::io::Write;
#[derive(Clone, Copy)]
enum Failure {
Write,
Flush,
}
struct FailingWriter {
failure: Failure,
kind: io::ErrorKind,
}
impl Write for FailingWriter {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
match self.failure {
Failure::Write => Err(io::Error::new(self.kind, "output failed")),
Failure::Flush => Ok(bytes.len()),
}
}
fn flush(&mut self) -> io::Result<()> {
match self.failure {
Failure::Write => Ok(()),
Failure::Flush => Err(io::Error::new(self.kind, "output failed")),
}
}
}
#[test]
fn plugin_list_treats_broken_pipe_as_success() {
for json in [false, true] {
for failure in [Failure::Write, Failure::Flush] {
let mut writer = FailingWriter {
failure,
kind: io::ErrorKind::BrokenPipe,
};
let args = ListPluginsArgs {
marketplace_name: None,
json,
available: true,
};
write_plugin_list(&mut writer, Vec::new(), args).unwrap();
}
}
}
#[test]
fn plugin_list_preserves_other_output_errors() {
for json in [false, true] {
for failure in [Failure::Write, Failure::Flush] {
for kind in [io::ErrorKind::PermissionDenied, io::ErrorKind::WriteZero] {
let mut writer = FailingWriter { failure, kind };
let args = ListPluginsArgs {
marketplace_name: None,
json,
available: true,
};
let error = write_plugin_list(&mut writer, Vec::new(), args)
.unwrap_err()
.downcast::<io::Error>()
.unwrap();
assert_eq!(error.kind(), kind);
assert_eq!(error.to_string(), "output failed");
}
}
}
}