Test containers implementation for MoonBit
Dependencies
moon test --target nativemoon test --target js{
"deps": {
"ryota0624/moonbit_test_containers": "0.1.0",
"moonbitlang/async": "0.16.5"
}
}{
"import": [
"moonbitlang/async",
"moonbitlang/async/process",
"moonbitlang/async/http"
]
}// Create and start a Redis container
let container = ContainerImage::new("redis", "7.2.4")
.with_exposed_port(6379)
.with_env_var("REDIS_PORT", "6379")
.start()
match container {
Ok(c) => {
// Get the host port
match c.get_host_port(6379) {
Some(port) => println("Redis running on port: \{port}")
None => println("Port not mapped")
}
// Cleanup when done
let _ = c.cleanup()
}
Err(e) => println("Error: \{e.to_string()}")
}let container = ContainerImage::new("postgres", "16")
.with_exposed_port(5432)
.with_env_var("POSTGRES_USER", "testuser")
.with_env_var("POSTGRES_PASSWORD", "testpass")
.with_env_var("POSTGRES_DB", "testdb")
.start()
match container {
Ok(c) => {
println("PostgreSQL container: \{c.get_id()}")
// Use the container in your tests
// ...
// Stop and remove separately
let _ = c.stop()
let _ = c.remove()
}
Err(e) => println("Failed to start: \{e.to_string()}")
}///|
let container = ContainerImage::new("nginx", "1.25-alpine")
.with_exposed_port(80)
.with_volume("./nginx.conf", "/etc/nginx/nginx.conf")
.start()///|
let container = ContainerImage::new("myapp", "latest")
.with_volume_ro("./config", "/app/config")
.start()///|
let container = ContainerImage::new("myapp", "latest")
.with_volume_ro("./config", "/app/config")
.with_volume("./data", "/app/data")
.with_volume("./logs", "/app/logs")
.start()let container = ContainerImage::new("postgres", "16")
.with_exposed_port(5432)
.with_env_var("POSTGRES_USER", "test")
.with_env_var("POSTGRES_PASSWORD", "test")
.with_env_var("POSTGRES_DB", "testdb")
.with_volume("./init.sql", "/docker-entrypoint-initdb.d/init.sql")
.with_wait_for(WaitStrategy::TcpPort(5432))
.with_wait_timeout(60)
.start()
match container {
Ok(c) => {
// PostgreSQL is now ready with your schema initialized
match c.get_host_port(5432) {
Some(port) => println("PostgreSQL ready on port: \{port}")
None => println("Port not mapped")
}
let _ = c.cleanup()
}
Err(e) => println("Failed: \{e.to_string()}")
}///|
let container = ContainerImage::new("redis", "7.2.4")
.with_exposed_port(6379)
.with_wait_for(WaitStrategy::TcpPort(6379))
.with_wait_timeout(30) // optional, defaults to 60 seconds
.start()let container = ContainerImage::new("nginx", "1.25-alpine")
.with_exposed_port(80)
.with_wait_for(WaitStrategy::HttpGet("/", 200))
.with_wait_timeout(30)
.start()
match container {
Ok(c) => {
// Nginx is now ready and responding to HTTP requests
match c.get_host_port(80) {
Some(port) => println("Nginx ready on port: \{port}")
None => println("Port not mapped")
}
let _ = c.cleanup()
}
Err(e) => println("Failed: \{e.to_string()}")
}// Wait for a health check endpoint to return 200
///|
let container = ContainerImage::new("myapp", "latest")
.with_exposed_port(8080)
.with_wait_for(WaitStrategy::HttpGet("/health", 200))
.with_wait_timeout(60)
.start()///|
let container = ContainerImage::new("alpine", "3.19")
.with_cmd(["sleep", "10"])
.with_wait_for(WaitStrategy::Seconds(5))
.start()///|
let container = ContainerImage::new("postgres", "15-alpine")
.with_exposed_port(5432)
.with_env_var("POSTGRES_PASSWORD", "password")
.with_wait_for(
WaitStrategy::LogMessage("database system is ready to accept connections"),
)
.with_wait_timeout(60)
.start()let container = ContainerImage::new("postgres", "16")
.with_exposed_port(5432)
.with_env_var("POSTGRES_USER", "test")
.with_env_var("POSTGRES_PASSWORD", "test")
.with_env_var("POSTGRES_DB", "test")
.with_wait_for(WaitStrategy::TcpPort(5432))
.with_wait_timeout(60)
.start()
match container {
Ok(c) => {
// PostgreSQL is now ready to accept connections
match c.get_host_port(5432) {
Some(port) => println("PostgreSQL ready on port: \{port}")
None => println("Port not mapped")
}
let _ = c.cleanup()
}
Err(e) => println("Failed: \{e.to_string()}")
}let container = ContainerImage::new("postgres", "15-alpine")
.with_exposed_port(5432)
.with_env_var("POSTGRES_PASSWORD", "password")
.with_wait_for(
WaitStrategy::LogMessage("database system is ready to accept connections")
)
.with_wait_timeout(60)
.start()
match container {
Ok(c) => {
// PostgreSQL is fully initialized and ready
match c.get_host_port(5432) {
Some(port) => println("PostgreSQL ready on port: \{port}")
None => println("Port not mapped")
}
let _ = c.cleanup()
}
Err(e) => println("Failed: \{e.to_string()}")
}///|
let container = ContainerImage::new("redis", "7.2.4")
.with_exposed_port(6379)
.with_wait_for(WaitStrategy::LogMessage("Ready to accept connections"))
.with_wait_timeout(30)
.start()///|
let container = ContainerImage::new("nginx", "1.25-alpine")
.with_exposed_port(80)
.with_wait_for(WaitStrategy::LogMessage("start worker processes"))
.with_wait_timeout(30)
.start()match container.get_logs() {
Ok(logs) => println("Container logs:\n\{logs}")
Err(e) => println("Failed to get logs: \{e.to_string()}")
}// Get last 100 lines with timestamps
let options = LogOptions::default()
.with_tail(100)
.with_timestamps(true)
match container.get_logs_with_options(options) {
Ok(logs) => println("Recent logs:\n\{logs}")
Err(e) => println("Failed to get logs: \{e.to_string()}")
}// Stream logs line by line
let callback = fn(line: String) {
println("Log: \{line}")
}
match container.stream_logs(callback) {
Ok(_) => println("Streaming complete")
Err(e) => println("Failed to stream: \{e.to_string()}")
}// Stream only recent logs
let options = LogOptions::default().with_tail(50)
match container.stream_logs(callback, options=options) {
Ok(_) => println("Streaming complete")
Err(e) => println("Failed to stream: \{e.to_string()}")
}let container = ContainerImage::new("myapp", "latest")
.with_exposed_port(8080)
.start()
match container {
Ok(c) => {
// If something goes wrong, check the logs
match c.get_logs() {
Ok(logs) => {
if logs.contains("ERROR") {
println("Container error detected:\n\{logs}")
}
}
Err(e) => println("Failed to get logs: \{e.to_string()}")
}
let _ = c.cleanup()
}
Err(e) => println("Failed to start: \{e.to_string()}")
}# Native backend
moon check --target native
# JavaScript backend
moon check --target js# Native backend (recommended)
moon test --target native
# JavaScript backend (requires Node.js)
moon test --target js
# Both backends
moon test --target native && moon test --target jsasync fn Container::get_logs_with_options(self : Container, options : LogOptions) -> Result[String, TestContainerError]async fn Container::stream_logs(self : Container, callback : (String) -> Unit, options? : LogOptions) -> Result[Unit, TestContainerError]pub struct ContainerImage {
name : String
tag : String
exposed_ports : Array[Int]
env_vars : Array[(String, String)]
cmd : Array[String]
wait_strategy : WaitStrategy?
wait_timeout : Int
volumes : Array[VolumeMount]
}async fn ContainerImage::start(self : ContainerImage) -> Result[Container, TestContainerError] noraisefn ContainerImage::with_env_var(self : ContainerImage, key : String, value : String) -> ContainerImagefn ContainerImage::with_volume(self : ContainerImage, host_path : String, container_path : String) -> ContainerImagefn ContainerImage::with_volume_ro(self : ContainerImage, host_path : String, container_path : String) -> ContainerImagepub struct LogOptions {
follow : Bool
timestamps : Bool
tail : Int?
since : String?
until : String?
}pub struct VolumeMount {
host_path : String
container_path : String
read_only : Bool
}fn parse_port_mapping(output : String) -> Int?Install
Download zipTest containers implementation for MoonBit
Dependencies