Files
attune/work-summary/sessions/2024-01-13-integration-testing-setup.md
2026-02-04 17:46:30 -06:00

13 KiB

Work Summary: API Integration Testing Infrastructure Setup

Date: 2024-01-13
Session Duration: ~2 hours
Focus Area: Phase 2.12 - API Integration Testing (Initial Setup)


Objectives

Set up comprehensive integration testing infrastructure for the Attune API service to enable automated testing of all endpoints, authentication flows, and business logic.


What Was Accomplished

1. Test Infrastructure Setup

Added Library Target to API Crate

  • Created src/lib.rs to expose internal modules for testing
  • Updated Cargo.toml to include [lib] target
  • Restructured main.rs to use the library modules
  • This enables integration tests to import and test internal components

Files Modified:

  • crates/api/Cargo.toml - Added [lib] section
  • crates/api/src/lib.rs - Created library entry point
  • crates/api/src/main.rs - Updated to use library modules

2. Refactored Server and AppState for Testability

Changes:

  • Updated Server::new() to accept Arc<AppState> and derive host/port from config
  • Simplified AppState::new() to only require PgPool and Config (derives JWT config internally)
  • Added Server::router() method to expose router for testing without starting server
  • These changes make the API more testable while maintaining production functionality

Files Modified:

  • crates/api/src/server.rs - Simplified constructor, added router() method
  • crates/api/src/state.rs - Updated to derive JWT config from security config
  • crates/api/src/main.rs - Updated to use new constructor signatures

Test Infrastructure Created

New Files:

  • crates/api/src/lib.rs - Library entry point for API crate (enables integration testing)
  • crates/api/tests/helpers.rs - Comprehensive test helpers and fixtures (424 lines)
  • crates/api/tests/health_and_auth_tests.rs - Integration tests for health and auth endpoints (398 lines)

Test Infrastructure Features:

  1. TestContext - Manages test lifecycle:

    • Database pool creation and cleanup
    • Test server instantiation
    • Authenticated request helpers
    • Automatic user creation for auth tests
  2. Test Helpers:

    • create_test_pool() - Database connection for tests
    • clean_database() - Reset database between tests
    • TestContext::new() - Create test server
    • TestContext::with_auth() - Create authenticated context
    • Request helpers: get(), post(), put(), delete()
  3. Test Fixtures:

    • create_test_pack() - Create test pack data
    • create_test_action() - Create test actions
    • create_test_trigger() - Create test triggers
  4. TestResponse Helper:

    • Async body handling
    • JSON deserialization
    • Status code assertions
    • Text response extraction

Test Coverage

Health & Authentication Tests (15 tests)

Health Endpoints (4 tests):

  • test_health_check - Basic health check
  • test_health_ping - Ping endpoint
  • test_health_ready - Readiness check with DB status
  • test_health_live - Liveness check

Authentication Tests (11 tests):

  • test_register_user - Successful user registration
  • test_register_duplicate_user - Conflict handling
  • test_register_invalid_password - Password validation
  • test_login_success - Successful login flow
  • test_login_wrong_password - Authentication failure
  • test_login_nonexistent_user - User not found handling
  • test_get_current_user - Protected endpoint with auth
  • test_get_current_user_unauthorized - Auth required
  • test_get_current_user_invalid_token - Invalid token handling
  • test_refresh_token - Token refresh flow
  • test_refresh_with_invalid_token - Error handling

Test Coverage

  • Health endpoints: All 4 endpoints (health, ping, ready, live)
  • Authentication: Register, login, current user, refresh token
  • Error cases: Duplicate users, wrong passwords, invalid tokens, unauthorized access

Technical Implementation

