#[cfg(test)]
mod tests {
use super::super::migrations::{
MigrationManager, Migration, MigrationRecord, MigrationStatus
};
use chrono::{DateTime, Utc};
struct MockMigration {
name: String,
up_sql: String,
down_sql: String,
}
impl Migration for MockMigration {
fn name(&self) -> &str {
&self.name
}
fn up(&self) -> &str {
&self.up_sql
}
fn down(&self) -> &str {
&self.down_sql
}
}
#[test]
fn test_migration_trait_implementation() {
let migration = MockMigration {
name: "create_users_table".to_string(),
up_sql: "CREATE TABLE users (id UUID PRIMARY KEY, name TEXT);".to_string(),
down_sql: "DROP TABLE users;".to_string(),
};
assert_eq!(migration.name(), "create_users_table");
assert_eq!(migration.up(), "CREATE TABLE users (id UUID PRIMARY KEY, name TEXT);");
assert_eq!(migration.down(), "DROP TABLE users;");
}
#[test]
fn test_migration_record_structure() {
let now = Utc::now();
let record = MigrationRecord {
id: 1,
name: "create_users_table".to_string(),
applied_at: now,
};
assert_eq!(record.id, 1);
assert_eq!(record.name, "create_users_table");
assert_eq!(record.applied_at, now);
}
#[test]
fn test_migration_status_structure() {
let status = MigrationStatus {
total_migrations: 5,
applied_migrations: vec![
"create_users_table".to_string(),
"create_posts_table".to_string(),
],
pending_migrations: vec![
"create_comments_table".to_string(),
"add_user_indexes".to_string(),
"add_post_indexes".to_string(),
],
last_migration: Some("create_posts_table".to_string()),
};
assert_eq!(status.total_migrations, 5);
assert_eq!(status.applied_migrations.len(), 2);
assert_eq!(status.pending_migrations.len(), 3);
assert_eq!(status.last_migration, Some("create_posts_table".to_string()));
}
#[test]
fn test_migration_status_empty() {
let status = MigrationStatus {
total_migrations: 0,
applied_migrations: vec![],
pending_migrations: vec![],
last_migration: None,
};
assert_eq!(status.total_migrations, 0);
assert!(status.applied_migrations.is_empty());
assert!(status.pending_migrations.is_empty());
assert!(status.last_migration.is_none());
}
#[test]
fn test_migration_status_all_applied() {
let status = MigrationStatus {
total_migrations: 2,
applied_migrations: vec![
"create_users_table".to_string(),
"create_posts_table".to_string(),
],
pending_migrations: vec![],
last_migration: Some("create_posts_table".to_string()),
};
assert_eq!(status.total_migrations, 2);
assert_eq!(status.applied_migrations.len(), 2);
assert!(status.pending_migrations.is_empty());
assert!(status.last_migration.is_some());
}
#[test]
fn test_migration_status_all_pending() {
let status = MigrationStatus {
total_migrations: 3,
applied_migrations: vec![],
pending_migrations: vec![
"create_users_table".to_string(),
"create_posts_table".to_string(),
"create_comments_table".to_string(),
],
last_migration: None,
};
assert_eq!(status.total_migrations, 3);
assert!(status.applied_migrations.is_empty());
assert_eq!(status.pending_migrations.len(), 3);
assert!(status.last_migration.is_none());
}
#[test]
fn test_migration_manager_new() {
fn check_migration_manager_constructor() {
let _: Option<MigrationManager> = None;
}
check_migration_manager_constructor();
}
#[test]
fn test_migration_names() {
let migrations = vec![
MockMigration {
name: "001_create_users_table".to_string(),
up_sql: "CREATE TABLE users (id UUID PRIMARY KEY);".to_string(),
down_sql: "DROP TABLE users;".to_string(),
},
MockMigration {
name: "002_create_posts_table".to_string(),
up_sql: "CREATE TABLE posts (id UUID PRIMARY KEY, user_id UUID);".to_string(),
down_sql: "DROP TABLE posts;".to_string(),
},
MockMigration {
name: "003_add_foreign_key".to_string(),
up_sql: "ALTER TABLE posts ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id);".to_string(),
down_sql: "ALTER TABLE posts DROP CONSTRAINT fk_user;".to_string(),
},
];
assert_eq!(migrations[0].name(), "001_create_users_table");
assert_eq!(migrations[1].name(), "002_create_posts_table");
assert_eq!(migrations[2].name(), "003_add_foreign_key");
assert!(migrations[0].up().contains("CREATE TABLE users"));
assert!(migrations[0].down().contains("DROP TABLE users"));
assert!(migrations[1].up().contains("CREATE TABLE posts"));
assert!(migrations[2].up().contains("FOREIGN KEY"));
assert!(migrations[2].down().contains("DROP CONSTRAINT"));
}
#[test]
fn test_migration_sql_content() {
let migration = MockMigration {
name: "create_users_table_with_timestamps".to_string(),
up_sql: r#"
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
"#.to_string(),
down_sql: r#"
DROP INDEX IF EXISTS idx_users_email;
DROP TABLE IF EXISTS users;
"#.to_string(),
};
assert!(migration.up().contains("CREATE TABLE users"));
assert!(migration.up().contains("gen_random_uuid()"));
assert!(migration.up().contains("TIMESTAMP WITH TIME ZONE"));
assert!(migration.up().contains("CREATE INDEX"));
assert!(migration.up().contains("UNIQUE NOT NULL"));
assert!(migration.down().contains("DROP INDEX"));
assert!(migration.down().contains("DROP TABLE"));
assert!(migration.down().contains("IF EXISTS"));
}
#[test]
fn test_timestamp_formatting() {
let timestamp = "2023-12-25T10:30:45Z".parse::<DateTime<Utc>>().unwrap();
let record = MigrationRecord {
id: 42,
name: "test_migration".to_string(),
applied_at: timestamp,
};
let formatted = record.applied_at.format("%Y-%m-%d %H:%M:%S").to_string();
assert_eq!(formatted, "2023-12-25 10:30:45");
}
#[test]
fn test_migration_status_calculations() {
let test_cases = vec![
(vec![], vec!["a", "b", "c"], 3, 0, 3),
(vec!["a"], vec!["b", "c"], 3, 1, 2),
(vec!["a", "b", "c"], vec![], 3, 3, 0),
(vec!["a", "b"], vec!["c"], 3, 2, 1),
];
for (applied_names, pending_names, total, applied_count, pending_count) in test_cases {
let status = MigrationStatus {
total_migrations: total,
applied_migrations: applied_names.iter().map(|s| s.to_string()).collect(),
pending_migrations: pending_names.iter().map(|s| s.to_string()).collect(),
last_migration: applied_names.last().map(|s| s.to_string()),
};
assert_eq!(status.total_migrations, total);
assert_eq!(status.applied_migrations.len(), applied_count);
assert_eq!(status.pending_migrations.len(), pending_count);
if applied_count > 0 {
assert_eq!(status.last_migration, applied_names.last().map(|s| s.to_string()));
} else {
assert!(status.last_migration.is_none());
}
}
}
#[test]
fn test_migration_types_integration() {
fn verify_migration_trait<T: Migration>(migration: &T) {
let _name = migration.name();
let _up = migration.up();
let _down = migration.down();
}
fn verify_migration_record(record: MigrationRecord) -> MigrationRecord {
record
}
fn verify_migration_status(status: MigrationStatus) -> MigrationStatus {
status
}
let migration = MockMigration {
name: "test".to_string(),
up_sql: "SELECT 1;".to_string(),
down_sql: "SELECT 2;".to_string(),
};
verify_migration_trait(&migration);
let record = MigrationRecord {
id: 1,
name: "test".to_string(),
applied_at: Utc::now(),
};
let _returned_record = verify_migration_record(record);
let status = MigrationStatus {
total_migrations: 1,
applied_migrations: vec!["test".to_string()],
pending_migrations: vec![],
last_migration: Some("test".to_string()),
};
let _returned_status = verify_migration_status(status);
}
}