From 2ab293a5469702cba5238675d201f03569c11db7 Mon Sep 17 00:00:00 2001 From: Charles GTE Date: Mon, 30 Mar 2026 19:00:44 +0200 Subject: [PATCH 1/3] feat: add firebird --- Makefile | 7 ++++- databases.json | 10 ++++++ docker-compose.yml | 19 ++++++++++++ docker/Dockerfile | 1 + scripts/firebird/seed.sql | 11 +++++++ src/domain/factory.rs | 3 ++ src/domain/firebird/backup.rs | 48 ++++++++++++++++++++++++++++ src/domain/firebird/database.rs | 48 ++++++++++++++++++++++++++++ src/domain/firebird/mod.rs | 4 +++ src/domain/firebird/ping.rs | 55 +++++++++++++++++++++++++++++++++ src/domain/firebird/restore.rs | 40 ++++++++++++++++++++++++ src/domain/mod.rs | 1 + src/services/config.rs | 4 +++ 13 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 scripts/firebird/seed.sql create mode 100644 src/domain/firebird/backup.rs create mode 100644 src/domain/firebird/database.rs create mode 100644 src/domain/firebird/mod.rs create mode 100644 src/domain/firebird/ping.rs create mode 100644 src/domain/firebird/restore.rs diff --git a/Makefile b/Makefile index b05eebb..3914ac9 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,11 @@ seed-sqlite: docker exec -it db-sqlite sqlite3 /workspace/data/app.db "SELECT name FROM sqlite_master WHERE type='table';" @echo "Done" - +seed-firebird: + @echo "Seeding Firebird..." + docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb < ./scripts/firebird/seed.sql + @echo "Verifying Firebird tables..." + docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb \ + -sql "SHOW TABLES;" seed-all: seed-mongo seed-mysql seed-postgres seed-postgres-1gb diff --git a/databases.json b/databases.json index 9fbfdc0..71c878e 100644 --- a/databases.json +++ b/databases.json @@ -91,6 +91,16 @@ "port": 3306, "host": "db-mysql", "generated_id": "16656124-ff7e-4c97-8c83-0adeff214681" + }, + { + "name": "Test database 13 - Firebird", + "database": "/var/lib/firebird/data/mirror.fdb", + "type": "firebird", + "username": "alice", + "password": "fake_password", + "port": 3050, + "host": "db-firebird", + "generated_id": "16706124-ff7e-4c97-8c83-0adeff214681" } ] } diff --git a/docker-compose.yml b/docker-compose.yml index 46eedf6..f097e39 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -167,6 +167,24 @@ services: # networks: # - portabase + db-firebird: + image: firebirdsql/firebird + container_name: db-firebird + restart: always + environment: + - FIREBIRD_ROOT_PASSWORD=fake_root_password + - FIREBIRD_USER=alice + - FIREBIRD_PASSWORD=fake_password + - FIREBIRD_DATABASE=mirror.fdb + - FIREBIRD_DATABASE_DEFAULT_CHARSET=UTF8 + volumes: + - firebird-data:/var/lib/firebird/data + ports: + - "3060:3050" + + networks: + - portabase + volumes: cargo-registry: cargo-git: @@ -181,6 +199,7 @@ volumes: # redis-data-auth: # valkey-data: # valkey-data-auth: + firebird-data: networks: portabase: diff --git a/docker/Dockerfile b/docker/Dockerfile index 5ed6453..975185e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,6 +18,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ sqlite3 \ redis-tools \ valkey \ + firebird3.0-utils \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/scripts/firebird/seed.sql b/scripts/firebird/seed.sql new file mode 100644 index 0000000..fc58da1 --- /dev/null +++ b/scripts/firebird/seed.sql @@ -0,0 +1,11 @@ +CREATE TABLE users ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + name VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice'); +INSERT INTO users (email, name) VALUES ('bob@example.com', 'Bob'); + +COMMIT; \ No newline at end of file diff --git a/src/domain/factory.rs b/src/domain/factory.rs index 279f4a0..c32fda8 100644 --- a/src/domain/factory.rs +++ b/src/domain/factory.rs @@ -9,6 +9,7 @@ use crate::services::config::{DatabaseConfig, DbType}; use anyhow::Result; use std::path::{Path, PathBuf}; use std::sync::Arc; +use crate::domain::firebird::database::FirebirdDatabase; use crate::domain::mariadb::database::MariaDBDatabase; #[async_trait::async_trait] @@ -34,6 +35,7 @@ impl DatabaseFactory { DbType::Sqlite => Arc::new(SqliteDatabase::new(cfg)), DbType::Redis => Arc::new(RedisDatabase::new(cfg)), DbType::Valkey => Arc::new(ValkeyDatabase::new(cfg)), + DbType::Firebird => Arc::new(FirebirdDatabase::new(cfg)), } } @@ -49,6 +51,7 @@ impl DatabaseFactory { DbType::Sqlite => Arc::new(SqliteDatabase::new(cfg)), DbType::Redis => Arc::new(RedisDatabase::new(cfg)), DbType::Valkey => Arc::new(ValkeyDatabase::new(cfg)), + DbType::Firebird => Arc::new(FirebirdDatabase::new(cfg)), } } } diff --git a/src/domain/firebird/backup.rs b/src/domain/firebird/backup.rs new file mode 100644 index 0000000..b1d90bf --- /dev/null +++ b/src/domain/firebird/backup.rs @@ -0,0 +1,48 @@ +use crate::services::config::DatabaseConfig; +use anyhow::{Context, Result}; +use std::path::PathBuf; +use std::process::Command; +use tracing::{debug, error, info}; + +pub async fn run( + cfg: DatabaseConfig, + backup_dir: PathBuf, + file_extension: &'static str, +) -> Result { + tokio::task::spawn_blocking(move || -> Result { + debug!("Starting backup for database {}", cfg.name); + + let file_path = backup_dir.join(format!("{}{}", cfg.generated_id, file_extension)); + + let db_path = format!( + "{}/{}:{}", + cfg.host, + cfg.port, + cfg.database + ); + + info!("Firebird database target: {}", db_path); + info!("Backup file: {}", file_path.display()); + + let output = Command::new("gbak") + .arg("-b") + .arg("-v") + .arg("-user").arg(&cfg.username) + .arg("-password").arg(&cfg.password) + .arg(db_path) + .arg(&file_path) + .output() + .with_context(|| format!("Failed to run gbak for {}", cfg.name))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + error!("Firebird backup failed: {}", stderr); + anyhow::bail!("Firebird backup failed for {}: {}", cfg.name, stderr); + } + + info!("Firebird backup completed: {}", file_path.display()); + + Ok(file_path) + }) + .await? +} diff --git a/src/domain/firebird/database.rs b/src/domain/firebird/database.rs new file mode 100644 index 0000000..b2af6da --- /dev/null +++ b/src/domain/firebird/database.rs @@ -0,0 +1,48 @@ +use super::{backup, ping, restore}; +use crate::domain::factory::Database; +use crate::services::config::DatabaseConfig; +use crate::utils::locks::{DbOpLock, FileLock}; +use anyhow::Result; +use async_trait::async_trait; +use std::path::{Path, PathBuf}; + +pub struct FirebirdDatabase { + cfg: DatabaseConfig, +} + +impl FirebirdDatabase { + pub fn new(cfg: DatabaseConfig) -> Self { + Self { cfg } + } + +} + +#[async_trait] +impl Database for FirebirdDatabase { + fn file_extension(&self) -> &'static str { + ".fbk" + } + + async fn ping(&self) -> Result { + ping::run(self.cfg.clone()).await + } + + async fn backup(&self, dir: &Path) -> Result { + FileLock::acquire(&self.cfg.generated_id, DbOpLock::Backup.as_str()).await?; + let res = backup::run( + self.cfg.clone(), + dir.to_path_buf(), + self.file_extension(), + ) + .await; + FileLock::release(&self.cfg.generated_id).await?; + res + } + + async fn restore(&self, file: &Path) -> Result<()> { + FileLock::acquire(&self.cfg.generated_id, DbOpLock::Restore.as_str()).await?; + let res = restore::run(self.cfg.clone(), file.to_path_buf()).await; + FileLock::release(&self.cfg.generated_id).await?; + res + } +} diff --git a/src/domain/firebird/mod.rs b/src/domain/firebird/mod.rs new file mode 100644 index 0000000..057b272 --- /dev/null +++ b/src/domain/firebird/mod.rs @@ -0,0 +1,4 @@ +pub mod database; +mod ping; +mod backup; +mod restore; \ No newline at end of file diff --git a/src/domain/firebird/ping.rs b/src/domain/firebird/ping.rs new file mode 100644 index 0000000..2082a80 --- /dev/null +++ b/src/domain/firebird/ping.rs @@ -0,0 +1,55 @@ +use std::process::Stdio; +use tracing::{error, info}; +use tokio::io::AsyncWriteExt; +use crate::services::config::DatabaseConfig; +use tokio::process::Command; +use tokio::time::{Duration, timeout}; + +pub async fn run(cfg: DatabaseConfig) -> anyhow::Result { + let db_path = format!("{}/{}:{}", cfg.host, cfg.port, cfg.database); + + let mut child = Command::new("isql-fb") + .arg("-q") + .arg("-user") + .arg(&cfg.username) + .arg("-password") + .arg(&cfg.password) + .arg(&db_path) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + let query = b"SELECT 1 FROM RDB$DATABASE;\nQUIT;\n"; + + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(query).await?; + } + + let output = match timeout(Duration::from_secs(5), child.wait_with_output()).await { + Ok(res) => res?, + Err(_) => return Ok(false), + }; + + if !output.status.success() { + error!("Error output for firebird: {:?}", output); + return Ok(false); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + if stderr.to_lowercase().contains("error") { + error!("Error output for firebird: {:?}", output); + return Ok(false); + } + + if stdout.contains("1") { + return Ok(true); + } + + info!("stdout {}", stdout); + error!("stderr {}", stderr); + + Ok(false) +} diff --git a/src/domain/firebird/restore.rs b/src/domain/firebird/restore.rs new file mode 100644 index 0000000..b573014 --- /dev/null +++ b/src/domain/firebird/restore.rs @@ -0,0 +1,40 @@ +use crate::services::config::DatabaseConfig; +use anyhow::{Context, Result}; +use std::path::PathBuf; +use std::process::Command; +use tracing::{debug, error, info}; + +pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { + tokio::task::spawn_blocking(move || -> Result<()> { + debug!("Starting MongoDB restore for database {}", cfg.name); + + let db_path = format!("{}/{}:{}", cfg.host, cfg.port, cfg.database); + + info!("Restore source: {}", restore_file.display()); + info!("Restore target: {}", db_path); + + let output = Command::new("gbak") + .arg("-c") + .arg("-v") + .arg("-replace_database") + .arg("-user") + .arg(&cfg.username) + .arg("-password") + .arg(&cfg.password) + .arg(&restore_file) + .arg(&db_path) + .output() + .with_context(|| format!("Failed to run gbak restore for {}", cfg.name))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + error!("Firebird restore failed for {}: {}", cfg.name, stderr); + anyhow::bail!("Firebird restore failed for {}: {}", cfg.name, stderr); + } + + info!("Firebird restore completed for {}", cfg.name); + + Ok(()) + }) + .await? +} diff --git a/src/domain/mod.rs b/src/domain/mod.rs index ab005c3..b24891d 100644 --- a/src/domain/mod.rs +++ b/src/domain/mod.rs @@ -6,3 +6,4 @@ mod redis; mod sqlite; mod valkey; mod mariadb; +mod firebird; diff --git a/src/services/config.rs b/src/services/config.rs index b1d5a3b..c16cc6c 100644 --- a/src/services/config.rs +++ b/src/services/config.rs @@ -21,6 +21,7 @@ pub enum DbType { Sqlite, Redis, Valkey, + Firebird } impl DbType { @@ -33,6 +34,7 @@ impl DbType { DbType::Sqlite => "sqlite", DbType::Redis => "redis", DbType::Valkey => "valkey", + DbType::Firebird => "firebird", } } } @@ -182,6 +184,7 @@ impl ConfigService { | DbType::Mariadb | DbType::MongoDB | DbType::Redis + | DbType::Firebird | DbType::Valkey => required(&db.host, &db.name, "host")?, DbType::Sqlite => optional(&db.host), }; @@ -192,6 +195,7 @@ impl ConfigService { | DbType::Mariadb | DbType::MongoDB | DbType::Redis + | DbType::Firebird | DbType::Valkey => required(&db.port, &db.name, "port")?, DbType::Sqlite => db.port.unwrap_or(0), }; From 014eb16074892bf643df64a8bfca00d7bf461b70 Mon Sep 17 00:00:00 2001 From: Charles GTE Date: Tue, 31 Mar 2026 12:15:27 +0200 Subject: [PATCH 2/3] feat: add firebird tests --- Makefile | 59 ------- docker-compose.databases.yml | 177 +++++++++++++++++++++ docker-compose.yml | 186 ++--------------------- docker/entrypoints/app-dev-entrypoint.sh | 3 +- justfile | 54 +++++++ scripts/sqlite/seed.sh | 14 ++ src/domain/firebird/ping.rs | 2 + src/domain/redis/ping.rs | 5 +- src/domain/valkey/ping.rs | 6 +- src/services/status.rs | 3 + src/tests/domain/firebird.rs | 107 +++++++++++++ src/tests/domain/mod.rs | 1 + 12 files changed, 371 insertions(+), 246 deletions(-) delete mode 100644 Makefile create mode 100644 docker-compose.databases.yml create mode 100644 justfile create mode 100644 scripts/sqlite/seed.sh create mode 100644 src/tests/domain/firebird.rs diff --git a/Makefile b/Makefile deleted file mode 100644 index 3914ac9..0000000 --- a/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -include .env -export $(shell sed 's/=.*//' .env) -CLUSTER_SCRIPT=docker/entrypoints/app-dev-entrypoint.sh - -.PHONY: seed-mongo seed-mysql seed-postgres - -up: - @bash $(CLUSTER_SCRIPT) - -seed-mongo: - @echo "Seeding MongoDB..." - bash ./scripts/mongo/seed-mongo.sh - -seed-mongo-auth: - @echo "Seeding MongoDB with auth..." - bash ./scripts/mongo/seed-mongo.sh auth - -seed-mysql: - @echo "Seeding MySQL..." - mysql -h 127.0.0.1 -P "$$MYSQL_PORT" -u "$$MYSQL_USER" -p"$$MYSQL_PASSWORD" "$$MYSQL_DB" < ./scripts/mysql/seed-mysql.sql - -seed-mysql-1gb: - @echo "Seeding MySQL..." - mysql -h 127.0.0.1 -P "$$MYSQL_PORT" -u "$$MYSQL_USER" -p"$$MYSQL_PASSWORD" "$$MYSQL_DB" < ./scripts/mysql/seed-1gb.sql - - -seed-postgres: - @echo "Seeding Postgres..." - docker exec -i -e PGPASSWORD=$$PG_PASSWORD $$PG_CONTAINER \ - psql -U $$PG_USER -d $$PG_DB < ./scripts/postgres/seed.sql - -seed-postgres-1gb: - @echo "Seeding Postgres..." - docker exec -i -e PGPASSWORD=$$PG_PASSWORD $$PG_CONTAINER \ - psql -U $$PG_USER -d $$PG_DB < ./scripts/postgres/seed-1gb.sql - - -SQLITE_SEED_FILE := $(if $(filter big,$(SEED)),./scripts/sqlite/seed-big.sql,./scripts/sqlite/seed.sql) - -seed-sqlite: - @echo "Seeding Sqlite..." - @echo "Run as root to fix permissions inside the volume" - docker exec -u 0 -it db-sqlite sh -c "chmod -R 777 /workspace/data" - @echo "Create the database file (if it doesn’t exist)" - docker exec -u 0 -it db-sqlite sh -c "touch /workspace/data/app.db" - @echo "Seed the database" - docker exec -i db-sqlite sh -c "sqlite3 /workspace/data/app.db" < $(SQLITE_SEED_FILE) - @echo "Verify" - docker exec -it db-sqlite sqlite3 /workspace/data/app.db "SELECT name FROM sqlite_master WHERE type='table';" - @echo "Done" - -seed-firebird: - @echo "Seeding Firebird..." - docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb < ./scripts/firebird/seed.sql - @echo "Verifying Firebird tables..." - docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb \ - -sql "SHOW TABLES;" - -seed-all: seed-mongo seed-mysql seed-postgres seed-postgres-1gb diff --git a/docker-compose.databases.yml b/docker-compose.databases.yml new file mode 100644 index 0000000..67abe07 --- /dev/null +++ b/docker-compose.databases.yml @@ -0,0 +1,177 @@ +services: + db-postgres: + container_name: db-postgres + image: postgres:17-alpine + ports: + - "5436:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=devdb + - POSTGRES_USER=devuser + - POSTGRES_PASSWORD=changeme + networks: + - portabase + + db-mariadb: + container_name: db-mariadb + image: mariadb:latest + ports: + - "3311:3306" + environment: + - MYSQL_DATABASE=mariadb + - MYSQL_USER=mariadb + - MYSQL_PASSWORD=changeme + - MYSQL_RANDOM_ROOT_PASSWORD=yes + volumes: + - mariadb-data:/var/lib/mysql + networks: + - portabase + + db-mysql: + container_name: db-mysql + image: mysql:9.5 + ports: + - "3312:3306" + environment: + - MYSQL_DATABASE=mysqldb + - MYSQL_USER=mysqldb + - MYSQL_PASSWORD=changeme + - MYSQL_RANDOM_ROOT_PASSWORD=yes + volumes: + - mysql-data:/var/lib/mysql + networks: + - portabase + + + db-mongodb-auth: + container_name: db-mongodb-auth + image: mongo:latest + ports: + - "27082:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: rootpassword + MONGO_INITDB_DATABASE: testdbauth + command: mongod --auth + networks: + - portabase + volumes: + - mongodb-data-auth:/data/db + healthcheck: + test: [ "CMD", "mongo", "--eval", "db.adminCommand('ping')" ] + interval: 5s + timeout: 5s + retries: 10 + + db-mongodb: + container_name: db-mongodb + image: mongo:latest + ports: + - "27083:27017" + volumes: + - mongodb-data:/data/db + healthcheck: + test: [ "CMD", "mongosh", "--eval", "db.adminCommand('ping')" ] + interval: 5s + timeout: 5s + retries: 10 + environment: + MONGO_INITDB_DATABASE: testdb + networks: + - portabase + + sqlite: + container_name: db-sqlite + image: keinos/sqlite3 + volumes: + - sqlite-data:/workspace/data + working_dir: /workspace + command: tail -f /dev/null + stdin_open: true + tty: true + + db-redis: + image: redis:latest + container_name: db-redis + ports: + - "6379:6379" + volumes: + - redis-data:/data + command: [ "redis-server", "--appendonly", "yes" ] + networks: + - portabase + + db-redis-auth: + image: redis:latest + container_name: db-redis-auth + ports: + - "6380:6379" + volumes: + - redis-data-auth:/data + environment: + - REDIS_PASSWORD=supersecurepassword + command: [ "redis-server", "--requirepass", "supersecurepassword", "--appendonly", "yes" ] + networks: + - portabase + + db-valkey: + image: valkey/valkey + container_name: db-valkey + environment: + - ALLOW_EMPTY_PASSWORD=yes + ports: + - '6381:6379' + volumes: + - valkey-data:/data + networks: + - portabase + + db-valkey-auth: + image: valkey/valkey + container_name: db-valkey-auth + command: > + --requirepass "supersecurepassword" + ports: + - '6382:6379' + volumes: + - valkey-data-auth:/data + networks: + - portabase + + db-firebird: + image: firebirdsql/firebird + container_name: db-firebird + restart: always + environment: + - FIREBIRD_ROOT_PASSWORD=fake_root_password + - FIREBIRD_USER=alice + - FIREBIRD_PASSWORD=fake_password + - FIREBIRD_DATABASE=mirror.fdb + - FIREBIRD_DATABASE_DEFAULT_CHARSET=UTF8 + volumes: + - firebird-data:/var/lib/firebird/data + ports: + - "3060:3050" + + networks: + - portabase + +volumes: + postgres-data: + mariadb-data: + mysql-data: + mongodb-data: + mongodb-data-auth: + sqlite-data: + redis-data: + redis-data-auth: + valkey-data: + valkey-data-auth: + firebird-data: + +networks: + portabase: + name: portabase_network + external: true + diff --git a/docker-compose.yml b/docker-compose.yml index f097e39..e90f0af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,19 +7,19 @@ services: container_name: rust-dev volumes: - .:/app - - ./databases.json:/config/config.json - # - ./databases.toml:/config/config.toml - cargo-registry:/usr/local/cargo/registry - cargo-git:/usr/local/cargo/git -# - /var/run/docker.sock:/var/run/docker.sock - # - cargo-target:/app/target - # - sqlite-data:/sqlite-data/workspace/data - # - ./scripts/sqlite/test-db:/sqlite-data-2/workspace/data + - ./databases.json:/config/config.json + #- ./databases.toml:/config/config.toml + #- /var/run/docker.sock:/var/run/docker.sock + #- cargo-target:/app/target + #- sqlite-data:/sqlite-data/workspace/data + #- ./scripts/sqlite/test-db:/sqlite-data-2/workspace/data environment: APP_ENV: development LOG: debug TZ: "Europe/Paris" - EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiNGI1OTM2MGItNTNkMi00ZTZmLWE1ODctODcyMmQ1NDc1MTNmIiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ==" + EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiZWQzNDNmOTUtNTU0Yi00NTAwLTkzZjAtMzEyODQzMjQ2M2RhIiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ==" #POOLING: 1 #DATABASES_CONFIG_FILE: "config.toml" extra_hosts: @@ -27,179 +27,11 @@ services: networks: - portabase -# db-postgres: -# container_name: db-postgres -# image: postgres:17-alpine -# ports: -# - "5436:5432" -# volumes: -# - postgres-data:/var/lib/postgresql/data -# environment: -# - POSTGRES_DB=devdb -# - POSTGRES_USER=devuser -# - POSTGRES_PASSWORD=changeme -# networks: -# - portabase - - db-mariadb: - container_name: db-mariadb - image: mariadb:latest - ports: - - "3311:3306" - environment: - - MYSQL_DATABASE=mariadb - - MYSQL_USER=mariadb - - MYSQL_PASSWORD=changeme - - MYSQL_RANDOM_ROOT_PASSWORD=yes - volumes: - - mariadb-data:/var/lib/mysql - networks: - - portabase - - db-mysql: - container_name: db-mysql - image: mysql:9.5 - ports: - - "3312:3306" - environment: - - MYSQL_DATABASE=mysqldb - - MYSQL_USER=mysqldb - - MYSQL_PASSWORD=changeme - - MYSQL_RANDOM_ROOT_PASSWORD=yes - volumes: - - mysql-data:/var/lib/mysql - networks: - - portabase - # - # - # db-mongodb-auth: - # container_name: db-mongodb-auth - # image: mongo:latest - # ports: - # - "27082:27017" - # environment: - # MONGO_INITDB_ROOT_USERNAME: root - # MONGO_INITDB_ROOT_PASSWORD: rootpassword - # MONGO_INITDB_DATABASE: testdbauth - # command: mongod --auth - # networks: - # - portabase - # volumes: - # - mongodb-data-auth:/data/db - # healthcheck: - # test: [ "CMD", "mongo", "--eval", "db.adminCommand('ping')" ] - # interval: 5s - # timeout: 5s - # retries: 10 - # - # db-mongodb: - # container_name: db-mongodb - # image: mongo:latest - # ports: - # - "27083:27017" - # volumes: - # - mongodb-data:/data/db - # healthcheck: - # test: [ "CMD", "mongosh", "--eval", "db.adminCommand('ping')" ] - # interval: 5s - # timeout: 5s - # retries: 10 - # environment: - # MONGO_INITDB_DATABASE: testdb - # networks: - # - portabase - - # sqlite: - # container_name: db-sqlite - # image: keinos/sqlite3 - # volumes: - # - sqlite-data:/workspace/data - # working_dir: /workspace - # command: tail -f /dev/null - # stdin_open: true - # tty: true - -# db-redis: -# image: redis:latest -# container_name: db-redis -# ports: -# - "6379:6379" -# volumes: -# - redis-data:/data -# command: [ "redis-server", "--appendonly", "yes" ] -# networks: -# - portabase -# -# db-redis-auth: -# image: redis:latest -# container_name: db-redis-auth -# ports: -# - "6380:6379" -# volumes: -# - redis-data-auth:/data -# environment: -# - REDIS_PASSWORD=supersecurepassword -# command: [ "redis-server", "--requirepass", "supersecurepassword", "--appendonly", "yes" ] -# networks: -# - portabase -# -# db-valkey: -# image: valkey/valkey -# container_name: db-valkey -# environment: -# - ALLOW_EMPTY_PASSWORD=yes -# ports: -# - '6381:6379' -# volumes: -# - valkey-data:/data -# networks: -# - portabase -# -# db-valkey-auth: -# image: valkey/valkey -# container_name: db-valkey-auth -# command: > -# --requirepass "supersecurepassword" -# ports: -# - '6382:6379' -# volumes: -# - valkey-data-auth:/data -# networks: -# - portabase - - db-firebird: - image: firebirdsql/firebird - container_name: db-firebird - restart: always - environment: - - FIREBIRD_ROOT_PASSWORD=fake_root_password - - FIREBIRD_USER=alice - - FIREBIRD_PASSWORD=fake_password - - FIREBIRD_DATABASE=mirror.fdb - - FIREBIRD_DATABASE_DEFAULT_CHARSET=UTF8 - volumes: - - firebird-data:/var/lib/firebird/data - ports: - - "3060:3050" - - networks: - - portabase - volumes: cargo-registry: cargo-git: -# cargo-target: -# postgres-data: - mariadb-data: - mysql-data: -# mongodb-data: -# mongodb-data-auth: -# sqlite-data: -# redis-data: -# redis-data-auth: -# valkey-data: -# valkey-data-auth: - firebird-data: + #cargo-target: + networks: portabase: diff --git a/docker/entrypoints/app-dev-entrypoint.sh b/docker/entrypoints/app-dev-entrypoint.sh index be4a8f8..d7d9a2d 100755 --- a/docker/entrypoints/app-dev-entrypoint.sh +++ b/docker/entrypoints/app-dev-entrypoint.sh @@ -31,9 +31,10 @@ check_network() { fi } -check_docker +#check_docker check_network echo "Starting docker-compose..." +docker compose -f ./docker-compose.databases.yml up -d docker compose -f ./docker-compose.yml up echo "Docker-compose started successfully." \ No newline at end of file diff --git a/justfile b/justfile new file mode 100644 index 0000000..135ef1d --- /dev/null +++ b/justfile @@ -0,0 +1,54 @@ +set dotenv-load := true +set shell := ["bash", "-cu"] + +CLUSTER_SCRIPT := "docker/entrypoints/app-dev-entrypoint.sh" + +up: + bash {{CLUSTER_SCRIPT}} + +seed-mongo: + echo "Seeding MongoDB..." + bash ./scripts/mongo/seed-mongo.sh + +seed-mongo-auth: + echo "Seeding MongoDB with auth..." + bash ./scripts/mongo/seed-mongo.sh auth + +seed-mysql: + echo "Seeding MySQL..." + mysql -h 127.0.0.1 -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DB" < ./scripts/mysql/seed-mysql.sql + +seed-mysql-1gb: + echo "Seeding MySQL (1GB)..." + mysql -h 127.0.0.1 -P "$MYSQL_PORT" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DB" < ./scripts/mysql/seed-1gb.sql + +seed-postgres: + echo "Seeding Postgres..." + docker exec -i -e PGPASSWORD="$PG_PASSWORD" "$PG_CONTAINER" \ + psql -U "$PG_USER" -d "$PG_DB" < ./scripts/postgres/seed.sql + +seed-postgres-1gb: + echo "Seeding Postgres (1GB)..." + docker exec -i -e PGPASSWORD="$PG_PASSWORD" "$PG_CONTAINER" \ + psql -U "$PG_USER" -d "$PG_DB" < ./scripts/postgres/seed-1gb.sql + +seed-sqlite: + echo "Seeding SQLite..." + bash ./scripts/sqlite/seed.sh + echo "Done" + +seed-firebird: + echo "Seeding Firebird..." + docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb < ./scripts/firebird/seed.sql + + echo "Verifying Firebird tables..." + docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb -sql "SHOW TABLES;" + +seed-all: + just seed-mongo + just seed-mysql + just seed-postgres + just seed-postgres-1gb + just seed-sqlite + just seed-mongo + just seed-firebird \ No newline at end of file diff --git a/scripts/sqlite/seed.sh b/scripts/sqlite/seed.sh new file mode 100644 index 0000000..d0b6f5d --- /dev/null +++ b/scripts/sqlite/seed.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +SEED_VALUE="${SEED:-}" + +if [ "$SEED_VALUE" = "big" ]; then + SQLITE_SEED_FILE="./scripts/sqlite/seed-big.sql" +else + SQLITE_SEED_FILE="./scripts/sqlite/seed.sql" +fi + +docker exec -u 0 db-sqlite sh -c "chmod -R 777 /workspace/data" +docker exec -u 0 db-sqlite sh -c "touch /workspace/data/app.db" +docker exec -i db-sqlite sh -c "sqlite3 /workspace/data/app.db" < "$SQLITE_SEED_FILE" \ No newline at end of file diff --git a/src/domain/firebird/ping.rs b/src/domain/firebird/ping.rs index 2082a80..ebfdbb1 100644 --- a/src/domain/firebird/ping.rs +++ b/src/domain/firebird/ping.rs @@ -8,6 +8,8 @@ use tokio::time::{Duration, timeout}; pub async fn run(cfg: DatabaseConfig) -> anyhow::Result { let db_path = format!("{}/{}:{}", cfg.host, cfg.port, cfg.database); + info!("Running Ping database from {}", db_path); + let mut child = Command::new("isql-fb") .arg("-q") .arg("-user") diff --git a/src/domain/redis/ping.rs b/src/domain/redis/ping.rs index c53b822..4c78204 100644 --- a/src/domain/redis/ping.rs +++ b/src/domain/redis/ping.rs @@ -30,10 +30,7 @@ pub async fn run(cfg: DatabaseConfig) -> Result { let output = output.context("Failed to execute redis-cli")?; let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); - - if !stdout.is_empty() { - error!("Redis stderr: {}", stderr); - } + if stderr.contains("NOAUTH") { error!("Redis authentication failed (NOAUTH required)"); diff --git a/src/domain/valkey/ping.rs b/src/domain/valkey/ping.rs index b3864da..55ce931 100644 --- a/src/domain/valkey/ping.rs +++ b/src/domain/valkey/ping.rs @@ -30,11 +30,7 @@ pub async fn run(cfg: DatabaseConfig) -> Result { let output = output.context("Failed to execute valkey-cli")?; let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); - - if !stdout.is_empty() { - error!("Valkey stderr: {}", stderr); - } - + if stderr.contains("NOAUTH") { error!("Valkey authentication failed (NOAUTH required)"); return Ok(false); diff --git a/src/services/status.rs b/src/services/status.rs index 3bb2bb3..3f8a018 100644 --- a/src/services/status.rs +++ b/src/services/status.rs @@ -9,6 +9,7 @@ use reqwest::Client; use std::error::Error; use std::sync::Arc; use futures_util::future::try_join_all; +use tracing::info; use crate::domain::factory::DatabaseFactory; pub struct StatusService { @@ -33,6 +34,8 @@ impl StatusService { let reachable = db_engine.ping().await?; + info!("Ping {} => {:?}",db.name, reachable); + Ok::(DatabasePayload { name: &db.name, dbms: &db.db_type.as_str(), diff --git a/src/tests/domain/firebird.rs b/src/tests/domain/firebird.rs new file mode 100644 index 0000000..b052c75 --- /dev/null +++ b/src/tests/domain/firebird.rs @@ -0,0 +1,107 @@ +use crate::domain::factory::DatabaseFactory; +use crate::services::config::{DatabaseConfig, DbType}; +use crate::tests::init_tracing_for_test; +use crate::utils::compress::{compress_to_tar_gz_large, decompress_large_tar_gz}; +use std::path::PathBuf; +use std::time::Duration; +use tempfile::TempDir; +use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, GenericImage, ImageExt}; +use testcontainers::core::{IntoContainerPort}; +use tracing::{error, info}; + + +async fn create_config() -> (ContainerAsync, DatabaseConfig) { + + let container = GenericImage::new("jacobalberty/firebird", "latest") + .with_exposed_port(3050.tcp()) + .with_env_var("FIREBIRD_ROOT_PASSWORD", "fake_root_password") + .with_env_var("FIREBIRD_USER", "alice") + .with_env_var("FIREBIRD_PASSWORD", "fake_password") + .with_env_var("FIREBIRD_DATABASE", "mirror.fdb") + .with_env_var("FIREBIRD_DATABASE_DEFAULT_CHARSET", "UTF8") + .start() + .await + .expect("Firebird started"); + + tokio::time::sleep(Duration::from_secs(10)).await; + + let host = container.get_host().await.unwrap().to_string(); + let port = container.get_host_port_ipv4(3050).await.unwrap(); + + let database = "/firebird/data/mirror.fdb"; + + let config = DatabaseConfig { + name: "Test Firebird".to_string(), + database: database.to_string(), + db_type: DbType::Firebird, + username: "alice".to_string(), + password: "fake_password".to_string(), + port, + host, + generated_id: "3c445eb4-c2c6-4bde-a423-ee1385dcf6d2".to_string(), + path: "".to_string(), + }; + + (container, config) +} + +#[tokio::test] +async fn firebird_ping_test() { + init_tracing_for_test(); + + let (_container, config) = create_config().await; + + let db = DatabaseFactory::create_for_backup(config.clone()).await; + let reachable = db.ping().await.unwrap_or(false); + + assert!(reachable); +} + +#[tokio::test] +async fn firebird_backup_restore_test() { + init_tracing_for_test(); + + let (_container, config) = create_config().await; + + let temp_dir = TempDir::new().unwrap(); + let backup_path = temp_dir.path(); + + let db = DatabaseFactory::create_for_backup(config.clone()).await; + + let file_path = db.backup(backup_path).await.unwrap(); + assert!(file_path.is_file()); + + let compression = compress_to_tar_gz_large(&file_path).await.unwrap(); + assert!(compression.compressed_path.is_file()); + + let files = decompress_large_tar_gz( + compression.compressed_path.as_path(), + temp_dir.path(), + ) + .await + .unwrap(); + + let backup_file: PathBuf = if files.len() == 1 { + files[0].clone() + } else { + panic!("Unexpected number of files after decompression"); + }; + + let db = DatabaseFactory::create_for_restore(config.clone(), &backup_file).await; + + let reachable = db.ping().await.unwrap_or(false); + info!("Reachable: {}", reachable); + assert!(reachable); + + match db.restore(&backup_file).await { + Ok(_) => { + info!("Restore succeeded for {}", config.generated_id); + assert!(true) + } + Err(e) => { + error!("Restore failed for {}: {:?}", config.generated_id, e); + assert!(false) + } + } +} \ No newline at end of file diff --git a/src/tests/domain/mod.rs b/src/tests/domain/mod.rs index bfcb388..868ff33 100644 --- a/src/tests/domain/mod.rs +++ b/src/tests/domain/mod.rs @@ -4,3 +4,4 @@ mod mysql; mod postgres; mod redis; mod valkey; +mod firebird; From 05f795c53f7b05414463ddf34822cfa57070f2f3 Mon Sep 17 00:00:00 2001 From: Charles GTE Date: Tue, 31 Mar 2026 14:53:28 +0200 Subject: [PATCH 3/3] fix: tests and refactoring, add justfile instead of makefile --- databases.json | 20 +++++----- databases.toml | 70 ++++++++++++++++++++++++++++++++-- docker-compose.yml | 9 +++-- src/domain/firebird/restore.rs | 2 +- src/domain/sqlite/backup.rs | 3 +- src/tests/domain/firebird.rs | 17 ++++++--- 6 files changed, 98 insertions(+), 23 deletions(-) diff --git a/databases.json b/databases.json index 71c878e..83b750d 100644 --- a/databases.json +++ b/databases.json @@ -21,7 +21,7 @@ "generated_id": "16678124-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 4 - MongoDB Auth", + "name": "Test database 3 - MongoDB Auth", "database": "testdbauth", "type": "mongodb", "username": "root", @@ -31,7 +31,7 @@ "generated_id": "16678145-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 5 - MongoDB", + "name": "Test database 4 - MongoDB", "database": "testdb", "type": "mongodb", "port": 27017, @@ -39,26 +39,26 @@ "generated_id": "16678147-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 6 - SQLite DB", + "name": "Test database 5 - SQLite DB", "type": "sqlite", "path": "/sqlite-data/workspace/data/app.db", "generated_id": "16678178-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 7 - SQLite DB", + "name": "Test database 6 - SQLite DB", "type": "sqlite", "path": "/sqlite-data-2/workspace/data/app.db", "generated_id": "16678179-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 8 - Redis", + "name": "Test database 7 - Redis", "type": "redis", "port": 6379, "host": "db-redis", "generated_id": "16678166-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 9 - Redis Auth", + "name": "Test database 8 - Redis Auth", "type": "redis", "password": "supersecurepassword", "port": 6379, @@ -67,14 +67,14 @@ "generated_id": "16678160-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 10 - Valkey", + "name": "Test database 9 - Valkey", "type": "valkey", "port": 6379, "host": "db-valkey", "generated_id": "16678560-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 11 - Valkey Auth", + "name": "Test database 10 - Valkey Auth", "type": "valkey", "password": "supersecurepassword", "port": 6379, @@ -83,7 +83,7 @@ "generated_id": "16678561-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 12 - Mysql", + "name": "Test database 11 - Mysql", "database": "mysqldb", "type": "mysql", "username": "mysqldb", @@ -93,7 +93,7 @@ "generated_id": "16656124-ff7e-4c97-8c83-0adeff214681" }, { - "name": "Test database 13 - Firebird", + "name": "Test database 12 - Firebird", "database": "/var/lib/firebird/data/mirror.fdb", "type": "firebird", "username": "alice", diff --git a/databases.toml b/databases.toml index 635b343..a28cff9 100644 --- a/databases.toml +++ b/databases.toml @@ -29,11 +29,75 @@ host = "db-mongodb-auth" generated_id = "16678145-ff7e-4c97-8c83-0adeff214681" [[databases]] -name = "Test database 3 - MongoDB" +name = "Test database 4 - MongoDB" database = "testdb" type = "mongodb" -username = "" -password = "" port = 27017 host = "db-mongodb" generated_id = "16678147-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 5 - SQLite DB" +type = "sqlite" +path = "/sqlite-data/workspace/data/app.db" +generated_id = "16678178-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 6 - SQLite DB 2" +type = "sqlite" +path = "/sqlite-data-2/workspace/data/app.db" +generated_id = "16678179-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 7 - Redis" +type = "redis" +port = 6379 +host = "db-redis" +generated_id = "16678166-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 8 - Redis Auth" +type = "redis" +password = "supersecurepassword" +username = "default" +port = 6379 +host = "db-redis-auth" +generated_id = "16678160-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 9 - Valkey" +type = "valkey" +port = 6379 +host = "db-valkey" +generated_id = "16678560-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 10 - Valkey Auth" +type = "valkey" +password = "supersecurepassword" +username = "default" +port = 6379 +host = "db-valkey-auth" +generated_id = "16678561-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 11 - Mysql" +database = "mysqldb" +type = "mysql" +password = "changeme" +username = "mysqldb" +port = 3306 +host = "db-mysql" +generated_id = "16656124-ff7e-4c97-8c83-0adeff214681" + +[[databases]] +name = "Test database 12 - Firebird" +database = "/var/lib/firebird/data/mirror.fdb" +type = "firebird" +password = "fake_password" +username = "alice" +port = 3050 +host = "db-firebird" +generated_id = "16706124-ff7e-4c97-8c83-0adeff214681" + + diff --git a/docker-compose.yml b/docker-compose.yml index e90f0af..a83ef58 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,15 +13,15 @@ services: #- ./databases.toml:/config/config.toml #- /var/run/docker.sock:/var/run/docker.sock #- cargo-target:/app/target - #- sqlite-data:/sqlite-data/workspace/data + #- agent-rust_sqlite-data:/sqlite-data/workspace/data #- ./scripts/sqlite/test-db:/sqlite-data-2/workspace/data environment: APP_ENV: development LOG: debug TZ: "Europe/Paris" - EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiZWQzNDNmOTUtNTU0Yi00NTAwLTkzZjAtMzEyODQzMjQ2M2RhIiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ==" + EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiOWRjZjYzNzQtZTc5ZC00MzU5LTkwMTYtNjA0OTQ2NDNhNjEwIiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ==" #POOLING: 1 - #DATABASES_CONFIG_FILE: "config.toml" + DATABASES_CONFIG_FILE: "config.toml" extra_hosts: - "localhost:host-gateway" networks: @@ -31,6 +31,9 @@ volumes: cargo-registry: cargo-git: #cargo-target: +# agent-rust_sqlite-data: +# external: true + networks: diff --git a/src/domain/firebird/restore.rs b/src/domain/firebird/restore.rs index b573014..d6ac1e7 100644 --- a/src/domain/firebird/restore.rs +++ b/src/domain/firebird/restore.rs @@ -6,7 +6,7 @@ use tracing::{debug, error, info}; pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { tokio::task::spawn_blocking(move || -> Result<()> { - debug!("Starting MongoDB restore for database {}", cfg.name); + debug!("Starting Firebird restore for database {}", cfg.name); let db_path = format!("{}/{}:{}", cfg.host, cfg.port, cfg.database); diff --git a/src/domain/sqlite/backup.rs b/src/domain/sqlite/backup.rs index 7b4b21c..2ab9cd1 100644 --- a/src/domain/sqlite/backup.rs +++ b/src/domain/sqlite/backup.rs @@ -19,6 +19,7 @@ pub async fn run( }; let db_path = PathBuf::from(db_path_str); + info!("database path: {}", db_path.display()); if !db_path.exists() { anyhow::bail!("SQLite database file not found: {}", db_path.display()); @@ -31,7 +32,7 @@ pub async fn run( .arg(format!(".backup '{}'", file_path.display())) .output() .context("SQLite backup command failed to start")?; - + info!("Backup successful: {:?}", output); if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); error!("SQLite backup failed for {}: {}", cfg.name, stderr); diff --git a/src/tests/domain/firebird.rs b/src/tests/domain/firebird.rs index b052c75..a465081 100644 --- a/src/tests/domain/firebird.rs +++ b/src/tests/domain/firebird.rs @@ -7,14 +7,18 @@ use std::time::Duration; use tempfile::TempDir; use testcontainers::runners::AsyncRunner; use testcontainers::{ContainerAsync, GenericImage, ImageExt}; -use testcontainers::core::{IntoContainerPort}; +use testcontainers::core::{AccessMode, IntoContainerPort}; use tracing::{error, info}; - +use testcontainers::core::{Mount}; async fn create_config() -> (ContainerAsync, DatabaseConfig) { - let container = GenericImage::new("jacobalberty/firebird", "latest") + let mount = Mount::volume_mount("firebird-test-data", "/var/lib/firebird/data") + .with_access_mode(AccessMode::ReadWrite); + + let container = GenericImage::new("firebirdsql/firebird", "latest") .with_exposed_port(3050.tcp()) + .with_mount(mount) .with_env_var("FIREBIRD_ROOT_PASSWORD", "fake_root_password") .with_env_var("FIREBIRD_USER", "alice") .with_env_var("FIREBIRD_PASSWORD", "fake_password") @@ -24,12 +28,12 @@ async fn create_config() -> (ContainerAsync, DatabaseConfig) { .await .expect("Firebird started"); - tokio::time::sleep(Duration::from_secs(10)).await; + tokio::time::sleep(Duration::from_secs(15)).await; let host = container.get_host().await.unwrap().to_string(); let port = container.get_host_port_ipv4(3050).await.unwrap(); - let database = "/firebird/data/mirror.fdb"; + let database = "/var/lib/firebird/data/mirror.fdb"; let config = DatabaseConfig { name: "Test Firebird".to_string(), @@ -70,6 +74,9 @@ async fn firebird_backup_restore_test() { let db = DatabaseFactory::create_for_backup(config.clone()).await; let file_path = db.backup(backup_path).await.unwrap(); + + tokio::time::sleep(Duration::from_secs(10)).await; + assert!(file_path.is_file()); let compression = compress_to_tar_gz_large(&file_path).await.unwrap();