# Testing Guide

## Overview

This document describes the automated testing infrastructure for the M1 ERP application. The test suite uses PHPUnit 11.5 and provides comprehensive coverage of security features, business logic, and critical functionality.

## Test Statistics

- **Total Tests**: 77
- **Test Suites**: 4 (Authentication, Business Logic, CSRF Protection, File Upload Validation)
- **Coverage Areas**: Security, Authentication, Business Calculations, Input Validation

## Running Tests

### Run All Tests

```bash
./vendor/bin/phpunit
```

### Run Tests with Detailed Output

```bash
./vendor/bin/phpunit --testdox
```

### Run Specific Test Suite

```bash
# Run only unit tests
./vendor/bin/phpunit tests/Unit

# Run specific test file
./vendor/bin/phpunit tests/Unit/CsrfProtectionTest.php

# Run specific test method
./vendor/bin/phpunit --filter testCsrfTokenGeneration
```

### Run Tests with Coverage (requires Xdebug)

```bash
./vendor/bin/phpunit --coverage-html coverage
```

## Test Structure

```
tests/
├── bootstrap.php           # Test environment setup
├── TestCase.php           # Base test class with helper methods
├── Unit/                  # Unit tests
│   ├── AuthenticationTest.php
│   ├── BusinessLogicTest.php
│   ├── CsrfProtectionTest.php
│   └── FileUploadValidationTest.php
└── Feature/               # Feature tests (future)
```

## Test Suites

### 1. Authentication Tests (18 tests)

Tests user authentication, authorization, and session management.

**Coverage:**
- User authentication flow
- Session management (login, logout, regeneration)
- Permission checking
- Password hashing security
- Session timeout
- Failed login attempts tracking
- Remember me functionality
- Session fixation prevention
- Role-based access control

**Example:**
```php
public function testAuthCheckReturnsTrueForAuthenticatedUser(): void
{
    $this->actingAs(1, 'testuser');
    $this->assertTrue($this->auth->check());
}
```

### 2. CSRF Protection Tests (20 tests)

Tests CSRF token generation, validation, and helper functions.

**Coverage:**
- Token generation and format
- Token validation (success and failure)
- Helper functions (csrf_token(), csrf_field(), csrf_validate())
- Edge cases (null, numeric, array inputs)
- Token regeneration
- Timing-safe comparison

**Example:**
```php
public function testCsrfTokenGeneration(): void
{
    $token = \Session::generateCsrfToken();
    
    $this->assertIsString($token);
    $this->assertEquals(64, strlen($token));
}
```

### 3. File Upload Validation Tests (18 tests)

Tests file upload security and validation.

**Coverage:**
- Valid file uploads (PDF, CSV, images)
- File size validation
- MIME type validation
- Upload error handling
- Filename sanitization
- Path traversal prevention
- CSV-specific validation

**Example:**
```php
public function testValidateFileUploadWithValidPdf(): void
{
    $tmpFile = $this->createTempFile('%PDF-1.4', 'pdf');
    
    $file = [
        'name' => 'document.pdf',
        'type' => 'application/pdf',
        'tmp_name' => $tmpFile,
        'error' => UPLOAD_ERR_OK,
        'size' => filesize($tmpFile),
    ];
    
    $result = validate_file_upload($file, 'document');
    
    $this->assertTrue($result['valid']);
}
```

### 4. Business Logic Tests (21 tests)

Tests critical business calculations and workflows.

**Coverage:**
- Invoice calculations (subtotal, tax, discount, total)
- Stock movement calculations
- Journal entry number generation
- Balance sheet calculations
- Percentage and rounding calculations
- Burn rate calculations

**Example:**
```php
public function testInvoiceTotalCalculation(): void
{
    $subtotal = 1000.00;
    $taxRate = 10.0;
    $discountAmount = 50.00;
    
    $taxAmount = $subtotal * ($taxRate / 100);
    $total = $subtotal + $taxAmount - $discountAmount;
    
    $this->assertEquals(1050.00, $total);
}
```

## Writing Tests

### Creating a New Test File

1. Create a new file in `tests/Unit/` or `tests/Feature/`
2. Extend the `Tests\TestCase` base class
3. Use the `test` prefix for test methods

```php
<?php

namespace Tests\Unit;

use Tests\TestCase;

class MyFeatureTest extends TestCase
{
    public function testSomething(): void
    {
        $this->assertTrue(true);
    }
}
```

