# 🧪 KYC Testing Guide

This guide covers how to test the KYC (Know Your Customer) flow in the application, including unit tests, feature tests, and integration tests.

## 📋 Table of Contents

1. [Test Structure](#test-structure)
2. [Running Tests](#running-tests)
3. [Unit Tests](#unit-tests)
4. [Feature Tests](#feature-tests)
5. [Integration Tests](#integration-tests)
6. [Writing Your Own Tests](#writing-your-own-tests)
7. [Test Helpers](#test-helpers)
8. [Common Patterns](#common-patterns)
9. [Troubleshooting](#troubleshooting)

## 🏗️ Test Structure

```
tests/
├── Unit/
│   └── BusinessVerificationServiceTest.php    # Service logic tests
├── Feature/
│   ├── BusinessKycFormTest.php                # Livewire component tests
│   └── KycIntegrationTest.php                 # End-to-end flow tests
├── TestHelpers/
│   └── KycTestHelper.php                      # Reusable test utilities
└── database/factories/
    └── BeneficialOwnerFactory.php             # Test data factory
```

## 🚀 Running Tests

### Run All Tests

```bash
php artisan test
```

### Run Specific Test Types

```bash
# Unit tests only
php artisan test --testsuite=Unit

# Feature tests only
php artisan test --testsuite=Feature

# Specific test file
php artisan test tests/Unit/BusinessVerificationServiceTest.php

# Specific test method
php artisan test --filter=it_creates_correct_payload_for_person_api
```

### Run Tests with Coverage

```bash
php artisan test --coverage
```

## 🔧 Unit Tests

**Purpose**: Test individual components in isolation

**Location**: `tests/Unit/BusinessVerificationServiceTest.php`

### What Unit Tests Cover:

1. **API Payload Generation**

    - Correct field mapping
    - Proper data transformation
    - Required vs optional fields

2. **Country Code Handling**

    - Uses `address_country` for address
    - Uses `id_country` for nationality
    - Proper ISO2 code formatting

3. **Error Handling**

    - API failures
    - Missing response data
    - Network errors

4. **Data Persistence**
    - owner_id saving
    - Status updates
    - Timestamp handling

### Example Unit Test:

```php
/** @test */
public function it_uses_correct_country_fields()
{
    $person = BeneficialOwner::factory()->create([
        'id_country' => 'US',      // Nationality
        'address_country' => 'NG'  // Address
    ]);

    Http::fake([
        'https://api.useoval.com/person' => Http::response(['data' => ['id' => 'test']], 200)
    ]);

    $this->service->createPerson($person);

    Http::assertSent(function ($request) {
        $payload = $request->data();
        return $payload['id_country'] === 'US' &&           // Nationality
               $payload['address']['country'] === 'NG';      // Address
    });
}
```

## 🎯 Feature Tests

**Purpose**: Test Livewire components and user interactions

**Location**: `tests/Feature/BusinessKycFormTest.php`

### What Feature Tests Cover:

1. **Form Rendering**

    - All required fields present
    - Proper labels and validation messages
    - Document upload functionality

2. **Form Validation**

    - Required field validation
    - Format validation (BVN, email, etc.)
    - Custom validation rules

3. **Dynamic Behavior**

    - Country → State → City cascading
    - Document add/remove functionality
    - Real-time validation

4. **Form Submission**
    - Data processing
    - API integration
    - Success/error handling

### Example Feature Test:

```php
/** @test */
public function it_validates_bvn_format()
{
    Livewire::test(BusinessKycForm::class)
        ->set('bankIdNumber', '123')  // Too short
        ->call('createPerson')
        ->assertHasErrors(['bankIdNumber' => 'digits']);
}
```

## 🔄 Integration Tests

**Purpose**: Test complete end-to-end workflows

**Location**: `tests/Feature/KycIntegrationTest.php`

### What Integration Tests Cover:

1. **Complete KYC Flow**

    - Form submission → Database → API → Response → Updates
    - Real user authentication
    - Actual HTTP requests (mocked)

2. **Error Scenarios**

    - API failures
    - Network timeouts
    - Invalid data handling

3. **Multi-Country Support**
    - Different country codes
    - Various validation rules
    - Localization handling

### Example Integration Test:

```php
/** @test */
public function complete_kyc_flow_works_end_to_end()
{
    // 1. Set up user and data
    $user = User::factory()->create();
    $this->actingAs($user);

    // 2. Mock API
    Http::fake([
        'https://api.useoval.com/person' => Http::response([
            'data' => ['id' => 'test_owner_id']
        ], 200)
    ]);

    // 3. Submit form
    Livewire::test(BusinessKycForm::class)
        ->fill($formData)
        ->call('createPerson');

    // 4. Verify results
    $person = BeneficialOwner::where('email', $email)->first();
    $this->assertEquals('test_owner_id', $person->owner_id);
}
```

## ✍️ Writing Your Own Tests

### 1. Setting Up a New Test

```php
<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class MyNewTest extends TestCase
{
    use RefreshDatabase;  // Reset database for each test

    protected function setUp(): void
    {
        parent::setUp();
        // Your setup code here
    }

    /** @test */
    public function it_does_something_important()
    {
        // Arrange - Set up test data
        $user = User::factory()->create();

        // Act - Perform the action
        $result = $user->doSomething();

        // Assert - Check the results
        $this->assertTrue($result);
    }
}
```

### 2. Testing Livewire Components

```php
use Livewire\Livewire;

/** @test */
public function it_handles_form_submission()
{
    Livewire::test(MyComponent::class)
        ->set('field', 'value')           // Set component properties
        ->call('methodName')              // Call component methods
        ->assertSee('Expected Text')      // Check rendered output
        ->assertHasErrors('field')        // Check validation errors
        ->assertEmitted('event-name');    // Check emitted events
}
```

### 3. Mocking HTTP Requests

```php
use Illuminate\Support\Facades\Http;

/** @test */
public function it_calls_external_api()
{
    // Mock the HTTP response
    Http::fake([
        'https://api.example.com/*' => Http::response([
            'data' => ['success' => true]
        ], 200)
    ]);

    // Make your API call
    $result = MyService::callApi();

    // Assert the request was made
    Http::assertSent(function ($request) {
        return $request->url() === 'https://api.example.com/endpoint' &&
               $request['field'] === 'expected_value';
    });
}
```

## 🛠️ Test Helpers

Use the `KycTestHelper` class for common testing operations:

```php
use Tests\TestHelpers\KycTestHelper;

/** @test */
public function my_kyc_test()
{
    // Create test locations
    $locations = KycTestHelper::createTestLocations();

    // Get valid form data
    $formData = KycTestHelper::getValidKycFormData([
        'firstName' => 'Custom Name'
    ]);

    // Mock successful API
    KycTestHelper::mockSuccessfulApiResponse('my_owner_id');

    // Create test person
    $person = KycTestHelper::createTestBeneficialOwner();

    // Assert API call was correct
    KycTestHelper::assertApiCalledWithCorrectPayload([
        'name_first' => 'Custom Name',
        'address.country' => 'NG'
    ]);
}
```

## 📝 Common Testing Patterns

### 1. Testing Validation Rules

```php
/** @test */
public function it_validates_required_fields()
{
    Livewire::test(BusinessKycForm::class)
        ->call('createPerson')
        ->assertHasErrors([
            'firstName' => 'required',
            'lastName' => 'required',
            'bankIdNumber' => 'required'
        ]);
}
```

### 2. Testing API Integration

```php
/** @test */
public function it_handles_api_success()
{
    Http::fake([
        'api.example.com/*' => Http::response(['data' => ['id' => 'test']], 200)
    ]);

    $result = $service->callApi($data);

    $this->assertTrue($result['success']);
    Http::assertSentCount(1);
}
```

### 3. Testing Database Changes

```php
/** @test */
public function it_saves_data_correctly()
{
    $service->processData($inputData);

    $this->assertDatabaseHas('beneficial_owners', [
        'email' => 'test@example.com',
        'kyc_status' => 'submitted'
    ]);
}
```

### 4. Testing File Uploads

```php
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

/** @test */
public function it_handles_file_uploads()
{
    Storage::fake('public');
    $file = UploadedFile::fake()->image('test.jpg');

    Livewire::test(BusinessKycForm::class)
        ->set('documents.0.url', $file)
        ->call('createPerson');

    Storage::disk('public')->assertExists('kyc_documents/' . $file->hashName());
}
```

## 🐛 Troubleshooting

### Common Issues:

1. **Database Not Refreshing**

    ```php
    use Illuminate\Foundation\Testing\RefreshDatabase;

    class MyTest extends TestCase
    {
        use RefreshDatabase;  // Add this trait
    }
    ```

2. **HTTP Mocks Not Working**

    ```php
    // Make sure to clear previous mocks
    Http::clearResolvedInstances();

    // Use specific URLs in mocks
    Http::fake([
        'https://api.useoval.com/person' => Http::response([...])
    ]);
    ```

3. **Livewire Component Not Found**

    ```php
    // Make sure component is properly registered
    Livewire::component('business-kyc-form', BusinessKycForm::class);
    ```

4. **Factory Relationship Issues**
    ```php
    // Create related models first
    $user = User::factory()->create();
    $person = BeneficialOwner::factory()->create(['user_id' => $user->id]);
    ```

### Debug Tips:

1. **Print Test Data**

    ```php
    dump($response->json());  // See API response
    dd($person->toArray());   // See model data
    ```

2. **Check HTTP Requests**

    ```php
    Http::assertSent(function ($request) {
        dump($request->data());  // See what was sent
        return true;
    });
    ```

3. **Validate Database State**
    ```php
    $this->assertDatabaseCount('beneficial_owners', 1);
    $this->assertDatabaseMissing('beneficial_owners', ['email' => 'test@example.com']);
    ```

## 🎯 Best Practices

1. **Test Names Should Be Descriptive**

    ```php
    // Good
    public function it_sends_correct_country_code_for_nigeria()

    // Bad
    public function test_country()
    ```

2. **Use Factories for Test Data**

    ```php
    // Good
    $person = BeneficialOwner::factory()->create(['email' => 'test@example.com']);

    // Bad
    $person = new BeneficialOwner(['email' => 'test@example.com', ...]);
    ```

3. **Mock External Services**

    ```php
    // Always mock HTTP calls
    Http::fake([...]);

    // Don't make real API calls in tests
    ```

4. **Test One Thing at a Time**

    ```php
    // Good - focused test
    public function it_validates_email_format()

    // Bad - testing multiple things
    public function it_validates_form_and_calls_api_and_saves_data()
    ```

5. **Clean Up After Tests**
    ```php
    protected function tearDown(): void
    {
        Http::clearResolvedInstances();
        parent::tearDown();
    }
    ```

---

## 🚀 Quick Start Commands

```bash
# Run all KYC tests
php artisan test --filter=Kyc

# Run with detailed output
php artisan test --verbose

# Run specific test method
php artisan test --filter=it_creates_correct_payload_for_person_api

# Generate test coverage report
php artisan test --coverage-html coverage/
```

Happy Testing! 🎉
