use codex_wrapper::{
ApplyCommand, ArchiveCommand, Codex, CodexCommand, CompletionCommand, DeleteCommand,
DoctorCommand, ExecCommand, ExecResumeCommand, FeaturesListCommand, ForkCommand,
LoginStatusCommand, McpListCommand, McpServerCommand, PluginListCommand,
PluginMarketplaceListCommand, ResumeCommand, ReviewCommand, SandboxCommand, Shell,
UnarchiveCommand, VersionCommand,
};
fn codex() -> Codex {
Codex::builder()
.build()
.expect("codex binary must be in PATH")
}
#[tokio::test]
#[ignore]
async fn version_command() {
let codex = codex();
let output = VersionCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
assert!(
output.stdout.contains("codex-cli"),
"expected version string, got: {}",
output.stdout
);
}
#[tokio::test]
#[ignore]
async fn cli_version_parsing() {
let codex = codex();
let version = codex.cli_version().await.unwrap();
assert!(version.major == 0, "unexpected major version: {version}");
assert!(version.minor > 0, "minor version should be > 0: {version}");
}
#[tokio::test]
#[ignore]
async fn check_version_satisfies() {
let codex = codex();
let minimum = codex_wrapper::CliVersion::new(0, 1, 0);
let version = codex.check_version(&minimum).await.unwrap();
assert!(version.satisfies_minimum(&minimum));
}
#[tokio::test]
#[ignore]
async fn check_version_too_high_fails() {
let codex = codex();
let minimum = codex_wrapper::CliVersion::new(999, 0, 0);
let result = codex.check_version(&minimum).await;
assert!(result.is_err());
}
#[tokio::test]
#[ignore]
async fn completion_bash() {
let codex = codex();
let output = CompletionCommand::new()
.shell(Shell::Bash)
.execute(&codex)
.await
.unwrap();
assert!(output.success);
assert!(!output.stdout.is_empty());
}
#[tokio::test]
#[ignore]
async fn completion_zsh() {
let codex = codex();
let output = CompletionCommand::new()
.shell(Shell::Zsh)
.execute(&codex)
.await
.unwrap();
assert!(output.success);
assert!(output.stdout.contains("compdef") || output.stdout.contains("codex"));
}
#[tokio::test]
#[ignore]
async fn login_status() {
let codex = codex();
let output = LoginStatusCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
assert!(
!output.stdout.is_empty() || !output.stderr.is_empty(),
"login status should produce output"
);
}
#[tokio::test]
#[ignore]
async fn features_list() {
let codex = codex();
let output = FeaturesListCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
assert!(
output.stdout.contains("stable") || output.stdout.contains("experimental"),
"expected feature flags in output, got: {}",
output.stdout
);
}
#[tokio::test]
#[ignore]
async fn mcp_list() {
let codex = codex();
let output = McpListCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
}
#[tokio::test]
#[ignore]
async fn mcp_list_json() {
let codex = codex();
let value = McpListCommand::new().execute_json(&codex).await.unwrap();
assert!(value.is_array(), "expected JSON array, got: {value}");
}
#[tokio::test]
#[ignore]
async fn sandbox_echo() {
let codex = codex();
let output = SandboxCommand::new("echo")
.arg("sandbox-test")
.execute(&codex)
.await
.unwrap();
assert!(output.success);
assert!(
output.stdout.contains("sandbox-test"),
"expected echo output, got: {}",
output.stdout
);
}
#[tokio::test]
#[ignore]
async fn exec_simple() {
let codex = codex();
let output = ExecCommand::new("respond with just the word 'pong'")
.ephemeral()
.execute(&codex)
.await
.unwrap();
assert!(output.success);
}
#[tokio::test]
#[ignore]
async fn exec_json_lines() {
let codex = codex();
let events = ExecCommand::new("respond with just the word 'test'")
.ephemeral()
.execute_json_lines(&codex)
.await
.unwrap();
assert!(!events.is_empty(), "expected at least one JSONL event");
let types: Vec<&str> = events.iter().map(|e| e.event_type.as_str()).collect();
assert!(
types.contains(&"thread.started"),
"expected thread.started event, got: {types:?}"
);
}
#[tokio::test]
#[ignore]
async fn exec_query_result() {
let codex = codex();
let result = ExecCommand::new("respond with just the word 'pong'")
.ephemeral()
.execute_json(&codex)
.await
.unwrap();
assert!(
!result.events.is_empty(),
"expected a non-empty event stream"
);
assert!(
result.thread_id.is_some(),
"expected a thread_id from the stream"
);
}
#[tokio::test]
#[ignore]
async fn exec_resume_json_lines() {
let codex = codex();
let events = ExecResumeCommand::new()
.last()
.ephemeral()
.execute_json_lines(&codex)
.await
.unwrap();
assert!(!events.is_empty(), "expected at least one JSONL event");
let types: Vec<&str> = events.iter().map(|e| e.event_type.as_str()).collect();
assert!(
types.contains(&"thread.started"),
"expected thread.started event, got: {types:?}"
);
}
#[tokio::test]
#[ignore]
async fn review_uncommitted() {
let codex = codex();
let output = ReviewCommand::new()
.uncommitted()
.ephemeral()
.execute(&codex)
.await;
assert!(output.is_ok() || output.is_err());
}
#[tokio::test]
#[ignore]
async fn resume_nonexistent_session_fails() {
let codex = codex();
let result = ResumeCommand::new()
.session_id("00000000-0000-0000-0000-000000000000")
.execute(&codex)
.await;
assert!(result.is_err(), "resuming a bogus session should fail");
}
#[tokio::test]
#[ignore]
async fn fork_nonexistent_session_fails() {
let codex = codex();
let result = ForkCommand::new()
.session_id("00000000-0000-0000-0000-000000000000")
.execute(&codex)
.await;
assert!(result.is_err(), "forking a bogus session should fail");
}
#[tokio::test]
#[ignore]
async fn apply_bogus_task_fails() {
let codex = codex();
let result = ApplyCommand::new("not-a-real-task").execute(&codex).await;
assert!(result.is_err(), "applying a bogus task should fail");
}
#[tokio::test]
#[ignore]
async fn doctor_json() {
let codex = codex();
let output = DoctorCommand::new().json().execute(&codex).await.unwrap();
assert!(output.success);
let value: serde_json::Value =
serde_json::from_str(&output.stdout).expect("doctor --json should emit valid JSON");
assert!(
value.get("codexVersion").is_some(),
"expected codexVersion field, got: {value}"
);
}
#[tokio::test]
#[ignore]
async fn plugin_list() {
let codex = codex();
let output = PluginListCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
}
#[tokio::test]
#[ignore]
async fn plugin_marketplace_list() {
let codex = codex();
let output = PluginMarketplaceListCommand::new()
.execute(&codex)
.await
.unwrap();
assert!(output.success);
}
#[tokio::test]
#[ignore]
async fn archive_bogus_session_fails() {
let codex = codex();
let result = ArchiveCommand::new("00000000-0000-0000-0000-000000000000")
.execute(&codex)
.await;
assert!(result.is_err(), "archiving a bogus session should fail");
}
#[tokio::test]
#[ignore]
async fn unarchive_bogus_session_fails() {
let codex = codex();
let result = UnarchiveCommand::new("00000000-0000-0000-0000-000000000000")
.execute(&codex)
.await;
assert!(result.is_err(), "unarchiving a bogus session should fail");
}
#[tokio::test]
#[ignore]
async fn delete_bogus_session_fails() {
let codex = codex();
let result = DeleteCommand::new("00000000-0000-0000-0000-000000000000")
.force()
.execute(&codex)
.await;
assert!(result.is_err(), "deleting a bogus session should fail");
}
#[tokio::test]
#[ignore]
async fn mcp_server_args_valid() {
let cmd = McpServerCommand::new().config("model=\"gpt-5\"");
let args = CodexCommand::args(&cmd);
assert_eq!(args[0], "mcp-server");
}
#[tokio::test]
#[ignore]
async fn with_working_dir() {
let codex = codex().with_working_dir("/tmp");
let output = VersionCommand::new().execute(&codex).await.unwrap();
assert!(output.success);
}
#[tokio::test]
#[ignore]
async fn timeout_fires() {
let codex = Codex::builder()
.timeout(std::time::Duration::from_millis(1))
.build()
.unwrap();
let result = ExecCommand::new("count to a million slowly")
.ephemeral()
.execute(&codex)
.await;
assert!(
matches!(result, Err(codex_wrapper::Error::Timeout { .. })),
"expected timeout error, got: {result:?}"
);
}