### Using Helper Methods

The `TestCase` base class provides helpful methods:

#### Authentication Helpers

```php
// Create a mock authenticated user
$this->actingAs(userId: 1, username: 'testuser', permissions: ['view_dashboard']);

// Get CSRF token
$token = $this->getCsrfToken();
```

#### File Upload Helpers

```php
// Create a temporary test file
$tmpFile = $this->createTempFile('content', 'txt');

// Mock file upload
$this->mockFileUpload('file', 'test.pdf', 'application/pdf', 1024);

// Clean up temp file
$this->cleanupTempFile($tmpFile);
```

#### Database Helpers

```php
// Assert database has record
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);

// Assert database missing record
$this->assertDatabaseMissing('users', ['email' => 'deleted@example.com']);

// Execute raw query
$this->executeQuery("INSERT INTO users (email) VALUES (?)", ['test@example.com']);

// Fetch records
$user = $this->fetchOne("SELECT * FROM users WHERE id = ?", [1]);
$users = $this->fetchAll("SELECT * FROM users");

// Get last insert ID
$id = $this->getLastInsertId();
```

### Database Transactions

All tests automatically run in database transactions that are rolled back after each test. This ensures:
- Tests don't pollute the database
- Tests are isolated from each other
- Tests can be run multiple times

To disable transactions for a specific test:

```php
class MyTest extends TestCase
{
    protected $useTransactions = false;
    
    // ...
}
```

## Best Practices

### 1. Test Naming

Use descriptive test names that explain what is being tested:

```php
// Good
public function testUserCanLoginWithValidCredentials(): void

// Bad
public function testLogin(): void
```

### 2. Arrange-Act-Assert Pattern

Structure tests clearly:

```php
public function testInvoiceCalculation(): void
{
    // Arrange
    $subtotal = 1000.00;
    $taxRate = 10.0;
    
    // Act
    $total = $subtotal + ($subtotal * $taxRate / 100);
    
    // Assert
    $this->assertEquals(1100.00, $total);
}
```

### 3. Test One Thing

Each test should verify one specific behavior:

```php
// Good - tests one thing
public function testCsrfTokenIsString(): void
{
    $token = \Session::generateCsrfToken();
    $this->assertIsString($token);
}

public function testCsrfTokenHasCorrectLength(): void
{
    $token = \Session::generateCsrfToken();
    $this->assertEquals(64, strlen($token));
}

// Bad - tests multiple things
public function testCsrfToken(): void
{
    $token = \Session::generateCsrfToken();
    $this->assertIsString($token);
    $this->assertEquals(64, strlen($token));
    $this->assertTrue(\Session::validateCsrfToken($token));
}
```

### 4. Use Data Providers for Similar Tests

```php
/**
 * @dataProvider invalidEmailProvider
 */
public function testInvalidEmailValidation(string $email): void
{
    $this->assertFalse(filter_var($email, FILTER_VALIDATE_EMAIL));
}

public function invalidEmailProvider(): array
{
    return [
        ['invalid'],
        ['@example.com'],
        ['user@'],
        ['user @example.com'],
    ];
}
```

## Continuous Integration

### GitHub Actions Example

```yaml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v2
      
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          
      - name: Install Dependencies
        run: composer install
        
      - name: Run Tests
        run: ./vendor/bin/phpunit
```

## Troubleshooting

### Tests Failing Due to Database Issues

1. Check database connection in config
2. Ensure test database exists
3. Run migrations if needed

### Session-Related Test Failures

1. Clear session before test: `$_SESSION = [];`
2. Use `actingAs()` helper for authenticated tests
3. Check session keys match application expectations

### File Upload Test Failures

1. Ensure temp directory is writable
2. Clean up temp files after tests
3. Use `createTempFile()` helper for consistent file creation

## Future Improvements

- [ ] Add feature tests for complete user workflows
- [ ] Increase code coverage to 80%+
- [ ] Add integration tests for external services
- [ ] Add performance/load tests
- [ ] Set up automated CI/CD pipeline
- [ ] Add mutation testing

## Resources

- [PHPUnit Documentation](https://phpunit.de/documentation.html)
- [PHPUnit Best Practices](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)
- [Test-Driven Development](https://en.wikipedia.org/wiki/Test-driven_development)

