1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
/// Contains traits and helper functions for creating servers
use std::collections::HashMap;
use dockertest::{waitfor::WaitFor, Composition, Image, RunningContainer, Source};
/// A configuration capable of configuring a [Server].
///
/// Types that implement this trait are intended to have a one-to-one
/// relationship with a [Server] as specified by [Server::Config]. The
/// implementation is left intentionally sparse in order to maximize the process
/// of creating a container [Composition]. The [generate_composition] helper
/// function is provided for creating a [Composition] from the usual required
/// configuration options.
///
/// See also [Test][crate::test::Test].
pub trait Config: Clone + Send + Sync {
fn into_composition(self) -> Composition;
fn handle(&self) -> &str;
}
/// A running instance of a specific container generated by a [Config].
///
/// Types that implement this trait are intended to have a one-to-one
/// relationship with a [Config] as specified by [Server::Config]. When a
/// [Test][crate::test::Test] is created it is passed one or more
/// [Configs][Config] which determine what containers are brought up during the
/// test. A [Server] represents a running container within the context of a
/// single test. This is reflected by the fact that a [Server] is created using
/// a [Config] as well as the runtime data provided by a [RunningContainer].
///
/// Types implementing this trait should provide as much utility to the end-user
/// as possible for interacting with the running container. For example, if the
/// container is a web server, this trait should provide functionality for
/// obtaining it's URL.
pub trait Server {
type Config: Config + 'static;
fn new(config: &Self::Config, container: &RunningContainer) -> Self;
}
/// A helper struct for creating [Compositions][Composition] from a set of
/// common configuration parameters.
///
/// This type can be freely cast into a [Composition]. It is only intended for
/// basic use-cases where limited control over how the [Composition] is
/// configured is acceptable.
pub struct ContainerConfig {
pub args: Vec<String>,
pub env: HashMap<String, String>,
pub handle: String,
pub name: String,
pub source: Source,
pub version: String,
pub ports: Option<Vec<(u32, u32)>>,
pub wait: Option<Box<dyn WaitFor>>,
}
#[allow(clippy::from_over_into)] // Only supports one-way casting
impl Into<Composition> for ContainerConfig {
fn into(self) -> Composition {
let image = Image::with_repository(self.name)
.source(self.source)
.tag(self.version);
let mut comp = Composition::with_image(image);
if let Some(p) = self.ports {
for pair in p {
comp.port_map(pair.0, pair.1);
}
};
match self.wait {
Some(w) => comp
.with_cmd(self.args)
.with_env(self.env)
.with_wait_for(w)
.with_container_name(self.handle),
None => comp
.with_cmd(self.args)
.with_env(self.env)
.with_container_name(self.handle),
}
}
}
/// A helper function for generating random handles.
///
/// The returned handle is a combination of the given name and a random 10
/// character string.
pub fn new_handle(name: &str) -> String {
format!("{}{}", name, crate::common::rand_string(10))
}
#[cfg(test)]
mod tests {
#[test]
fn test_new_handle() {
let result = super::new_handle("test");
assert_eq!(result.len(), 14);
}
}