Infrastructure Created

  1. Test Helpers Module (crates/api/tests/helpers.rs):

    • TestContext struct for managing test state
    • Database setup and cleanup functions
    • Helper methods for making authenticated HTTP requests (GET, POST, PUT, DELETE)
    • Fixture functions for creating test data (packs, actions, triggers)
    • Test response wrapper with status and JSON parsing
    • Clean database function to reset state between tests
  2. Library Target (crates/api/src/lib.rs):

    • Converted API from binary-only to library + binary
    • Exports all necessary modules for testing
    • Enables integration tests to import and use server components
  3. Test Configuration:

    • Uses config.test.yaml for test-specific settings
    • Separate test database (attune_test)
    • Fixed JWT secrets for reproducible tests
    • Minimal logging (WARN level)
  4. Test Infrastructure:

    • Test context with server setup
    • Authenticated request helpers
    • Database cleanup helpers
    • Test fixtures for common entities
    • Custom Result type for test errors

Tests Implemented

Health Check Endpoints (4 tests)

  • test_health_check - Basic health check
  • test_health_ping - Ping endpoint
  • test_health_ready - Readiness check with database status
  • test_health_live - Liveness check

Authentication Endpoints (10 tests)

  • test_register_user - User registration success
  • test_register_duplicate_user - Duplicate user detection
  • test_register_invalid_password - Password validation
  • test_login_success - Successful authentication
  • test_login_wrong_password - Invalid credentials
  • test_login_nonexistent_user - User not found
  • test_get_current_user - Authenticated user info
  • test_get_current_user_unauthorized - Missing token handling
  • test_get_current_user_invalid_token - Invalid token handling
  • test_refresh_token - Token refresh flow
  • test_refresh_with_invalid_token - Invalid refresh token handling

Implementation Status

Complete

  1. Test Infrastructure Setup

    • Created tests/ directory for integration tests
    • Created comprehensive test helpers (tests/helpers.rs)
    • Added library target to Cargo.toml to enable test imports
    • Created lib.rs to export API modules for testing
    • Updated main.rs to use library modules
    • Added test dependencies (tower, hyper, http-body-util)
  2. Test Helpers Module (tests/helpers.rs)

    • TestContext struct for managing test state
    • Database connection and cleanup utilities
    • Authenticated request helpers (GET, POST, PUT, DELETE)
    • Test fixtures for creating packs, actions, triggers
    • Test user creation and authentication
    • Response wrapper with JSON deserialization
  3. Health & Auth Integration Tests (15 tests)

    • Health check endpoints (4 tests)
    • User registration (3 tests - success, duplicate, validation)
    • Login/authentication (3 tests)
    • Current user endpoint (3 tests)
    • Token refresh (2 tests)

Architecture Changes

Made API Crate a Library

To enable integration testing, the API crate needed to be accessible as a library:

Changes:

  1. Added [lib] section to Cargo.toml:

    [lib]
    name = "attune_api"
    path = "src/lib.rs"
    
  2. Created src/lib.rs to export public modules:

    • auth, dto, middleware, openapi, routes, server, state
    • Re-exports Server and AppState for convenience
  3. Updated main.rs to use library imports:

    use attune_api::{AppState, Server};
    

Simplified Server and AppState Constructors

Changes to server.rs:

  • Modified Server::new() to take Arc<AppState> directly
  • Extracts host/port from config instead of requiring them as parameters
  • Added public router() method for testing

Changes to state.rs:

  • Simplified AppState::new() to take only PgPool and Config
  • Derives JWT config and CORS origins from the Config internally
  • Eliminates need for manual JWT config construction

Benefits:

  • Cleaner API surface
  • Easier to construct in tests
  • Single source of truth (Config)
  • Less parameter passing

Test Infrastructure

Created tests/helpers.rs:

  • TestContext struct for managing test state, server, and authentication
  • create_test_pool() - Database pool creation
  • clean_database() - Clean all tables between tests
  • TestResponse wrapper for convenient assertions
  • HTTP helper methods: get(), post(), put(), delete()
  • Fixture functions: create_test_pack(), create_test_action(), create_test_trigger()
  • Environment initialization with proper config loading

Created tests/health_and_auth_tests.rs:

  • 15 integration tests for health and authentication endpoints
  • Tests cover:
    • Health check endpoints (4 tests)
    • User registration (happy path, duplicates, validation)
    • Login (success, wrong password, nonexistent user)
    • Get current user (with/without auth, invalid token)
    • Token refresh (valid and invalid tokens)

Test Dependencies Added

