reinhardt_testkit/fixtures/migrations.rs
1//! Migration Registry Test Fixtures
2//! Migration test fixtures and helpers
3//!
4//! This module provides comprehensive fixtures for testing database migrations
5//! and schema operations in Reinhardt applications.
6//!
7//! ## Fixture Categories
8//!
9//! ### Unit Testing Fixtures
10//!
11//! - `` `migration_registry` `` - Isolated `LocalRegistry` for unit testing
12//! - `` `test_migration_source` `` - In-memory migration source
13//! - `` `in_memory_repository` `` - In-memory migration repository
14//!
15//! ### Integration Testing Fixtures (requires `testcontainers` feature)
16//!
17//! - `` `migration_executor` `` - DatabaseMigrationExecutor with PostgreSQL container
18//! - `` `postgres_table_creator` `` - PostgreSQL schema management helper
19//! - `` `admin_table_creator` `` - Admin panel integration helper (available in `reinhardt-test`)
20//!
21//! ## Usage Examples
22//!
23//! ### Unit Testing with LocalRegistry
24//!
25//! ```rust,no_run
26//! # use reinhardt_testkit::fixtures::*;
27//! # use reinhardt_db::migrations::Migration;
28//! # use rstest::*;
29//! // #[rstest]
30//! // fn test_migration_registration(migration_registry: LocalRegistry) {
31//! // let migration = Migration::new("0001_initial", "polls");
32//! //
33//! // migration_registry.register(migration).unwrap();
34//! // assert_eq!(migration_registry.all_migrations().len(), 1);
35//! // }
36//! ```
37//!
38//! ### Integration Testing with PostgresTableCreator
39//!
40//! ```rust,no_run
41//! # use reinhardt_testkit::fixtures::*;
42//! # use reinhardt_db::migrations::{Operation, ColumnDefinition, FieldType};
43//! # use rstest::*;
44//! // #[rstest]
45//! // #[tokio::test]
46//! // async fn test_with_schema(#[future] postgres_table_creator: PostgresTableCreator) {
47//! // let mut creator = postgres_table_creator.await;
48//! //
49//! // // Define schema using Operation enum
50//! // let schema = vec![
51//! // Operation::CreateTable {
52//! // name: "users".to_string(),
53//! // columns: vec![
54//! // ColumnDefinition::new("id", FieldType::Serial).primary_key(),
55//! // ColumnDefinition::new("name", FieldType::Text),
56//! // ],
57//! // constraints: vec![],
58//! // without_rowid: None,
59//! // interleave_in_parent: None,
60//! // partition: None,
61//! // },
62//! // ];
63//! //
64//! // // Apply schema
65//! // creator.apply(schema).await.unwrap();
66//! //
67//! // // Use the database
68//! // let pool = creator.pool();
69//! // // ... test code ...
70//! // }
71//! ```
72//!
73//! ## Migration Patterns
74//!
75//! The new `PostgresTableCreator` and `AdminTableCreator` fixtures promote type-safe
76//! schema management using `Operation` enum instead of raw SQL strings. This provides:
77//!
78//! - **Type safety**: Schema defined using Rust types
79//! - **Testability**: Easy to create isolated test databases
80//! - **Maintainability**: Schema changes are explicit and reviewable
81//! - **Consistency**: Same patterns across all tests
82
83use async_trait::async_trait;
84use reinhardt_db::migrations::registry::LocalRegistry;
85use reinhardt_db::migrations::{Migration, MigrationRepository, MigrationSource, Result};
86use rstest::*;
87use std::collections::HashMap;
88
89// TestContainers-related imports (conditional on feature)
90#[cfg(feature = "testcontainers")]
91use crate::fixtures::testcontainers::postgres_container;
92#[cfg(feature = "testcontainers")]
93use reinhardt_db::migrations::executor::DatabaseMigrationExecutor;
94#[cfg(feature = "testcontainers")]
95use reinhardt_db::migrations::{DatabaseConnection, MigrationError, Operation};
96#[cfg(feature = "testcontainers")]
97use std::sync::Arc;
98#[cfg(feature = "testcontainers")]
99use testcontainers::{ContainerAsync, GenericImage};
100
101/// Creates a new isolated migration registry for testing
102///
103/// Each test gets its own empty LocalRegistry instance, ensuring complete
104/// isolation between test cases. This avoids the "duplicate distributed_slice"
105/// errors that occur with linkme's global registry in test environments.
106///
107/// # Examples
108///
109/// ```rust,no_run
110/// # use reinhardt_testkit::fixtures::*;
111/// # use reinhardt_db::migrations::Migration;
112/// # use rstest::*;
113/// // #[rstest]
114/// // fn test_migration_operations(migration_registry: LocalRegistry) {
115/// // // Registry starts empty
116/// // assert!(migration_registry.all_migrations().is_empty());
117/// //
118/// // // Register a migration
119/// // migration_registry.register(Migration::new("0001_initial", "polls")).unwrap();
120/// //
121/// // // Verify registration
122/// // assert_eq!(migration_registry.all_migrations().len(), 1);
123/// // }
124/// ```
125#[fixture]
126pub fn migration_registry() -> LocalRegistry {
127 LocalRegistry::new()
128}
129
130/// In-memory migration source for testing
131///
132/// Provides a simple implementation of `MigrationSource` that stores migrations
133/// in memory. Useful for testing migration-related functionality without
134/// filesystem or database dependencies.
135///
136/// # Examples
137///
138/// ```rust,no_run
139/// # use reinhardt_testkit::fixtures::TestMigrationSource;
140/// # use reinhardt_db::migrations::{Migration, MigrationSource};
141/// # #[tokio::main]
142/// # async fn main() {
143/// // #[tokio::test]
144/// // async fn test_source() {
145/// let mut source = TestMigrationSource::new();
146/// source.add_migration(Migration::new("0001_initial", "polls"));
147///
148/// let migrations = source.all_migrations().await.unwrap();
149/// assert_eq!(migrations.len(), 1);
150/// // }
151/// # }
152/// ```
153pub struct TestMigrationSource {
154 migrations: Vec<Migration>,
155}
156
157impl TestMigrationSource {
158 /// Create a new empty TestMigrationSource
159 pub fn new() -> Self {
160 Self {
161 migrations: Vec::new(),
162 }
163 }
164
165 /// Create a TestMigrationSource with initial migrations
166 pub fn with_migrations(migrations: Vec<Migration>) -> Self {
167 Self { migrations }
168 }
169
170 /// Add a migration to the source
171 pub fn add_migration(&mut self, migration: Migration) {
172 self.migrations.push(migration);
173 }
174
175 /// Clear all migrations from the source
176 pub fn clear(&mut self) {
177 self.migrations.clear();
178 }
179
180 /// Get the number of migrations
181 pub fn len(&self) -> usize {
182 self.migrations.len()
183 }
184
185 /// Check if the source is empty
186 pub fn is_empty(&self) -> bool {
187 self.migrations.is_empty()
188 }
189}
190
191impl Default for TestMigrationSource {
192 fn default() -> Self {
193 Self::new()
194 }
195}
196
197#[async_trait]
198impl MigrationSource for TestMigrationSource {
199 async fn all_migrations(&self) -> Result<Vec<Migration>> {
200 Ok(self.migrations.clone())
201 }
202}
203
204/// In-memory migration repository for testing
205///
206/// Provides a simple implementation of `MigrationRepository` that stores migrations
207/// in memory using a HashMap. Useful for testing migration persistence without
208/// actual file I/O.
209///
210/// # Examples
211///
212/// ```rust,no_run
213/// # use reinhardt_testkit::fixtures::InMemoryRepository;
214/// # use reinhardt_db::migrations::{Migration, MigrationRepository};
215/// # #[tokio::main]
216/// # async fn main() {
217/// // #[tokio::test]
218/// // async fn test_repository() {
219/// let mut repo = InMemoryRepository::new();
220///
221/// let migration = Migration::new("0001_initial", "polls");
222///
223/// repo.save(&migration).await.unwrap();
224/// let retrieved = repo.get("polls", "0001_initial").await.unwrap();
225/// assert_eq!(retrieved.name, "0001_initial");
226/// // }
227/// # }
228/// ```
229pub struct InMemoryRepository {
230 migrations: HashMap<(String, String), Migration>,
231}
232
233impl InMemoryRepository {
234 /// Create a new empty InMemoryRepository
235 pub fn new() -> Self {
236 Self {
237 migrations: HashMap::new(),
238 }
239 }
240
241 /// Create an InMemoryRepository with initial migrations
242 pub fn with_migrations(migrations: Vec<Migration>) -> Self {
243 let mut repo = Self::new();
244 for migration in migrations {
245 let key = (migration.app_label.to_string(), migration.name.to_string());
246 repo.migrations.insert(key, migration);
247 }
248 repo
249 }
250
251 /// Clear all migrations from the repository
252 pub fn clear(&mut self) {
253 self.migrations.clear();
254 }
255
256 /// Get the number of migrations in the repository
257 pub fn len(&self) -> usize {
258 self.migrations.len()
259 }
260
261 /// Check if the repository is empty
262 pub fn is_empty(&self) -> bool {
263 self.migrations.is_empty()
264 }
265}
266
267impl Default for InMemoryRepository {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272
273#[async_trait]
274impl MigrationRepository for InMemoryRepository {
275 async fn save(&mut self, migration: &Migration) -> Result<()> {
276 let key = (migration.app_label.to_string(), migration.name.to_string());
277 self.migrations.insert(key, migration.clone());
278 Ok(())
279 }
280
281 async fn get(&self, app_label: &str, name: &str) -> Result<Migration> {
282 let key = (app_label.to_string(), name.to_string());
283 self.migrations.get(&key).cloned().ok_or_else(|| {
284 reinhardt_db::migrations::MigrationError::NotFound(format!("{}.{}", app_label, name))
285 })
286 }
287
288 async fn list(&self, app_label: &str) -> Result<Vec<Migration>> {
289 Ok(self
290 .migrations
291 .values()
292 .filter(|m| m.app_label == app_label)
293 .cloned()
294 .collect())
295 }
296
297 async fn delete(&mut self, app_label: &str, name: &str) -> Result<()> {
298 let key = (app_label.to_string(), name.to_string());
299 self.migrations.remove(&key).ok_or_else(|| {
300 reinhardt_db::migrations::MigrationError::NotFound(format!("{}.{}", app_label, name))
301 })?;
302 Ok(())
303 }
304}
305
306/// Creates a new TestMigrationSource for testing
307///
308/// Provides an empty migration source that can be populated with test migrations.
309///
310/// # Examples
311///
312/// ```rust,no_run
313/// # use reinhardt_testkit::fixtures::*;
314/// # use reinhardt_db::migrations::{Migration, MigrationSource};
315/// # use rstest::*;
316/// # #[tokio::main]
317/// # async fn main() {
318/// // #[rstest]
319/// // #[tokio::test]
320/// // async fn test_with_source(mut test_migration_source: TestMigrationSource) {
321/// let mut test_migration_source = TestMigrationSource::new();
322/// test_migration_source.add_migration(Migration::new("0001_initial", "polls"));
323///
324/// let migrations = test_migration_source.all_migrations().await.unwrap();
325/// assert_eq!(migrations.len(), 1);
326/// // }
327/// # }
328/// ```
329#[fixture]
330pub fn test_migration_source() -> TestMigrationSource {
331 TestMigrationSource::new()
332}
333
334/// Creates a new InMemoryRepository for testing
335///
336/// Provides an empty migration repository that stores migrations in memory.
337///
338/// # Examples
339///
340/// ```rust,no_run
341/// # use reinhardt_testkit::fixtures::*;
342/// # use reinhardt_db::migrations::{Migration, MigrationRepository};
343/// # use rstest::*;
344/// # #[tokio::main]
345/// # async fn main() {
346/// // #[rstest]
347/// // #[tokio::test]
348/// // async fn test_with_repository(mut in_memory_repository: InMemoryRepository) {
349/// let mut in_memory_repository = InMemoryRepository::new();
350/// let migration = Migration::new("0001_initial", "polls");
351///
352/// in_memory_repository.save(&migration).await.unwrap();
353/// let retrieved = in_memory_repository.get("polls", "0001_initial").await.unwrap();
354/// assert_eq!(retrieved.name, "0001_initial");
355/// }
356/// ```
357#[fixture]
358pub fn in_memory_repository() -> InMemoryRepository {
359 InMemoryRepository::new()
360}
361
362// ============================================================================
363// TestContainers-based Migration Executor Fixtures
364// ============================================================================
365
366/// Type alias for migration_executor fixture return value
367///
368/// Contains all elements from postgres_container plus the migration executor:
369/// - `DatabaseMigrationExecutor`: Migration executor instance
370/// - `ContainerAsync<GenericImage>`: PostgreSQL container
371/// - `Arc<PgPool>`: Database connection pool
372/// - `u16`: PostgreSQL port
373/// - `String`: Database URL
374#[cfg(feature = "testcontainers")]
375pub type MigrationExecutorFixture = (
376 DatabaseMigrationExecutor,
377 ContainerAsync<GenericImage>,
378 Arc<sqlx::PgPool>,
379 u16,
380 String,
381);
382
383/// Helper for applying database schema migrations in tests with PostgreSQL
384///
385/// Provides a convenient interface for creating database tables and applying
386/// schema operations during test setup. Holds a DatabaseMigrationExecutor
387/// and connection pool internally.
388///
389/// # Examples
390///
391/// ```rust,no_run
392/// # use reinhardt_testkit::fixtures::*;
393/// # use reinhardt_db::migrations::{Operation, ColumnDefinition, FieldType};
394/// # use rstest::*;
395/// #[rstest]
396/// #[tokio::test]
397/// async fn test_with_schema(#[future] postgres_table_creator: PostgresTableCreator) {
398/// let mut creator = postgres_table_creator.await;
399///
400/// let schema = vec![
401/// Operation::CreateTable {
402/// name: "users".to_string(),
403/// columns: vec![
404/// ColumnDefinition::new("id", FieldType::Integer),
405/// ColumnDefinition::new("name", FieldType::Text),
406/// ],
407/// constraints: vec![],
408/// without_rowid: None,
409/// interleave_in_parent: None,
410/// partition: None,
411/// },
412/// ];
413///
414/// creator.apply(schema).await.unwrap();
415///
416/// let pool = creator.pool();
417/// // Run tests using pool...
418/// }
419/// ```
420#[cfg(feature = "testcontainers")]
421pub struct PostgresTableCreator {
422 executor: DatabaseMigrationExecutor,
423 pool: Arc<sqlx::PgPool>,
424 container: ContainerAsync<GenericImage>,
425 port: u16,
426 url: String,
427}
428
429#[cfg(feature = "testcontainers")]
430impl PostgresTableCreator {
431 /// Create a new TableCreator
432 pub fn new(
433 executor: DatabaseMigrationExecutor,
434 container: ContainerAsync<GenericImage>,
435 pool: Arc<sqlx::PgPool>,
436 port: u16,
437 url: String,
438 ) -> Self {
439 Self {
440 executor,
441 pool,
442 container,
443 port,
444 url,
445 }
446 }
447
448 /// Apply schema operations by creating and executing a migration
449 ///
450 /// # Examples
451 ///
452 /// ```rust,no_run
453 /// # use reinhardt_testkit::fixtures::*;
454 /// # use reinhardt_db::migrations::{Operation, ColumnDefinition, FieldType};
455 /// # async fn example(mut creator: PostgresTableCreator) {
456 /// let schema = vec![
457 /// Operation::CreateTable {
458 /// name: "products".to_string(),
459 /// columns: vec![
460 /// ColumnDefinition::new("id", FieldType::Integer),
461 /// ColumnDefinition::new("name", FieldType::Text),
462 /// ],
463 /// constraints: vec![],
464 /// without_rowid: None,
465 /// interleave_in_parent: None,
466 /// partition: None,
467 /// },
468 /// ];
469 ///
470 /// creator.apply(schema).await.unwrap();
471 /// # }
472 /// ```
473 pub async fn apply(&mut self, schema: Vec<Operation>) -> Result<()> {
474 let mut migration = Migration::new("0001_test_schema", "testapp");
475
476 for operation in schema {
477 migration = migration.add_operation(operation);
478 }
479
480 self.executor
481 .apply_migrations(&[migration])
482 .await
483 .expect("Failed to apply test schema migrations");
484
485 Ok(())
486 }
487
488 /// Get a reference to the database connection pool
489 pub fn pool(&self) -> &Arc<sqlx::PgPool> {
490 &self.pool
491 }
492
493 /// Get the database URL
494 pub fn url(&self) -> &str {
495 &self.url
496 }
497
498 /// Get the database port
499 pub fn port(&self) -> u16 {
500 self.port
501 }
502
503 /// Get a reference to the container
504 ///
505 /// This is useful for advanced test scenarios that need direct container access.
506 pub fn container(&self) -> &ContainerAsync<GenericImage> {
507 &self.container
508 }
509
510 /// Insert data into a table using reinhardt-query
511 ///
512 /// This method provides a convenient way to insert test data using type-safe
513 /// reinhardt-query builders instead of raw SQL strings.
514 ///
515 /// # Examples
516 ///
517 /// ```rust,no_run
518 /// # use reinhardt_testkit::fixtures::*;
519 /// # use reinhardt_query::prelude::Value;
520 /// # async fn example(creator: &PostgresTableCreator) {
521 /// creator.insert_data(
522 /// "users",
523 /// vec!["id", "name", "email"],
524 /// vec![
525 /// vec![
526 /// Value::Int(Some(1)),
527 /// Value::String(Some(Box::new("Alice".to_string()))),
528 /// Value::String(Some(Box::new("alice@example.com".to_string()))),
529 /// ],
530 /// ],
531 /// ).await.unwrap();
532 /// # }
533 /// ```
534 pub async fn insert_data(
535 &self,
536 table: &str,
537 columns: Vec<&str>,
538 values: Vec<Vec<reinhardt_query::prelude::Value>>,
539 ) -> Result<()> {
540 use reinhardt_query::prelude::{Alias, PostgresQueryBuilder, Query, QueryStatementBuilder};
541
542 for row_values in values {
543 let mut query = Query::insert();
544 query
545 .into_table(Alias::new(table))
546 .columns(columns.iter().map(|&c| Alias::new(c)));
547
548 query.values_panic(row_values);
549
550 // Build SQL string
551 let sql = query.to_string(PostgresQueryBuilder::new());
552
553 sqlx::query(&sql)
554 .execute(self.pool.as_ref())
555 .await
556 .map_err(MigrationError::SqlError)?;
557 }
558 Ok(())
559 }
560
561 /// Execute custom SQL (fallback for complex cases)
562 ///
563 /// This method allows executing arbitrary SQL statements when reinhardt-query
564 /// is insufficient for complex schema operations or PostgreSQL-specific features.
565 ///
566 /// # Examples
567 ///
568 /// ```rust,no_run
569 /// # use reinhardt_testkit::fixtures::*;
570 /// # async fn example(creator: &PostgresTableCreator) {
571 /// // PostgreSQL-specific feature
572 /// creator.execute_sql(
573 /// "CREATE TABLE test (id SERIAL PRIMARY KEY) WITH (autovacuum_enabled = false)"
574 /// ).await.unwrap();
575 /// # }
576 /// ```
577 pub async fn execute_sql(&self, sql: &str) -> Result<sqlx::postgres::PgQueryResult> {
578 sqlx::query(sql)
579 .execute(self.pool.as_ref())
580 .await
581 .map_err(MigrationError::SqlError)
582 }
583
584 /// Begin a transaction for advanced test scenarios
585 ///
586 /// This is useful for testing two-phase commit (2PC) or other transaction-based
587 /// operations.
588 ///
589 /// # Examples
590 ///
591 /// ```rust,no_run
592 /// # use reinhardt_testkit::fixtures::*;
593 /// # async fn example(creator: &PostgresTableCreator) {
594 /// let mut tx = creator.begin_transaction().await.unwrap();
595 /// // Perform transactional operations...
596 /// tx.commit().await.unwrap();
597 /// # }
598 /// ```
599 pub async fn begin_transaction(&self) -> Result<sqlx::Transaction<'_, sqlx::Postgres>> {
600 self.pool.begin().await.map_err(MigrationError::SqlError)
601 }
602}
603
604// ============================================================================
605// Migration Executor and Table Creator Fixtures
606// ============================================================================
607
608/// Creates a DatabaseMigrationExecutor connected to a test PostgreSQL container
609///
610/// This fixture combines postgres_container with migration executor creation,
611/// providing a ready-to-use migration executor for tests.
612///
613/// # Type Signature
614///
615/// Returns `MigrationExecutorFixture`:
616/// - `DatabaseMigrationExecutor`: Migration executor instance
617/// - `ContainerAsync<GenericImage>`: PostgreSQL container
618/// - `Arc<PgPool>`: Database connection pool
619/// - `u16`: PostgreSQL port
620/// - `String`: Database URL
621///
622/// # Examples
623///
624/// ```rust,no_run
625/// # use reinhardt_testkit::fixtures::*;
626/// # use reinhardt_db::migrations::Migration;
627/// # use rstest::*;
628/// #[rstest]
629/// #[tokio::test]
630/// async fn test_with_executor(
631/// #[future] migration_executor: MigrationExecutorFixture
632/// ) {
633/// let (mut executor, _container, _pool, _port, _url) = migration_executor.await;
634///
635/// let migration = Migration::new("0001_test", "testapp");
636/// executor.apply_migrations(&[migration]).await.unwrap();
637/// }
638/// ```
639#[cfg(feature = "testcontainers")]
640#[fixture]
641pub async fn migration_executor(
642 #[future] postgres_container: (ContainerAsync<GenericImage>, Arc<sqlx::PgPool>, u16, String),
643) -> MigrationExecutorFixture {
644 let (container, pool, port, url) = postgres_container.await;
645
646 let connection = DatabaseConnection::connect_postgres(&url)
647 .await
648 .expect("Failed to connect to test database");
649
650 let executor = DatabaseMigrationExecutor::new(connection);
651
652 (executor, container, pool, port, url)
653}
654
655/// Creates a PostgresTableCreator for applying test schemas
656///
657/// This fixture combines migration_executor with a convenient helper structure
658/// for applying database schema operations in PostgreSQL tests.
659///
660/// # Type Signature
661///
662/// Returns `` `PostgresTableCreator` ``:
663/// - Helper with methods to apply schema operations
664/// - Provides access to connection pool, URL, and port
665/// - Includes methods for data insertion and custom SQL execution
666///
667/// # Examples
668///
669/// ```rust,no_run
670/// # use reinhardt_testkit::fixtures::*;
671/// # use reinhardt_db::migrations::{Operation, ColumnDefinition, FieldType};
672/// # use rstest::*;
673/// #[rstest]
674/// #[tokio::test]
675/// async fn test_with_creator(#[future] postgres_table_creator: PostgresTableCreator) {
676/// let mut creator = postgres_table_creator.await;
677///
678/// let schema = vec![
679/// Operation::CreateTable {
680/// name: "test_table".to_string(),
681/// columns: vec![
682/// ColumnDefinition::new("id", FieldType::Integer),
683/// ],
684/// constraints: vec![],
685/// without_rowid: None,
686/// interleave_in_parent: None,
687/// partition: None,
688/// },
689/// ];
690///
691/// creator.apply(schema).await.unwrap();
692/// let pool = creator.pool();
693/// // Use pool for testing...
694/// }
695/// ```
696#[cfg(feature = "testcontainers")]
697#[fixture]
698pub async fn postgres_table_creator(
699 #[future] migration_executor: MigrationExecutorFixture,
700) -> PostgresTableCreator {
701 let (executor, container, pool, port, url) = migration_executor.await;
702 PostgresTableCreator::new(executor, container, pool, port, url)
703}
704
705#[cfg(test)]
706mod tests {
707 use super::*;
708 use reinhardt_db::migrations::Migration;
709 use reinhardt_db::migrations::registry::MigrationRegistry;
710
711 #[rstest]
712 fn test_migration_registry_fixture(migration_registry: LocalRegistry) {
713 assert!(migration_registry.all_migrations().is_empty());
714 }
715
716 #[rstest]
717 fn test_registry_isolation_between_tests(migration_registry: LocalRegistry) {
718 // This test runs independently - registry should be empty
719 assert_eq!(migration_registry.all_migrations().len(), 0);
720
721 migration_registry
722 .register(Migration::new("0001_initial", "test_app"))
723 .unwrap();
724
725 assert_eq!(migration_registry.all_migrations().len(), 1);
726 }
727
728 #[rstest]
729 fn test_another_isolated_test(migration_registry: LocalRegistry) {
730 // Even though previous test registered a migration,
731 // this new fixture instance should be empty
732 assert_eq!(migration_registry.all_migrations().len(), 0);
733 }
734
735 #[cfg(feature = "testcontainers")]
736 mod testcontainer_fixtures {
737 use super::*;
738 use reinhardt_db::migrations::{ColumnDefinition, DatabaseType, FieldType, Operation};
739
740 #[rstest]
741 #[tokio::test]
742 async fn test_migration_executor_fixture(
743 #[future] migration_executor: MigrationExecutorFixture,
744 ) {
745 let (executor, _container, _pool, _port, _url) = migration_executor.await;
746
747 // Verify executor is connected to PostgreSQL
748 assert_eq!(executor.database_type(), DatabaseType::Postgres);
749 }
750
751 #[rstest]
752 #[tokio::test]
753 async fn test_postgres_table_creator_fixture(
754 #[future] postgres_table_creator: PostgresTableCreator,
755 ) {
756 let mut creator = postgres_table_creator.await;
757
758 // Define schema directly in test
759 let schema = vec![Operation::CreateTable {
760 name: "fixture_test_table".to_string(),
761 columns: vec![
762 ColumnDefinition::new("id", FieldType::Integer),
763 ColumnDefinition::new("value", FieldType::Text),
764 ],
765 constraints: vec![],
766 without_rowid: None,
767 interleave_in_parent: None,
768 partition: None,
769 }];
770
771 // Apply schema
772 creator.apply(schema).await.unwrap();
773
774 // Verify table was created
775 let pool = creator.pool();
776 let result = sqlx::query("SELECT * FROM fixture_test_table")
777 .fetch_all(pool.as_ref())
778 .await
779 .unwrap();
780
781 assert!(result.is_empty());
782 }
783 }
784}