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
//! Represents the multiple phases and variants a docker container exists in dockertest.

mod cleanup;
mod operational;
mod pending;

pub(crate) use cleanup::CleanupContainer;
pub(crate) use operational::HostPortMappings;
pub use operational::OperationalContainer;
pub use pending::PendingContainer;

/// Represents an exisiting static external container.
///
// FIXME: does this need to be public?
#[derive(Clone)]
pub struct StaticExternalContainer {
    pub handle: String,
    pub id: String,
}

pub enum CreatedContainer {
    StaticExternal(StaticExternalContainer),
    Pending(PendingContainer),
}

#[cfg(test)]
mod tests {
    use crate::container::{CreatedContainer, OperationalContainer, PendingContainer};
    use crate::docker::Docker;
    use crate::image::Source;
    use crate::waitfor::{async_trait, WaitFor};
    use crate::{composition::Composition, DockerTestError, Network};

    use std::sync::{Arc, RwLock};

    #[derive(Clone, Debug)]
    struct TestWaitFor {
        invoked: Arc<RwLock<bool>>,
    }

    #[async_trait]
    impl WaitFor for TestWaitFor {
        async fn wait_for_ready(
            &self,
            container: PendingContainer,
        ) -> Result<OperationalContainer, DockerTestError> {
            let mut invoked = self.invoked.write().expect("failed to take invoked lock");
            *invoked = true;
            Ok(container.into())
        }
    }

    // Tests that the provided WaitFor trait object is invoked
    // during the start method of Composition
    #[tokio::test]
    async fn test_wait_for_invoked_during_start() {
        let wait_for = TestWaitFor {
            invoked: Arc::new(RwLock::new(false)),
        };

        let wrapped_wait_for = Box::new(wait_for);

        let client = Docker::new().unwrap();
        let repository = "dockertest-rs/hello".to_string();
        let mut composition =
            Composition::with_repository(repository).with_wait_for(wrapped_wait_for.clone());
        composition.container_name = "dockertest_wait_for_invoked_during_start".to_string();

        // Ensure image is present with id populated
        client
            .pull_image(composition.image(), &Source::Local)
            .await
            .expect("failed to pull image");

        // Create and start the container
        let pending = client
            .create_container(composition, None, &Network::Isolated)
            .await
            .expect("failed to create container");
        let container = match pending {
            CreatedContainer::Pending(c) => c,
            _ => panic!("expected pending created container"),
        };
        container.start().await.expect("failed to start container");

        let was_invoked = wrapped_wait_for
            .invoked
            .read()
            .expect("failed to get read lock");

        assert!(
            *was_invoked,
            "wait_for trait object was not invoked during startup"
        );
    }
}