switchy_schema_test_utils 0.3.0

Switchy Schema Test Utils package
# Switchy Schema Test Utils

Test utilities for `switchy_schema` that provide comprehensive migration testing capabilities.

## Overview

This package provides testing utilities for validating database migrations:

- `verify_migrations_full_cycle` - Basic up/down migration testing
- `verify_migrations_with_state` - Migration testing with pre-seeded state
- `verify_migrations_with_mutations` - Testing with data mutations between migration steps
- `MigrationTestBuilder` - Builder pattern for complex migration scenarios with breakpoints (requires `sqlite` feature)
- `MigrationSnapshotTest` - Schema snapshot comparison testing (requires `snapshots` feature)
- `assertions` module - Database schema and state assertion helpers (requires `sqlite` feature)
- `create_empty_in_memory` - Create an in-memory SQLite database for testing (requires `sqlite` feature)

## Features

- **Database Support**: SQLite support via feature flags (additional databases planned)
- **Comprehensive Testing**: Full migration lifecycle validation
- **State Preservation**: Verify data integrity during migrations
- **Mutation Testing**: Test against various database states and scenarios
- **Async Support**: Full async/await support for modern Rust applications
- **Snapshot Testing**: Schema snapshot comparison (optional `snapshots` feature)

## Usage

Add to your `Cargo.toml`:

```toml
[dev-dependencies]
switchy_schema_test_utils = { workspace = true, features = ["sqlite"] }
```

### Creating a Test Database

Use `create_empty_in_memory` to create an in-memory SQLite database for testing (requires `sqlite` feature):

```rust
use switchy_schema_test_utils::create_empty_in_memory;

#[tokio::test]
async fn test_example() {
    let db = create_empty_in_memory().await.unwrap();
    // Use db for migration testing...
}
```

### Basic Migration Testing

```rust
use switchy_schema_test_utils::verify_migrations_full_cycle;

#[tokio::test]
async fn test_migrations() {
    let db = /* your database connection */;
    let migrations = vec![/* your migrations */];
    verify_migrations_full_cycle(db, migrations).await.unwrap();
}
```

### State Preservation Testing

```rust
use switchy_schema_test_utils::verify_migrations_with_state;

#[tokio::test]
async fn test_migrations_with_data() {
    let db = /* your database connection */;
    let migrations = vec![/* your migrations */];
    verify_migrations_with_state(
        db,
        migrations,
        |db| Box::pin(async move {
            // Setup initial state before migrations
            db.exec_raw("INSERT INTO existing_table (id) VALUES (1)").await?;
            Ok(())
        })
    ).await.unwrap();
}
```

### Mutation Testing

```rust
use switchy_schema_test_utils::verify_migrations_with_mutations;
use switchy_schema_test_utils::mutations::MutationBuilder;

#[tokio::test]
async fn test_migrations_comprehensive() {
    let db = /* your database connection */;
    let migrations = vec![/* your migrations */];
    let mutations = MutationBuilder::new()
        .add_mutation("001_create_users", "INSERT INTO users (name) VALUES ('test')")
        .build();
    verify_migrations_with_mutations(
        db,
        migrations,
        mutations,
    ).await.unwrap();
}
```

### Advanced: Migration Test Builder (requires `sqlite` feature)

```rust
use switchy_schema_test_utils::MigrationTestBuilder;

#[tokio::test]
async fn test_data_migration() {
    let db = /* your database connection */;
    let migrations = vec![/* your migrations */];

    MigrationTestBuilder::new(migrations)
        .with_data_before("002_migrate_data", |db| {
            Box::pin(async move {
                // Insert test data before the migration runs
                db.exec_raw("INSERT INTO old_table (value) VALUES ('test')").await?;
                Ok(())
            })
        })
        .with_data_after("002_migrate_data", |db| {
            Box::pin(async move {
                // Verify migration transformed data correctly
                db.exec_raw("SELECT * FROM new_table WHERE value = 'test'").await?;
                Ok(())
            })
        })
        .run(db)
        .await
        .unwrap();
}
```

### Schema and State Assertions (requires `sqlite` feature)

```rust
use switchy_schema_test_utils::assertions::*;

#[tokio::test]
async fn test_migration_schema() {
    let db = /* your database connection */;

    // Verify table existence
    assert_table_exists(db, "users").await.unwrap();
    assert_table_not_exists(db, "old_table").await.unwrap();

    // Verify columns
    assert_column_exists(db, "users", "email", "TEXT").await.unwrap();

    // Verify data state
    assert_row_count(db, "users", 5).await.unwrap();
    assert_row_count_min(db, "posts", 10).await.unwrap();

    // Verify migrations applied
    assert_migrations_applied(db, &["001_initial", "002_add_users"]).await.unwrap();
}
```

### Snapshot Testing (requires `snapshots` feature)

```rust
use switchy_schema_test_utils::MigrationSnapshotTest;

#[tokio::test]
async fn test_migration_snapshots() {
    MigrationSnapshotTest::new("my_migration_test")
        .migrations_dir("migrations")
        .expected_tables(vec!["users".to_string(), "posts".to_string()])
        .assert_schema(true)
        .assert_sequence(true)
        .run()
        .await
        .unwrap();
}
```

## Cargo Features

- `sqlite` - Enable SQLite support (included in default features)
- `snapshots` - Enable schema snapshot testing capabilities
- `decimal` - Enable decimal type support (included in default features)
- `uuid` - Enable UUID type support (included in default features)
- `fail-on-warnings` - Treat warnings as errors during compilation

## Examples

See the `examples/` directory in the parent `switchy_schema` package for complete working examples of each testing utility.