# Testing

## Setup

- **Framework:** PHPUnit 11.5
- **Config:** `phpunit.xml`
- **Bootstrap:** `tests/bootstrap.php` (PSR-4 autoloader for `Tests\` namespace)
- **Run:** `composer test` → `vendor/bin/phpunit -c phpunit.xml --testdox`
- **Helper:** `dg/bypass-finals` — allows mocking `final` classes

## PHPUnit Configuration

```xml
<phpunit bootstrap="tests/bootstrap.php"
    cacheDirectory="storage/cache/phpunit"
    colors="true"
    executionOrder="depends,defects"
    failOnWarning="true"
    failOnRisky="true">
```

## Test Files (`tests/Unit/`)

| File | Subject | Coverage |
|------|---------|---------|
| `AllegroOrderImportServiceTest.php` | `AllegroOrderImportService` | Import, retry on 401, empty ID guard |
| `AllegroShipmentServiceTest.php` | `AllegroShipmentService` | Shipment creation |
| `AllegroStatusSyncServiceTest.php` | `AllegroStatusSyncService` | Status sync |
| `AllegroTokenManagerTest.php` | `AllegroTokenManager` | Token refresh |
| `ApaczkaShipmentServiceTest.php` | `ApaczkaShipmentService` | Apaczka API calls |
| `AutomationServiceTest.php` | `AutomationService` | Email-once guard, condition evaluation |
| `DeliveryStatusTest.php` | Delivery status mapping | Status translation |

**No repository, controller, or view tests exist** — only service-layer unit tests.

## Test Patterns

### Mock Setup

```php
final class AllegroOrderImportServiceTest extends TestCase {
    private AllegroIntegrationRepository&MockObject $integrationRepository;
    private AllegroApiClient&MockObject $apiClient;

    protected function setUp(): void {
        $this->apiClient = $this->createMock(AllegroApiClient::class);
        $this->service = new AllegroOrderImportService(
            $this->integrationRepository, $this->tokenManager, $this->apiClient, ...
        );
    }
}
```

### Behavior Verification

```php
$this->emailOnceRepository
    ->expects($this->exactly(2))
    ->method('wasSent')
    ->willReturnOnConsecutiveCalls(false, true);

$this->emailService
    ->expects($this->once())
    ->method('send');
```

### Exception Testing

```php
$this->expectException(AllegroApiException::class);
$this->expectExceptionMessage('Podaj ID zamowienia');
$this->service->importSingleOrder('');
```

### Retry Logic Testing

```php
$callCount = 0;
$this->apiClient->method('getCheckoutForm')
    ->willReturnCallback(function () use (&$callCount): array {
        if (++$callCount === 1) {
            throw new RuntimeException('ALLEGRO_HTTP_401');
        }
        return $payload;
    });

$this->service->importSingleOrder($id);
$this->assertSame(2, $callCount); // retry happened
```

### Test Data Builders

```php
private function buildMinimalPayload(string $id): array {
    return [
        'id' => $id,
        'status' => 'READY_FOR_PROCESSING',
        'payment' => ['id' => 'pay-1', 'type' => 'allegro', ...],
        // all required nested fields
    ];
}
```

## What Is Not Tested

- Controllers (no HTTP integration tests)
- Repositories (no DB integration tests — no test database configured)
- Views (no rendering tests)
- Cron handlers
- Migration scripts

## Manual UAT

Phase summaries note manual UAT steps after feature implementation (e.g., Phase 104 — Apaczka weekend delivery tested via UI). No documented UAT scripts or Postman collections.
