mirror of
https://github.com/openai/codex.git
synced 2026-04-28 10:21:06 +03:00
3.7 KiB
3.7 KiB
DOs
- Put tests first: Keep helper fns after tests to foreground intent.
// Tests first
/// Sends a prompt and asserts the streamed message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn full_conversation_turn_integration() { /* ... */ }
// Helpers after tests
fn write_config(codex_home: &Path, server: &MockServer) { /* ... */ }
- Use readable multi-line config strings: Start with a leading newline; inline variables with format!.
fs::write(
codex_home.join("config.toml"),
format!(r#"
model_provider = "mock"
model = "test-model"
[model_providers.mock]
name = "mock"
base_url = "{}/v1"
env_key = "PATH"
wire_api = "responses"
"#, server.uri()),
)?;
- Consider indoc for long blocks: Keep indentation tidy without left margin loss.
use indoc::formatdoc;
fs::write(
codex_home.join("config.toml"),
formatdoc!(r#"
model_provider = "mock"
model = "test-model"
[model_providers.mock]
name = "mock"
base_url = "{base}/v1"
env_key = "PATH"
wire_api = "responses"
"#, base = server.uri()),
)?;
- Prefer template + replace for SSE: Avoid brace-escaping hell in format! strings.
fn sse_message(text: &str) -> String {
const TEMPLATE: &str = r#"event: response.output_item.done
data: {"type":"response.output_item.done","item":{"type":"message","role":"assistant","content":[{"type":"output_text","text":"TEXT_PLACEHOLDER"}]}}
event: response.completed
data: {"type":"response.completed","response":{"id":"resp1","output":[]}}
"#;
TEMPLATE.replace("TEXT_PLACEHOLDER", text)
}
- Name things precisely: Use codex_home for the temp config directory.
fn write_config(codex_home: &Path, server: &MockServer) {
// ...
}
- Assert on the final message via file: Use --output-last-message and compare file contents.
let codex_home = TempDir::new().unwrap();
let sandbox = TempDir::new().unwrap();
write_config(codex_home.path(), &server);
let last = sandbox.path().join("last_message.txt");
let mut cmd = assert_cmd::Command::cargo_bin("codex").unwrap();
cmd.env("CODEX_HOME", codex_home.path())
.current_dir(sandbox.path())
.arg("exec")
.arg("--skip-git-repo-check")
.arg("--output-last-message")
.arg(&last)
.arg("Hello");
cmd.assert().success().stdout(predicates::str::contains("Hello, world."));
assert_eq!(fs::read_to_string(&last).unwrap().trim(), "Hello, world.");
- Document each test: Add a one-liner docstring explaining the behavior under test.
/// Simulates a shell tool call then verifies the assistant's follow-up.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn tool_invocation_flow() { /* ... */ }
DON'Ts
- Don’t put helpers above tests: It hides the test’s purpose and increases scrolling.
// Anti-pattern: helpers first
fn write_config(...) { /* ... */ }
#[tokio::test] async fn test_case() { /* ... */ }
- Don’t hand-escape JSON braces inside format!: Hard to read and easy to break.
// Anti-pattern: unreadable escaping
format!("data: {{\"text\":\"{text}\"}}");
- Don’t use vague names like dir or home: Be explicit about the directory’s role.
// Anti-pattern: vague
fn write_config(dir: &Path, server: &MockServer) { /* ... */ }
- Don’t assert only via stdout: Stream formatting can change; assert the canonical last message file.
// Anti-pattern: only stdout check
cmd.assert().stdout(predicates::str::contains("done"));
- Don’t omit test docstrings: Future readers shouldn’t have to infer the intent.
// Anti-pattern: no docstring
#[tokio::test] async fn full_conversation_turn_integration() { /* ... */ }