testcontainers_modules/oracle/
free.rs

1use std::borrow::Cow;
2
3use testcontainers::{
4    core::{ContainerPort, WaitFor},
5    Image,
6};
7
8const DEFAULT_IMAGE_NAME: &str = "gvenzl/oracle-free";
9const DEFAULT_IMAGE_TAG: &str = "23-slim-faststart";
10/// Port that the [`Oracle Database Free`] container has internally
11/// Can be rebound externally via [`testcontainers::core::ImageExt::with_mapped_port`]
12///
13/// [`Oracle Database Free`]: https://www.oracle.com/database/free/
14pub const FREE_PORT: ContainerPort = ContainerPort::Tcp(1521);
15
16/// Module to work with [`Oracle Database Free`] inside of tests.
17/// The default image is [`gvenzl/oracle-free:23-slim-faststart`] (unofficial).
18/// Official dockerfiles can be found [here][Oracle official dockerfiles].
19///
20/// The default schema is `test`, with a password `test`.
21///
22/// NOTE: Currently, there is no Oracle Database Free port for ARM chips,
23/// hence Oracle Database Free images cannot run on the new Apple M chips via Docker Desktop.
24///
25/// # Example
26/// ```
27/// use std::time::Duration;
28/// use testcontainers_modules::{oracle::free::Oracle, testcontainers::{runners::SyncRunner, ImageExt}};
29///
30/// // On slower machines more time needed than 60 seconds may be required (see `with_startup_timeout`).
31/// let oracle = Oracle::default()
32///     .start()
33///     .unwrap();
34///
35/// let http_port = oracle.get_host_port_ipv4(1521).unwrap();
36///
37/// // do something with the started Oracle instance..
38/// ```
39///
40/// [`Oracle Database Free`]: https://www.oracle.com/database/free/
41/// [Oracle official dockerfiles]: https://github.com/oracle/docker-images/tree/main/OracleDatabase
42/// [`gvenzl/oracle-free:23-slim-faststart`]: https://hub.docker.com/r/gvenzl/oracle-free
43#[derive(Debug, Default, Clone)]
44pub struct Oracle {
45    /// (remove if there is another variable)
46    /// Field is included to prevent this struct to be a unit struct.
47    /// This allows extending functionality (and thus further variables) without breaking changes
48    _priv: (),
49}
50
51impl Image for Oracle {
52    fn name(&self) -> &str {
53        DEFAULT_IMAGE_NAME
54    }
55
56    fn tag(&self) -> &str {
57        DEFAULT_IMAGE_TAG
58    }
59
60    fn ready_conditions(&self) -> Vec<WaitFor> {
61        vec![WaitFor::message_on_stdout("DATABASE IS READY TO USE!")]
62    }
63
64    fn env_vars(
65        &self,
66    ) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
67        [
68            ("ORACLE_PASSWORD", "testsys"),
69            ("APP_USER", "test"),
70            ("APP_USER_PASSWORD", "test"),
71        ]
72    }
73
74    fn expose_ports(&self) -> &[ContainerPort] {
75        &[FREE_PORT]
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use std::time::Duration;
82
83    use super::*;
84    use crate::testcontainers::{runners::SyncRunner, ImageExt};
85
86    // remember to provide Oracle client 11.2 or later (see https://crates.io/crates/oracle)
87
88    #[test]
89    fn oracle_one_plus_one() -> Result<(), Box<dyn std::error::Error + 'static>> {
90        let oracle = Oracle::default()
91            .pull_image()?
92            .with_startup_timeout(Duration::from_secs(75));
93
94        let node = oracle.start()?;
95
96        let connection_string = format!(
97            "//{}:{}/FREEPDB1",
98            node.get_host()?,
99            node.get_host_port_ipv4(1521)?
100        );
101        let conn = oracle::Connection::connect("test", "test", connection_string)?;
102
103        let mut rows = conn.query("SELECT 1 + 1", &[])?;
104        let row = rows.next().unwrap()?;
105        let col: i32 = row.get(0)?;
106        assert_eq!(col, 2);
107        Ok(())
108    }
109}