//! Test database utilities. //! //! AI AGENT NOTE: These helpers manage the test database connection pool. //! Uses rusty_petroleum_test database for all tests. use sqlx::mysql::{MySqlPool, MySqlPoolOptions}; use std::time::Duration; /// Creates a connection pool to the test database. /// /// AI AGENT NOTE: Uses config.toml or config.test.toml for connection details. /// The test database should be separate from dev/prod to avoid data conflicts. pub async fn create_test_pool() -> MySqlPool { let config = crate::config::Config::load(crate::config::Env::Test) .expect("Failed to load test config"); MySqlPoolOptions::new() .max_connections(1) .acquire_timeout(Duration::from_secs(10)) .connect(&config.database.connection_url()) .await .expect("Failed to connect to test database") } /// Resets the test database by dropping and recreating all tables. /// /// AI AGENT NOTE: This is used before running tests to ensure a clean state. /// It uses the `rusty_petroleum_test` database. pub async fn reset_test_database() -> anyhow::Result<()> { let config = crate::config::Config::load(crate::config::Env::Test)?; let database_url = config.database.connection_url(); let base_url = database_url.trim_end_matches(config.env.database_name()); let setup_pool = MySqlPoolOptions::new() .max_connections(1) .connect(base_url) .await?; // Drop database if exists sqlx::query(&format!("DROP DATABASE IF EXISTS {}", config.env.database_name())) .execute(&setup_pool) .await?; // Create fresh database sqlx::query(&format!("CREATE DATABASE {}", config.env.database_name())) .execute(&setup_pool) .await?; drop(setup_pool); // Now create tables let pool = create_test_pool().await; // Create customers table sqlx::query( r#" CREATE TABLE customers ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, customer_number VARCHAR(50) NOT NULL UNIQUE, card_report_group TINYINT UNSIGNED NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_customer_number (customer_number) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci "#, ) .execute(&pool) .await?; // Create cards table sqlx::query( r#" CREATE TABLE cards ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, card_number VARCHAR(50) NOT NULL UNIQUE, customer_id INT UNSIGNED NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_card_number (card_number), INDEX idx_customer_id (customer_id), FOREIGN KEY (customer_id) REFERENCES customers(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci "#, ) .execute(&pool) .await?; // Create transactions table sqlx::query( r#" CREATE TABLE transactions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, transaction_date DATETIME NOT NULL, batch_number VARCHAR(20) NOT NULL, amount DECIMAL(10,2) NOT NULL, volume DECIMAL(10,3) NOT NULL, price DECIMAL(8,4) NOT NULL, quality_code INT NOT NULL, quality_name VARCHAR(50) NOT NULL, card_number VARCHAR(50) NOT NULL, station VARCHAR(20) NOT NULL, terminal VARCHAR(10) NOT NULL, pump VARCHAR(10) NOT NULL, receipt VARCHAR(20) NOT NULL, control_number VARCHAR(20), customer_id INT UNSIGNED NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_transaction_date (transaction_date), INDEX idx_batch_number (batch_number), INDEX idx_customer_id (customer_id), INDEX idx_card_number (card_number), INDEX idx_station (station) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci "#, ) .execute(&pool) .await?; drop(pool); Ok(()) }