Skip to content

Parallel test execution causes intermittent TenantKey unwrap failures #62

@kevalyq

Description

@kevalyq

Problem

When running tests with php artisan test --parallel, intermittent failures occur in PersonTest > repository search is tenant-isolated:

RuntimeException: Failed to unwrap idx_key
at app/Models/TenantKey.php:218

Reproduction

# Fails intermittently (1-10% of runs)
php artisan test --parallel

# Always passes
./vendor/bin/pest

Root Cause

Test isolation issue with parallel execution. Tests that create TenantKey records may interfere with each other when run in parallel, likely due to:

  1. KEK file sharing - All tests use same storage/app/keys/kek.key
  2. Database state - RefreshDatabase may not properly isolate parallel processes
  3. Nonce collision - Multiple parallel tests generating keys simultaneously

Evidence

  • ✅ Test passes when run individually
  • ✅ Test passes when run sequentially (non-parallel)
  • ❌ Test fails intermittently in parallel mode (~1-10% failure rate)
  • Location: tests/Feature/PersonTest.php:354

Impact

  • Pre-push hook fails - Uses php artisan test --parallel
  • CI may fail - If CI uses parallel testing
  • Developer friction - Unclear why tests fail intermittently

Current Workaround

Run tests sequentially for now:

./vendor/bin/pest  # Instead of php artisan test --parallel

Proposed Solutions

Option 1: Per-Process KEK Files (Recommended)

// In TenantKeyTest and PersonTest
beforeEach(function() {
    $processId = getmypid();
    $kekPath = storage_path("app/keys/kek-{$processId}.key");
    // Use process-specific KEK file
});

Option 2: Disable Parallel for TenantKey Tests

// In TenantKeyTest.php
uses(RefreshDatabase::class)->group('serial');

Option 3: Mock KEK Operations

  • Mock TenantKey::generateKek() in tests
  • Avoid filesystem operations in parallel tests

Related

Priority

Medium - Tests work sequentially, but parallel execution is broken. Affects developer workflow and CI reliability.

Labels

  • bug
  • tests
  • good first issue (if we go with Option 2)

Acceptance Criteria

  • Tests pass consistently with php artisan test --parallel (100 runs)
  • Pre-push hook succeeds reliably
  • No test isolation violations
  • Documentation updated if parallel execution is disabled

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions