Add comprehensive TDD infrastructure with 45 tests

- Add lib crate exposing modules for integration testing
- Add dev-dependencies: tokio-test 0.4, tempfile
- Refactor parse_csv_fields() as pure function for unit testing
- Add field validation (minimum 16 fields required)
- Fix repository last_insert_id using SELECT LAST_INSERT_ID()
- Add 10 lib tests for CSV parsing and date formatting
- Add 10 config tests for environment configuration
- Add 7 import tests for CSV file parsing
- Add 6 models tests for database structs
- Add 12 repository tests for CRUD operations
This commit is contained in:
2026-04-02 11:13:41 +02:00
parent 429d5d774f
commit e2123e4619
11 changed files with 1648 additions and 87 deletions

122
tests/common/test_db.rs Normal file
View File

@@ -0,0 +1,122 @@
//! 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(())
}