Skip to content

Commit 2cefa9a

Browse files
refactor(testkit): share YamlFile and report stdout when output checks fail
Replace CliSandboxBuilder's private PolicyFile with a public testkit::YamlFile, so other CLI commands that read YAML files can reuse it. Its .yaml suffix lets the CLI recognize the format. ExecOutput::success and CliOutput::success now include stdout in their failure messages, since many commands report errors there. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
1 parent caff38c commit 2cefa9a

6 files changed

Lines changed: 39 additions & 22 deletions

File tree

‎tests/testkit/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ fails. A killed test process skips cleanup, so names are unique to the run.
8989
- `cx.cli([...])` to run the installed CLI against the same gateway, with
9090
`success()`, `failure()`, `expect_stdout()`, and `json()` on the output. `cx.cli_handle()`
9191
returns that CLI for code that can't borrow `cx`, such as a cleanup hook.
92+
`testkit::YamlFile` writes YAML to a temporary file for commands that read
93+
one, such as `profile import --file`.
9294
- `cx.on_cleanup(name, hook)` to remove something when the test ends.
9395
- `cx.client()` for SDK calls testkit does not wrap.
9496
- `poll_until` for waiting on other state, and `with_deadline(what, timeout,

‎tests/testkit/testkit/src/cli.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,10 @@ impl CliOutput {
156156
pub fn success(self) -> Result<Self> {
157157
crate::ensure!(
158158
self.exit_code == 0,
159-
"`{}` exited with {}; stderr: {}",
159+
"`{}` exited with {}; stdout: {}; stderr: {}",
160160
self.command,
161161
self.exit_code,
162+
self.stdout.trim(),
162163
self.stderr.trim()
163164
);
164165
Ok(self)

‎tests/testkit/testkit/src/cli_sandbox.rs‎

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
88
use crate::cli::CliOutput;
99
use crate::context::{Context, Sandbox};
10-
use crate::error::{Error, Result, ResultExt};
10+
use crate::error::{Result, ResultExt};
11+
use crate::yaml_file::YamlFile;
1112

1213
/// A sandbox to create with `openshell sandbox create`. Start one with
1314
/// [`Context::cli_sandbox`].
@@ -95,7 +96,7 @@ impl<'a> CliSandboxBuilder<'a> {
9596

9697
async fn invoke(&self) -> Result<CliOutput> {
9798
self.cx.track(&self.name);
98-
let policy = self.policy.as_deref().map(PolicyFile::new).transpose()?;
99+
let policy = self.policy.as_deref().map(YamlFile::new).transpose()?;
99100
let mut args = vec!["sandbox", "create", "--name", &self.name, "--no-tty"];
100101
if let Some(policy) = &policy {
101102
args.extend(["--policy", policy.path()?]);
@@ -108,21 +109,3 @@ impl<'a> CliSandboxBuilder<'a> {
108109
self.cx.cli(args).await
109110
}
110111
}
111-
112-
/// A policy written to a temporary file for `sandbox create --policy`.
113-
struct PolicyFile(tempfile::NamedTempFile);
114-
115-
impl PolicyFile {
116-
fn new(contents: &str) -> Result<Self> {
117-
let file = tempfile::NamedTempFile::new().context("create policy file")?;
118-
std::fs::write(file.path(), contents)
119-
.context(format!("write {}", file.path().display()))?;
120-
Ok(Self(file))
121-
}
122-
123-
fn path(&self) -> Result<&str> {
124-
let path = self.0.path();
125-
path.to_str()
126-
.ok_or_else(|| Error::msg(format!("{} is not UTF-8", path.display())))
127-
}
128-
}

‎tests/testkit/testkit/src/context.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,10 @@ impl ExecOutput {
411411
pub fn success(self) -> Result<Self> {
412412
crate::ensure!(
413413
self.exit_code == 0,
414-
"{} exited with {}; stderr: {}",
414+
"{} exited with {}; stdout: {}; stderr: {}",
415415
self.command,
416416
self.exit_code,
417+
self.stdout.trim(),
417418
self.stderr.trim()
418419
);
419420
Ok(self)

‎tests/testkit/testkit/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub mod program;
3838
mod ready;
3939
mod registry;
4040
mod run;
41+
mod yaml_file;
4142

4243
pub use cleanup::CLEANUP_TIMEOUT;
4344
pub use cli::{CLI_TIMEOUT, Cli, CliOutput};
@@ -56,6 +57,7 @@ pub use ready::check as check_ready;
5657
pub use registry::Test;
5758
pub use run::{TestInfo, run};
5859
pub use testkit_macros::test;
60+
pub use yaml_file::YamlFile;
5961

6062
#[doc(hidden)]
6163
pub mod __private {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::error::{Error, Result, ResultExt as _};
5+
6+
/// YAML in a temporary file, for CLI commands that read one, such as
7+
/// `sandbox create --policy` or `profile import --file`. The file is removed
8+
/// when dropped.
9+
pub struct YamlFile(tempfile::NamedTempFile);
10+
11+
impl YamlFile {
12+
pub fn new(contents: &str) -> Result<Self> {
13+
let file = tempfile::Builder::new()
14+
.suffix(".yaml")
15+
.tempfile()
16+
.context("create a temporary YAML file")?;
17+
std::fs::write(file.path(), contents)
18+
.context(format!("write {}", file.path().display()))?;
19+
Ok(Self(file))
20+
}
21+
22+
/// The file's path, as a CLI argument.
23+
pub fn path(&self) -> Result<&str> {
24+
let path = self.0.path();
25+
path.to_str()
26+
.ok_or_else(|| Error::msg(format!("{} is not UTF-8", path.display())))
27+
}
28+
}

0 commit comments

Comments
 (0)