sharenet/backend/crates/integration-tests/src/migration_tests.rs
continuist 61117b6fa6
Some checks are pending
CI/CD Pipeline / Test Backend (push) Waiting to run
CI/CD Pipeline / Test Frontend (push) Waiting to run
CI/CD Pipeline / Build and Push Docker Images (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
Created test_setup.rs file that consolidates test setup code for interface tests, including db migrations
2025-06-28 01:57:12 -04:00

220 lines
No EOL
7.6 KiB
Rust

/*
* This file is part of Sharenet.
*
* Sharenet is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
*
* You may obtain a copy of the license at:
* https://creativecommons.org/licenses/by-nc-sa/4.0/
*
* Copyright (c) 2024 Continuist <continuist02@gmail.com>
*/
use sqlx::PgPool;
use sqlx::Row;
use serial_test::serial;
/// Setup test database connection for migration tests
async fn setup_migration_test_db() -> PgPool {
// Use the centralized test setup that ensures migrations are run only once
crate::test_setup::setup_test_db().await
}
#[tokio::test]
#[serial]
async fn test_migrations_can_be_applied() {
let pool = setup_migration_test_db().await;
// Run migrations
sqlx::migrate!("../../migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
// Verify tables exist
let users_table_exists = sqlx::query(
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'users')"
)
.fetch_one(&pool)
.await
.expect("Failed to check users table")
.get::<bool, _>(0);
let products_table_exists = sqlx::query(
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'products')"
)
.fetch_one(&pool)
.await
.expect("Failed to check products table")
.get::<bool, _>(0);
let migrations_table_exists = sqlx::query(
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '_sqlx_migrations')"
)
.fetch_one(&pool)
.await
.expect("Failed to check migrations table")
.get::<bool, _>(0);
assert!(users_table_exists, "Users table should exist after migrations");
assert!(products_table_exists, "Products table should exist after migrations");
assert!(migrations_table_exists, "Migrations table should exist after migrations");
// Verify table structure
let users_columns = sqlx::query(
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'users' ORDER BY ordinal_position"
)
.fetch_all(&pool)
.await
.expect("Failed to get users table columns");
let products_columns = sqlx::query(
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'products' ORDER BY ordinal_position"
)
.fetch_all(&pool)
.await
.expect("Failed to get products table columns");
// Check that expected columns exist
let user_column_names: Vec<String> = users_columns.iter()
.map(|row| row.get::<String, _>(0))
.collect();
let product_column_names: Vec<String> = products_columns.iter()
.map(|row| row.get::<String, _>(0))
.collect();
assert!(user_column_names.contains(&"id".to_string()), "Users table should have id column");
assert!(user_column_names.contains(&"username".to_string()), "Users table should have username column");
assert!(user_column_names.contains(&"email".to_string()), "Users table should have email column");
assert!(user_column_names.contains(&"created_at".to_string()), "Users table should have created_at column");
assert!(user_column_names.contains(&"updated_at".to_string()), "Users table should have updated_at column");
assert!(product_column_names.contains(&"id".to_string()), "Products table should have id column");
assert!(product_column_names.contains(&"name".to_string()), "Products table should have name column");
assert!(product_column_names.contains(&"description".to_string()), "Products table should have description column");
assert!(product_column_names.contains(&"created_at".to_string()), "Products table should have created_at column");
assert!(product_column_names.contains(&"updated_at".to_string()), "Products table should have updated_at column");
}
#[tokio::test]
#[serial]
async fn test_migrations_are_idempotent() {
let pool = setup_migration_test_db().await;
// Run migrations first time
sqlx::migrate!("../../migrations")
.run(&pool)
.await
.expect("Failed to run migrations first time");
// Run migrations second time (should be idempotent)
sqlx::migrate!("../../migrations")
.run(&pool)
.await
.expect("Failed to run migrations second time");
// Verify tables still exist and are functional
let users_count = sqlx::query("SELECT COUNT(*) FROM users")
.fetch_one(&pool)
.await
.expect("Failed to count users")
.get::<i64, _>(0);
let products_count = sqlx::query("SELECT COUNT(*) FROM products")
.fetch_one(&pool)
.await
.expect("Failed to count products")
.get::<i64, _>(0);
assert_eq!(users_count, 0, "Users table should be empty after migrations");
assert_eq!(products_count, 0, "Products table should be empty after migrations");
}
#[tokio::test]
#[serial]
async fn test_migration_constraints() {
let pool = setup_migration_test_db().await;
// Run migrations
sqlx::migrate!("../../migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
// Test unique constraint on username
sqlx::query("INSERT INTO users (id, username, email, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)")
.bind(uuid::Uuid::new_v4())
.bind("testuser")
.bind("test@example.com")
.bind(chrono::Utc::now())
.bind(chrono::Utc::now())
.execute(&pool)
.await
.expect("Failed to insert first user");
// Try to insert another user with the same username (should fail)
let result = sqlx::query("INSERT INTO users (id, username, email, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)")
.bind(uuid::Uuid::new_v4())
.bind("testuser")
.bind("test2@example.com")
.bind(chrono::Utc::now())
.bind(chrono::Utc::now())
.execute(&pool)
.await;
assert!(result.is_err(), "Should not be able to insert user with duplicate username");
}
#[tokio::test]
#[serial]
async fn test_migration_data_types() {
let pool = setup_migration_test_db().await;
// Run migrations
sqlx::migrate!("../../migrations")
.run(&pool)
.await
.expect("Failed to run migrations");
// Test that UUID columns accept valid UUIDs
let user_id = uuid::Uuid::new_v4();
let product_id = uuid::Uuid::new_v4();
sqlx::query("INSERT INTO users (id, username, email, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)")
.bind(user_id)
.bind("testuser")
.bind("test@example.com")
.bind(chrono::Utc::now())
.bind(chrono::Utc::now())
.execute(&pool)
.await
.expect("Failed to insert user with UUID");
sqlx::query("INSERT INTO products (id, name, description, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)")
.bind(product_id)
.bind("Test Product")
.bind("Test Description")
.bind(chrono::Utc::now())
.bind(chrono::Utc::now())
.execute(&pool)
.await
.expect("Failed to insert product with UUID");
// Verify the data was inserted correctly
let user = sqlx::query("SELECT id FROM users WHERE id = $1")
.bind(user_id)
.fetch_one(&pool)
.await
.expect("Failed to fetch user");
let product = sqlx::query("SELECT id FROM products WHERE id = $1")
.bind(product_id)
.fetch_one(&pool)
.await
.expect("Failed to fetch product");
assert_eq!(user.get::<uuid::Uuid, _>(0), user_id, "User ID should match");
assert_eq!(product.get::<uuid::Uuid, _>(0), product_id, "Product ID should match");
}