aster_forge_test/
mysql.rs1use crate::state::{ContainerLease, ContainerStateLock};
9use crate::suite::TestContainerSuite;
10use testcontainers::core::{ContainerAsync, IntoContainerPort, WaitFor};
11use testcontainers::{GenericImage, ImageExt, ReuseDirective, runners::AsyncRunner};
12
13pub struct MysqlTestContainer {
15 root_url: String,
16 suite: TestContainerSuite,
17 stale_resources: Vec<String>,
18 _container: ContainerAsync<GenericImage>,
19 _lease: ContainerLease,
20}
21
22impl MysqlTestContainer {
23 pub async fn start(suite: &TestContainerSuite) -> Self {
25 let lock = ContainerStateLock::acquire(suite, "mysql");
26 let mut state = lock.load();
27 let stale_resources = state.prune_stale();
28 state.register_pid(std::process::id());
29 lock.save(&state);
30
31 let container = GenericImage::new("mysql", "8.4")
32 .with_exposed_port(IntoContainerPort::tcp(3306))
33 .with_wait_for(WaitFor::message_on_stdout("ready for connections"))
34 .with_container_name(suite.container_name("mysql"))
35 .with_reuse(ReuseDirective::Always)
36 .with_env_var("MYSQL_ROOT_PASSWORD", "rootpass")
37 .start()
38 .await
39 .expect("failed to start MySQL test container");
40 let port = container
41 .get_host_port_ipv4(IntoContainerPort::tcp(3306))
42 .await
43 .expect("MySQL test port should be exposed");
44 drop(lock);
45
46 Self {
47 root_url: format!("mysql://root:rootpass@127.0.0.1:{port}/mysql"),
48 suite: suite.clone(),
49 stale_resources,
50 _container: container,
51 _lease: ContainerLease::new(suite.clone(), "mysql"),
52 }
53 }
54
55 pub fn root_url(&self) -> &str {
57 &self.root_url
58 }
59
60 pub fn database_url(&self, database: &str) -> String {
62 self.root_url
63 .rsplit_once('/')
64 .map(|(base, _)| format!("{base}/{database}"))
65 .unwrap_or_else(|| self.root_url.clone())
66 }
67
68 pub fn stale_resources(&self) -> &[String] {
70 &self.stale_resources
71 }
72
73 pub fn remember_resource(&self, resource: &str) {
75 let lock = ContainerStateLock::acquire(&self.suite, "mysql");
76 let mut state = lock.load();
77 state.remember_resource(std::process::id(), resource);
78 lock.save(&state);
79 }
80
81 pub fn forget_resource(&self, resource: &str) {
83 let lock = ContainerStateLock::acquire(&self.suite, "mysql");
84 let mut state = lock.load();
85 state.forget_resource(std::process::id(), resource);
86 lock.save(&state);
87 }
88}