391 lines
8.2 KiB
Markdown
391 lines
8.2 KiB
Markdown
# Attune Common Library - Integration Tests
|
|
|
|
This directory contains integration tests for the Attune common library, specifically testing the database repository layer and migrations.
|
|
|
|
## Overview
|
|
|
|
The test suite includes:
|
|
|
|
- **Migration Tests** (`migration_tests.rs`) - Verify database schema, migrations, and constraints
|
|
- **Repository Tests** - Comprehensive CRUD and transaction tests for each repository:
|
|
- `pack_repository_tests.rs` - Pack repository operations
|
|
- `action_repository_tests.rs` - Action repository operations
|
|
- Additional repository tests for all other entities
|
|
- **Test Helpers** (`helpers.rs`) - Fixtures, utilities, and common test setup
|
|
|
|
## Prerequisites
|
|
|
|
Before running the tests, ensure you have:
|
|
|
|
1. **PostgreSQL** installed and running
|
|
2. **Test database** created and configured
|
|
3. **Environment variables** set (via `.env.test`)
|
|
|
|
### Setting Up the Test Database
|
|
|
|
```bash
|
|
# Create the test database
|
|
make db-test-create
|
|
|
|
# Run migrations on test database
|
|
make db-test-migrate
|
|
|
|
# Or do both at once
|
|
make db-test-setup
|
|
```
|
|
|
|
To reset the test database:
|
|
|
|
```bash
|
|
make db-test-reset
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
### Run All Integration Tests
|
|
|
|
```bash
|
|
# Automatic setup and run
|
|
make test-integration
|
|
|
|
# Or manually
|
|
cargo test --test '*' -p attune-common -- --test-threads=1
|
|
```
|
|
|
|
### Run Specific Test Files
|
|
|
|
```bash
|
|
# Run only migration tests
|
|
cargo test --test migration_tests -p attune-common
|
|
|
|
# Run only pack repository tests
|
|
cargo test --test pack_repository_tests -p attune-common
|
|
|
|
# Run only action repository tests
|
|
cargo test --test action_repository_tests -p attune-common
|
|
```
|
|
|
|
### Run Specific Tests
|
|
|
|
```bash
|
|
# Run a single test by name
|
|
cargo test test_create_pack -p attune-common
|
|
|
|
# Run tests matching a pattern
|
|
cargo test test_create -p attune-common
|
|
|
|
# Run with output
|
|
cargo test test_create_pack -p attune-common -- --nocapture
|
|
```
|
|
|
|
## Test Configuration
|
|
|
|
Test configuration is loaded from `.env.test` in the project root. Key settings:
|
|
|
|
```bash
|
|
# Test database URL
|
|
ATTUNE__DATABASE__URL=postgresql://postgres:postgres@localhost:5432/attune_test
|
|
|
|
# Enable SQL logging for debugging
|
|
ATTUNE__DATABASE__LOG_STATEMENTS=true
|
|
|
|
# Verbose logging
|
|
ATTUNE__LOG__LEVEL=debug
|
|
RUST_LOG=debug,sqlx=warn
|
|
```
|
|
|
|
## Test Structure
|
|
|
|
### Test Helpers (`helpers.rs`)
|
|
|
|
The helpers module provides:
|
|
|
|
- **Database Setup**: `create_test_pool()`, `clean_database()`
|
|
- **Fixtures**: Builder pattern for creating test data
|
|
- `PackFixture` - Create test packs
|
|
- `ActionFixture` - Create test actions
|
|
- `RuntimeFixture` - Create test runtimes
|
|
- And more for all entities
|
|
- **Utilities**: Transaction helpers, assertions
|
|
|
|
Example fixture usage:
|
|
|
|
```rust
|
|
use helpers::*;
|
|
|
|
let pool = create_test_pool().await.unwrap();
|
|
clean_database(&pool).await.unwrap();
|
|
|
|
let pack_repo = PackRepository::new(&pool);
|
|
|
|
// Use fixture to create test data
|
|
let pack = PackFixture::new("test.pack")
|
|
.with_version("2.0.0")
|
|
.with_name("Custom Pack Name")
|
|
.create(&pack_repo)
|
|
.await
|
|
.unwrap();
|
|
```
|
|
|
|
### Test Organization
|
|
|
|
Each test file follows this pattern:
|
|
|
|
1. **Import helpers module**: `mod helpers;`
|
|
2. **Setup phase**: Create pool and clean database
|
|
3. **Test execution**: Perform operations
|
|
4. **Assertions**: Verify expected outcomes
|
|
5. **Cleanup**: Automatic via `clean_database()` or transactions
|
|
|
|
Example test:
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_create_pack() {
|
|
// Setup
|
|
let pool = create_test_pool().await.unwrap();
|
|
clean_database(&pool).await.unwrap();
|
|
|
|
let repo = PackRepository::new(&pool);
|
|
|
|
// Execute
|
|
let pack = PackFixture::new("test.pack")
|
|
.create(&repo)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Assert
|
|
assert_eq!(pack.ref_name, "test.pack");
|
|
assert!(pack.created_at.timestamp() > 0);
|
|
}
|
|
```
|
|
|
|
## Test Categories
|
|
|
|
### CRUD Operations
|
|
|
|
Tests verify basic Create, Read, Update, Delete operations:
|
|
|
|
- Creating entities with valid data
|
|
- Retrieving entities by ID and other fields
|
|
- Listing and pagination
|
|
- Updating partial and full records
|
|
- Deleting entities
|
|
|
|
### Constraint Validation
|
|
|
|
Tests verify database constraints:
|
|
|
|
- Unique constraints (e.g., pack ref_name + version)
|
|
- Foreign key constraints
|
|
- NOT NULL constraints
|
|
- Check constraints
|
|
|
|
### Transaction Support
|
|
|
|
Tests verify transaction behavior:
|
|
|
|
- Commit preserves changes
|
|
- Rollback discards changes
|
|
- Isolation between transactions
|
|
|
|
### Error Handling
|
|
|
|
Tests verify proper error handling:
|
|
|
|
- Duplicate key violations
|
|
- Foreign key violations
|
|
- Not found scenarios
|
|
|
|
### Cascading Deletes
|
|
|
|
Tests verify cascade delete behavior:
|
|
|
|
- Deleting a pack deletes associated actions
|
|
- Deleting a runtime deletes associated workers
|
|
- And other cascade relationships
|
|
|
|
## Best Practices
|
|
|
|
### 1. Clean Database Before Tests
|
|
|
|
Always clean the database at the start of each test:
|
|
|
|
```rust
|
|
let pool = create_test_pool().await.unwrap();
|
|
clean_database(&pool).await.unwrap();
|
|
```
|
|
|
|
### 2. Use Fixtures for Test Data
|
|
|
|
Use fixture builders instead of manual creation:
|
|
|
|
```rust
|
|
// Good
|
|
let pack = PackFixture::new("test.pack").create(&repo).await.unwrap();
|
|
|
|
// Avoid
|
|
let create = CreatePack { /* ... */ };
|
|
let pack = repo.create(&create).await.unwrap();
|
|
```
|
|
|
|
### 3. Test Isolation
|
|
|
|
Each test should be independent:
|
|
|
|
- Don't rely on data from other tests
|
|
- Clean database between tests
|
|
- Use unique names/IDs
|
|
|
|
### 4. Single-Threaded Execution
|
|
|
|
Run integration tests single-threaded to avoid race conditions:
|
|
|
|
```bash
|
|
cargo test -- --test-threads=1
|
|
```
|
|
|
|
### 5. Descriptive Test Names
|
|
|
|
Use clear, descriptive test names:
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_create_pack_duplicate_ref_version() { /* ... */ }
|
|
```
|
|
|
|
### 6. Test Both Success and Failure
|
|
|
|
Test both happy paths and error cases:
|
|
|
|
```rust
|
|
#[tokio::test]
|
|
async fn test_create_pack() { /* success case */ }
|
|
|
|
#[tokio::test]
|
|
async fn test_create_pack_duplicate_ref_version() { /* error case */ }
|
|
```
|
|
|
|
## Debugging Tests
|
|
|
|
### Enable SQL Logging
|
|
|
|
Set in `.env.test`:
|
|
|
|
```bash
|
|
ATTUNE__DATABASE__LOG_STATEMENTS=true
|
|
RUST_LOG=debug,sqlx=debug
|
|
```
|
|
|
|
### Run with Output
|
|
|
|
```bash
|
|
cargo test test_name -- --nocapture
|
|
```
|
|
|
|
### Use Transaction Rollback
|
|
|
|
Wrap tests in transactions that rollback to inspect state:
|
|
|
|
```rust
|
|
let mut tx = pool.begin().await.unwrap();
|
|
// ... test operations ...
|
|
// Drop tx without commit to rollback
|
|
```
|
|
|
|
### Check Database State
|
|
|
|
Connect to test database directly:
|
|
|
|
```bash
|
|
psql -d attune_test -U postgres
|
|
```
|
|
|
|
## Continuous Integration
|
|
|
|
For CI environments:
|
|
|
|
```bash
|
|
# Setup test database
|
|
createdb attune_test
|
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/attune_test sqlx migrate run
|
|
|
|
# Run tests
|
|
cargo test --test '*' -p attune-common -- --test-threads=1
|
|
```
|
|
|
|
## Common Issues
|
|
|
|
### Database Connection Errors
|
|
|
|
**Issue**: Cannot connect to database
|
|
|
|
**Solution**:
|
|
- Ensure PostgreSQL is running
|
|
- Check credentials in `.env.test`
|
|
- Verify test database exists
|
|
|
|
### Migration Errors
|
|
|
|
**Issue**: Migrations fail
|
|
|
|
**Solution**:
|
|
- Run `make db-test-reset` to reset test database
|
|
- Ensure migrations are in `migrations/` directory
|
|
|
|
### Flaky Tests
|
|
|
|
**Issue**: Tests fail intermittently
|
|
|
|
**Solution**:
|
|
- Run single-threaded: `--test-threads=1`
|
|
- Clean database before each test
|
|
- Avoid time-dependent assertions
|
|
|
|
### Foreign Key Violations
|
|
|
|
**Issue**: Cannot delete entity due to foreign keys
|
|
|
|
**Solution**:
|
|
- Use `clean_database()` which handles dependencies
|
|
- Test cascade deletes explicitly
|
|
- Delete in correct order (children before parents)
|
|
|
|
## Adding New Tests
|
|
|
|
To add tests for a new repository:
|
|
|
|
1. Create test file: `tests/<entity>_repository_tests.rs`
|
|
2. Import helpers: `mod helpers;`
|
|
3. Create fixtures in `helpers.rs` if needed
|
|
4. Write comprehensive CRUD tests
|
|
5. Test constraints and error cases
|
|
6. Test transactions
|
|
7. Run and verify: `cargo test --test <entity>_repository_tests`
|
|
|
|
## Test Coverage
|
|
|
|
To generate test coverage reports:
|
|
|
|
```bash
|
|
# Install tarpaulin
|
|
cargo install cargo-tarpaulin
|
|
|
|
# Generate coverage
|
|
cargo tarpaulin --out Html --output-dir coverage --test '*' -p attune-common
|
|
```
|
|
|
|
## Additional Resources
|
|
|
|
- [SQLx Documentation](https://docs.rs/sqlx)
|
|
- [Tokio Testing Guide](https://tokio.rs/tokio/topics/testing)
|
|
- [Rust Testing Best Practices](https://doc.rust-lang.org/book/ch11-00-testing.html)
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
|
|
- Check existing tests for examples
|
|
- Review helper functions in `helpers.rs`
|
|
- Consult the main project documentation
|
|
- Open an issue on the project repository |