Struct libcnb_test::TestContext
source · pub struct TestContext<'a> {
pub pack_stdout: String,
pub pack_stderr: String,
pub config: BuildConfig,
/* private fields */
}
Expand description
Context for a currently executing test.
Fields§
§pack_stdout: String
Standard output of pack
, interpreted as an UTF-8 string.
pack_stderr: String
Standard error of pack
, interpreted as an UTF-8 string.
config: BuildConfig
The configuration used for this integration test.
Implementations§
source§impl<'a> TestContext<'a>
impl<'a> TestContext<'a>
sourcepub fn start_container<C: Borrow<ContainerConfig>, F: FnOnce(ContainerContext)>(
&self,
config: C,
f: F
)
pub fn start_container<C: Borrow<ContainerConfig>, F: FnOnce(ContainerContext)>( &self, config: C, f: F )
Starts a detached container using the provided ContainerConfig
.
After the passed function has returned, the Docker container is removed.
If you wish to run a shell command and don’t need to customise the configuration, use
the convenience function TestContext::run_shell_command
instead.
§Examples
use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/app"),
|context| {
// Start the container using the default process-type:
// https://buildpacks.io/docs/app-developer-guide/run-an-app/#default-process-type
context.start_container(ContainerConfig::new(), |container| {
// ...
});
// Start the container using the specified process-type:
// https://buildpacks.io/docs/app-developer-guide/run-an-app/#non-default-process-type
context.start_container(ContainerConfig::new().entrypoint("worker"), |container| {
// ...
});
// Start the container using the specified process-type and additional arguments:
// https://buildpacks.io/docs/app-developer-guide/run-an-app/#non-default-process-type-with-additional-arguments
context.start_container(
ContainerConfig::new()
.entrypoint("another-process")
.command(["--additional-arg"]),
|container| {
// ...
},
);
// Start the container using the provided bash script:
// https://buildpacks.io/docs/app-developer-guide/run-an-app/#user-provided-shell-process-with-bash-script
// Only use this shell command form if you need to customise the `ContainerConfig`,
// otherwise use the convenience function `TestContext::run_shell_command` instead.
context.start_container(
ContainerConfig::new()
.entrypoint("launcher")
.command(["for i in {1..3}; do echo \"${i}\"; done"]),
|container| {
// ...
},
);
},
);
§Panics
Panics if there was an error starting the container, such as when the specified entrypoint/command can’t be found.
Note: Does not panic if the container exits after starting (including if it crashes and exits non-zero).
sourcepub fn run_shell_command(&self, command: impl Into<String>) -> LogOutput
pub fn run_shell_command(&self, command: impl Into<String>) -> LogOutput
Run the provided shell command.
The CNB launcher will run the provided command using bash
.
Note: This method will block until the container stops.
§Example
use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/app"),
|context| {
// ...
let command_output =
context.run_shell_command("for i in {1..3}; do echo \"${i}\"; done");
assert_eq!(command_output.stdout, "1\n2\n3\n");
},
);
This is a convenience function for running shell commands inside the image, that is roughly equivalent to:
use libcnb_test::{BuildConfig, ContainerConfig, TestRunner};
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/app"),
|context| {
// ...
context.start_container(
ContainerConfig::new()
.entrypoint("launcher")
.command(["for i in {1..3}; do echo \"${i}\"; done"]),
|container| {
let log_output = container.logs_wait();
// ...
},
);
},
);
However, in addition to requiring less boilerplate, run_shell_command
is also able
to validate the exit status of the container, so should be used instead of start_container
where possible.
§Panics
Panics if there was an error starting the container, or the command exited with a non-zero exit code.
sourcepub fn download_sbom_files<R, F: Fn(SbomFiles) -> R>(&self, f: F) -> R
pub fn download_sbom_files<R, F: Fn(SbomFiles) -> R>(&self, f: F) -> R
Downloads SBOM files from the built image into a temporary directory.
References to the downloaded files are passed into the given function and will be cleaned-up after the function exits.
§Example
use libcnb_data::buildpack_id;
use libcnb_data::sbom::SbomFormat;
use libcnb_test::{BuildConfig, ContainerConfig, SbomType, TestRunner};
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/app"),
|context| {
context.download_sbom_files(|sbom_files| {
assert!(sbom_files
.path_for(
buildpack_id!("heroku/jvm"),
SbomType::Launch,
SbomFormat::SyftJson
)
.exists());
});
},
);
§Panics
Panics if there was an error creating the temporary directory used to store the SBOM files, or if the Pack CLI command used to download the SBOM files failed.
sourcepub fn rebuild<C: Borrow<BuildConfig>, F: FnOnce(TestContext<'_>)>(
self,
config: C,
f: F
)
pub fn rebuild<C: Borrow<BuildConfig>, F: FnOnce(TestContext<'_>)>( self, config: C, f: F )
Starts a subsequent integration test build.
This function behaves exactly like TestRunner::build
, but it will reuse the OCI image
from the previous test, causing the CNB lifecycle to restore any cached layers. It will use the
same TestRunner
as the previous test run.
This function allows testing of subsequent builds, including caching logic and buildpack behaviour when build environment variables change, stacks are upgraded and more.
Note that this function will consume the current context. This is because the image will be changed by the subsequent test, invalidating the context. Running a subsequent test must therefore be the last operation. You can nest subsequent runs if required.
§Example
use libcnb_test::{assert_contains, BuildConfig, TestRunner};
TestRunner::default().build(
BuildConfig::new("heroku/builder:22", "tests/fixtures/app"),
|context| {
assert_contains!(context.pack_stdout, "---> Installing dependencies");
let config = context.config.clone();
context.rebuild(config, |context| {
assert_contains!(context.pack_stdout, "---> Using cached dependencies");
});
},
);
Auto Trait Implementations§
impl<'a> Freeze for TestContext<'a>
impl<'a> !RefUnwindSafe for TestContext<'a>
impl<'a> !Send for TestContext<'a>
impl<'a> !Sync for TestContext<'a>
impl<'a> Unpin for TestContext<'a>
impl<'a> !UnwindSafe for TestContext<'a>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more