From eb5f7550a09dd5d325c149a279c385edf6d90029 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Tue, 7 Apr 2026 07:47:17 -0300 Subject: [PATCH] feat(fakecloud): add testcontainers module for fakecloud --- Cargo.toml | 5 +++ examples/fakecloud.rs | 28 +++++++++++++++ src/fakecloud/mod.rs | 83 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 +++ 4 files changed, 120 insertions(+) create mode 100644 examples/fakecloud.rs create mode 100644 src/fakecloud/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 7e8c891..d47a956 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ dynamodb = [] databend = ["http_wait"] elastic_search = [] elasticmq = [] +fakecloud = [] gitea = ["http_wait", "dep:rcgen"] google_cloud_sdk_emulators = [] hashicorp_vault = ["http_wait"] @@ -198,3 +199,7 @@ required-features = ["azurite"] [[example]] name = "selenium" required-features = ["selenium"] + +[[example]] +name = "fakecloud" +required-features = ["fakecloud"] diff --git a/examples/fakecloud.rs b/examples/fakecloud.rs new file mode 100644 index 0000000..e8feb71 --- /dev/null +++ b/examples/fakecloud.rs @@ -0,0 +1,28 @@ +use testcontainers_modules::{ + fakecloud::{FakeCloud, FAKECLOUD_PORT}, + testcontainers::runners::AsyncRunner, +}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = pretty_env_logger::try_init(); + + let node = FakeCloud::default().start().await?; + let host_ip = node.get_host().await?; + let host_port = node.get_host_port_ipv4(FAKECLOUD_PORT).await?; + + println!("fakecloud running at http://{}:{}", host_ip, host_port); + + let client = reqwest::Client::new(); + let response = client + .get(format!( + "http://{}:{}/_fakecloud/health", + host_ip, host_port + )) + .send() + .await?; + + println!("Health check: {}", response.text().await?); + + Ok(()) +} diff --git a/src/fakecloud/mod.rs b/src/fakecloud/mod.rs new file mode 100644 index 0000000..95164bd --- /dev/null +++ b/src/fakecloud/mod.rs @@ -0,0 +1,83 @@ +use testcontainers::{core::WaitFor, Image}; + +/// Container port for the fakecloud HTTP API. +pub const FAKECLOUD_PORT: u16 = 4566; + +const NAME: &str = "ghcr.io/faiscadev/fakecloud"; +const TAG: &str = "0.4.0"; + +/// [fakecloud](https://fakecloud.dev) is a free, open-source local AWS cloud emulator. +/// +/// Supports S3, SQS, SNS, EventBridge, IAM/STS, SSM, DynamoDB, Lambda, +/// Secrets Manager, CloudWatch Logs, KMS, SES, and CloudFormation. +/// +/// Currently pinned to [version `0.4.0`](https://github.com/faiscadev/fakecloud/releases/tag/v0.4.0). +/// +/// # Configuration +/// +/// fakecloud uses environment variables for configuration. See the +/// [documentation](https://fakecloud.dev) for the full list. +/// +/// ``` +/// use testcontainers_modules::{fakecloud::FakeCloud, testcontainers::ImageExt}; +/// +/// let container_request = FakeCloud::default().with_env_var("FAKECLOUD_LOG", "debug"); +/// ``` +/// +/// No environment variables are required. +#[derive(Default, Debug, Clone)] +pub struct FakeCloud { + _priv: (), +} + +impl Image for FakeCloud { + fn name(&self) -> &str { + NAME + } + + fn tag(&self) -> &str { + TAG + } + + fn ready_conditions(&self) -> Vec { + vec![WaitFor::message_on_stdout("fakecloud is ready")] + } +} + +#[cfg(test)] +mod tests { + use aws_config::{meta::region::RegionProviderChain, BehaviorVersion}; + use aws_sdk_sqs as sqs; + use testcontainers::runners::AsyncRunner; + + use super::FakeCloud; + + #[tokio::test] + #[allow(clippy::result_large_err)] + async fn create_and_list_queue() -> Result<(), Box> { + let node = FakeCloud::default().start().await?; + let host_ip = node.get_host().await?; + let host_port = node.get_host_port_ipv4(4566).await?; + + let region_provider = RegionProviderChain::default_provider().or_else("us-east-1"); + let creds = sqs::config::Credentials::new("test", "test", None, None, "test"); + let config = aws_config::defaults(BehaviorVersion::v2025_08_07()) + .region(region_provider) + .credentials_provider(creds) + .endpoint_url(format!("http://{host_ip}:{host_port}")) + .load() + .await; + let client = sqs::Client::new(&config); + + client + .create_queue() + .queue_name("example-queue") + .send() + .await?; + + let list_result = client.list_queues().send().await?; + assert_eq!(list_result.queue_urls().len(), 1); + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8751541..632d711 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,10 @@ pub mod elastic_search; #[cfg_attr(docsrs, doc(cfg(feature = "elasticmq")))] /// **ElasticMQ** (message queue) testcontainer pub mod elasticmq; +#[cfg(feature = "fakecloud")] +#[cfg_attr(docsrs, doc(cfg(feature = "fakecloud")))] +/// **fakecloud** (local AWS cloud emulator) testcontainer +pub mod fakecloud; #[cfg(feature = "gitea")] #[cfg_attr(docsrs, doc(cfg(feature = "gitea")))] /// **Gitea** (self-hosted Git service) testcontainer