[dev-dependencies]
mockall = { workspace = true }
tower = { workspace = true }
hyper = { version = "1.0", features = ["full"] }
http-body-util = "0.1"

Current Status

Completed

  1. Binary converted to lib+bin structure
  2. Server and AppState constructors simplified
  3. Test infrastructure fully implemented
  4. 15 comprehensive health and auth tests written
  5. All code compiles successfully

Issues Resolved

  • Tests fail with configuration error - FIXED: Load config directly from file instead of env vars
  • Database doesn't exist - FIXED: Created attune_test database and ran migrations
  • Table names mismatch - FIXED: Updated identity repository to use singular table names with attune. schema prefix

⚠️ Remaining Issues

  • Repository table name inconsistency: Most repositories use plural table names (actions, packs, rules) but database has singular tables (action, pack, rule)
  • Impact: Only identity-related tests work; other CRUD operations will fail
  • Solution: Need to systematically fix all repository queries to use singular table names with schema prefix

📋 Remaining Work

Phase 2.12 Continuation:

  1. Fix configuration loading issue in tests DONE
  2. Ensure test database is running DONE
  3. Fix identity repository table names DONE
  4. Fix remaining test failures (8 passed, 8 failed):
    • Health endpoints returning different response structures
    • Auth endpoints need response format adjustments
    • Test assertions need to match actual API responses
  5. Fix all repository table names systematically (actions, packs, triggers, rules, etc.)
  6. Write integration tests for remaining endpoints:
    • Packs CRUD (5 endpoints)
    • Actions CRUD (5 endpoints)
    • Triggers CRUD (4 endpoints)
    • Rules CRUD (4 endpoints)
    • Executions (2 endpoints)
    • Inquiries (4 endpoints)
    • Events (2 endpoints)
    • Secrets/Keys (5 endpoints)
  7. Test pagination, filtering, error handling
  8. Document testing best practices

Files Created/Modified

Created

  • crates/api/src/lib.rs - Library entry point
  • crates/api/tests/helpers.rs - Test infrastructure (424 lines)
  • crates/api/tests/health_and_auth_tests.rs - Health/auth tests (398 lines)

Modified

  • crates/api/Cargo.toml - Added [lib] section and test dependencies
  • crates/api/src/main.rs - Use library imports
  • crates/api/src/server.rs - Simplified constructor, added router() method
  • crates/api/src/state.rs - Simplified constructor, derive from Config
  • crates/api/tests/helpers.rs - Fixed config loading, table names in clean_database
  • crates/common/src/repositories/identity.rs - Fixed all table references to use attune.identity, attune.permission_set, attune.permission_assignment

Technical Notes

TestContext Pattern

The TestContext provides a fluent API for test setup:

let ctx = TestContext::new()
    .await?
    .with_auth()
    .await?;

let response = ctx.get("/auth/me", ctx.token()).await?;
assert_eq!(response.status(), StatusCode::OK);

Test Isolation

  • Each test starts with a clean database via clean_database()
  • Tests use separate test database (attune_test)
  • User creation happens via API registration for realistic tokens

Configuration Issue

The error suggests the config library is trying to parse an environment variable as a list when it should be a string. Investigation needed into:

  • How config crate handles ATTUNE__ENVIRONMENT
  • Whether shell or Rust is doing unexpected parsing
  • Potential workaround: load config directly from file instead of env vars

Lessons Learned

  1. Lib+Bin Structure: Essential for integration testing of binaries
  2. Simplified Constructors: Deriving config from single source reduces test complexity
  3. Test Helpers: Investing in good test infrastructure pays off quickly
  4. Config Management: Environment variable handling can be tricky with config libraries

Next Session Goals

  1. Fix config loading - Primary blocker
  2. Run tests - Verify all 15 tests pass
  3. Add more test coverage - Start with Pack endpoints
  4. Document testing - Add README for running tests

Status: 🔄 IN PROGRESS - Tests running, 50% passing, minor fixes needed Time Invested: ~3 hours Test Coverage: 16 tests written (health + auth), 8 passing, 8 need adjustment Code Quality: All code compiles with zero errors Database: Test database configured and working