- Introduced multiple new markdown files covering API and routing, application architecture, deployment architecture, database patterns, frontend patterns, and security practices. - Established guidelines for development workflows, testing strategies, and continuous improvement of rules. - Enhanced project overview and technology stack documentation to provide clarity on Coolify's features and architecture.
		
			
				
	
	
		
			607 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			607 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
---
 | 
						|
description: 
 | 
						|
globs: 
 | 
						|
alwaysApply: false
 | 
						|
---
 | 
						|
# Coolify Testing Architecture & Patterns
 | 
						|
 | 
						|
## Testing Philosophy
 | 
						|
 | 
						|
Coolify employs **comprehensive testing strategies** using modern PHP testing frameworks to ensure reliability of deployment operations, infrastructure management, and user interactions.
 | 
						|
 | 
						|
## Testing Framework Stack
 | 
						|
 | 
						|
### Core Testing Tools
 | 
						|
- **Pest PHP 3.8+** - Primary testing framework with expressive syntax
 | 
						|
- **Laravel Dusk** - Browser automation and end-to-end testing
 | 
						|
- **PHPUnit** - Underlying unit testing framework
 | 
						|
- **Mockery** - Mocking and stubbing for isolated tests
 | 
						|
 | 
						|
### Testing Configuration
 | 
						|
- **[tests/Pest.php](mdc:tests/Pest.php)** - Pest configuration and global setup (1.5KB, 45 lines)
 | 
						|
- **[tests/TestCase.php](mdc:tests/TestCase.php)** - Base test case class (163B, 11 lines)
 | 
						|
- **[tests/CreatesApplication.php](mdc:tests/CreatesApplication.php)** - Application factory trait (375B, 22 lines)
 | 
						|
- **[tests/DuskTestCase.php](mdc:tests/DuskTestCase.php)** - Browser testing setup (1.4KB, 58 lines)
 | 
						|
 | 
						|
## Test Directory Structure
 | 
						|
 | 
						|
### Test Organization
 | 
						|
- **[tests/Feature/](mdc:tests/Feature)** - Feature and integration tests
 | 
						|
- **[tests/Unit/](mdc:tests/Unit)** - Unit tests for isolated components
 | 
						|
- **[tests/Browser/](mdc:tests/Browser)** - Laravel Dusk browser tests
 | 
						|
- **[tests/Traits/](mdc:tests/Traits)** - Shared testing utilities
 | 
						|
 | 
						|
## Unit Testing Patterns
 | 
						|
 | 
						|
### Model Testing
 | 
						|
```php
 | 
						|
// Testing Eloquent models
 | 
						|
test('application model has correct relationships', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    expect($application->server)->toBeInstanceOf(Server::class);
 | 
						|
    expect($application->environment)->toBeInstanceOf(Environment::class);
 | 
						|
    expect($application->deployments)->toBeInstanceOf(Collection::class);
 | 
						|
});
 | 
						|
 | 
						|
test('application can generate deployment configuration', function () {
 | 
						|
    $application = Application::factory()->create([
 | 
						|
        'name' => 'test-app',
 | 
						|
        'git_repository' => 'https://github.com/user/repo.git'
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $config = $application->generateDockerCompose();
 | 
						|
    
 | 
						|
    expect($config)->toContain('test-app');
 | 
						|
    expect($config)->toContain('image:');
 | 
						|
    expect($config)->toContain('networks:');
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Service Layer Testing
 | 
						|
```php
 | 
						|
// Testing service classes
 | 
						|
test('configuration generator creates valid docker compose', function () {
 | 
						|
    $generator = new ConfigurationGenerator();
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    $compose = $generator->generateDockerCompose($application);
 | 
						|
    
 | 
						|
    expect($compose)->toBeString();
 | 
						|
    expect(yaml_parse($compose))->toBeArray();
 | 
						|
    expect($compose)->toContain('version: "3.8"');
 | 
						|
});
 | 
						|
 | 
						|
test('docker image parser validates image names', function () {
 | 
						|
    $parser = new DockerImageParser();
 | 
						|
    
 | 
						|
    expect($parser->isValid('nginx:latest'))->toBeTrue();
 | 
						|
    expect($parser->isValid('invalid-image-name'))->toBeFalse();
 | 
						|
    expect($parser->parse('nginx:1.21'))->toEqual([
 | 
						|
        'registry' => 'docker.io',
 | 
						|
        'namespace' => 'library',
 | 
						|
        'repository' => 'nginx',
 | 
						|
        'tag' => '1.21'
 | 
						|
    ]);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Action Testing
 | 
						|
```php
 | 
						|
// Testing Laravel Actions
 | 
						|
test('deploy application action creates deployment queue', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    $action = new DeployApplicationAction();
 | 
						|
    
 | 
						|
    $deployment = $action->handle($application);
 | 
						|
    
 | 
						|
    expect($deployment)->toBeInstanceOf(ApplicationDeploymentQueue::class);
 | 
						|
    expect($deployment->status)->toBe('queued');
 | 
						|
    expect($deployment->application_id)->toBe($application->id);
 | 
						|
});
 | 
						|
 | 
						|
test('server validation action checks ssh connectivity', function () {
 | 
						|
    $server = Server::factory()->create([
 | 
						|
        'ip' => '192.168.1.100',
 | 
						|
        'port' => 22
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $action = new ValidateServerAction();
 | 
						|
    
 | 
						|
    // Mock SSH connection
 | 
						|
    $this->mock(SshConnection::class, function ($mock) {
 | 
						|
        $mock->shouldReceive('connect')->andReturn(true);
 | 
						|
        $mock->shouldReceive('execute')->with('docker --version')->andReturn('Docker version 20.10.0');
 | 
						|
    });
 | 
						|
    
 | 
						|
    $result = $action->handle($server);
 | 
						|
    
 | 
						|
    expect($result['ssh_connection'])->toBeTrue();
 | 
						|
    expect($result['docker_installed'])->toBeTrue();
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Feature Testing Patterns
 | 
						|
 | 
						|
### API Testing
 | 
						|
```php
 | 
						|
// Testing API endpoints
 | 
						|
test('authenticated user can list applications', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $team = Team::factory()->create();
 | 
						|
    $user->teams()->attach($team);
 | 
						|
    
 | 
						|
    $applications = Application::factory(3)->create([
 | 
						|
        'team_id' => $team->id
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user)
 | 
						|
        ->getJson('/api/v1/applications');
 | 
						|
    
 | 
						|
    $response->assertStatus(200)
 | 
						|
        ->assertJsonCount(3, 'data')
 | 
						|
        ->assertJsonStructure([
 | 
						|
            'data' => [
 | 
						|
                '*' => ['id', 'name', 'fqdn', 'status', 'created_at']
 | 
						|
            ]
 | 
						|
        ]);
 | 
						|
});
 | 
						|
 | 
						|
test('user cannot access applications from other teams', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $otherTeam = Team::factory()->create();
 | 
						|
    
 | 
						|
    $application = Application::factory()->create([
 | 
						|
        'team_id' => $otherTeam->id
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user)
 | 
						|
        ->getJson("/api/v1/applications/{$application->id}");
 | 
						|
    
 | 
						|
    $response->assertStatus(403);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Deployment Testing
 | 
						|
```php
 | 
						|
// Testing deployment workflows
 | 
						|
test('application deployment creates docker containers', function () {
 | 
						|
    $application = Application::factory()->create([
 | 
						|
        'git_repository' => 'https://github.com/laravel/laravel.git',
 | 
						|
        'git_branch' => 'main'
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    // Mock Docker operations
 | 
						|
    $this->mock(DockerService::class, function ($mock) {
 | 
						|
        $mock->shouldReceive('buildImage')->andReturn('app:latest');
 | 
						|
        $mock->shouldReceive('createContainer')->andReturn('container_id');
 | 
						|
        $mock->shouldReceive('startContainer')->andReturn(true);
 | 
						|
    });
 | 
						|
    
 | 
						|
    $deployment = $application->deploy();
 | 
						|
    
 | 
						|
    expect($deployment->status)->toBe('queued');
 | 
						|
    
 | 
						|
    // Process the deployment job
 | 
						|
    $this->artisan('queue:work --once');
 | 
						|
    
 | 
						|
    $deployment->refresh();
 | 
						|
    expect($deployment->status)->toBe('success');
 | 
						|
});
 | 
						|
 | 
						|
test('failed deployment triggers rollback', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    // Mock failed deployment
 | 
						|
    $this->mock(DockerService::class, function ($mock) {
 | 
						|
        $mock->shouldReceive('buildImage')->andThrow(new DeploymentException('Build failed'));
 | 
						|
    });
 | 
						|
    
 | 
						|
    $deployment = $application->deploy();
 | 
						|
    
 | 
						|
    $this->artisan('queue:work --once');
 | 
						|
    
 | 
						|
    $deployment->refresh();
 | 
						|
    expect($deployment->status)->toBe('failed');
 | 
						|
    expect($deployment->error_message)->toContain('Build failed');
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Webhook Testing
 | 
						|
```php
 | 
						|
// Testing webhook endpoints
 | 
						|
test('github webhook triggers deployment', function () {
 | 
						|
    $application = Application::factory()->create([
 | 
						|
        'git_repository' => 'https://github.com/user/repo.git',
 | 
						|
        'git_branch' => 'main'
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $payload = [
 | 
						|
        'ref' => 'refs/heads/main',
 | 
						|
        'repository' => [
 | 
						|
            'clone_url' => 'https://github.com/user/repo.git'
 | 
						|
        ],
 | 
						|
        'head_commit' => [
 | 
						|
            'id' => 'abc123',
 | 
						|
            'message' => 'Update application'
 | 
						|
        ]
 | 
						|
    ];
 | 
						|
    
 | 
						|
    $response = $this->postJson("/webhooks/github/{$application->id}", $payload);
 | 
						|
    
 | 
						|
    $response->assertStatus(200);
 | 
						|
    
 | 
						|
    expect($application->deployments()->count())->toBe(1);
 | 
						|
    expect($application->deployments()->first()->commit_sha)->toBe('abc123');
 | 
						|
});
 | 
						|
 | 
						|
test('webhook validates payload signature', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    $payload = ['invalid' => 'payload'];
 | 
						|
    
 | 
						|
    $response = $this->postJson("/webhooks/github/{$application->id}", $payload);
 | 
						|
    
 | 
						|
    $response->assertStatus(400);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Browser Testing (Laravel Dusk)
 | 
						|
 | 
						|
### End-to-End Testing
 | 
						|
```php
 | 
						|
// Testing complete user workflows
 | 
						|
test('user can create and deploy application', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
 | 
						|
    
 | 
						|
    $this->browse(function (Browser $browser) use ($user, $server) {
 | 
						|
        $browser->loginAs($user)
 | 
						|
            ->visit('/applications/create')
 | 
						|
            ->type('name', 'Test Application')
 | 
						|
            ->type('git_repository', 'https://github.com/laravel/laravel.git')
 | 
						|
            ->type('git_branch', 'main')
 | 
						|
            ->select('server_id', $server->id)
 | 
						|
            ->press('Create Application')
 | 
						|
            ->assertPathIs('/applications/*')
 | 
						|
            ->assertSee('Test Application')
 | 
						|
            ->press('Deploy')
 | 
						|
            ->waitForText('Deployment started', 10)
 | 
						|
            ->assertSee('Deployment started');
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
test('user can monitor deployment logs in real-time', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $application = Application::factory()->create(['team_id' => $user->currentTeam->id]);
 | 
						|
    
 | 
						|
    $this->browse(function (Browser $browser) use ($user, $application) {
 | 
						|
        $browser->loginAs($user)
 | 
						|
            ->visit("/applications/{$application->id}")
 | 
						|
            ->press('Deploy')
 | 
						|
            ->waitForText('Deployment started')
 | 
						|
            ->click('@logs-tab')
 | 
						|
            ->waitFor('@deployment-logs')
 | 
						|
            ->assertSee('Building Docker image')
 | 
						|
            ->waitForText('Deployment completed', 30);
 | 
						|
    });
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### UI Component Testing
 | 
						|
```php
 | 
						|
// Testing Livewire components
 | 
						|
test('server status component updates in real-time', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $server = Server::factory()->create(['team_id' => $user->currentTeam->id]);
 | 
						|
    
 | 
						|
    $this->browse(function (Browser $browser) use ($user, $server) {
 | 
						|
        $browser->loginAs($user)
 | 
						|
            ->visit("/servers/{$server->id}")
 | 
						|
            ->assertSee('Status: Online')
 | 
						|
            ->waitFor('@server-metrics')
 | 
						|
            ->assertSee('CPU Usage')
 | 
						|
            ->assertSee('Memory Usage')
 | 
						|
            ->assertSee('Disk Usage');
 | 
						|
        
 | 
						|
        // Simulate server going offline
 | 
						|
        $server->update(['status' => 'offline']);
 | 
						|
        
 | 
						|
        $browser->waitForText('Status: Offline', 5)
 | 
						|
            ->assertSee('Status: Offline');
 | 
						|
    });
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Database Testing Patterns
 | 
						|
 | 
						|
### Migration Testing
 | 
						|
```php
 | 
						|
// Testing database migrations
 | 
						|
test('applications table has correct structure', function () {
 | 
						|
    expect(Schema::hasTable('applications'))->toBeTrue();
 | 
						|
    expect(Schema::hasColumns('applications', [
 | 
						|
        'id', 'name', 'fqdn', 'git_repository', 'git_branch',
 | 
						|
        'server_id', 'environment_id', 'created_at', 'updated_at'
 | 
						|
    ]))->toBeTrue();
 | 
						|
});
 | 
						|
 | 
						|
test('foreign key constraints are properly set', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    expect($application->server)->toBeInstanceOf(Server::class);
 | 
						|
    expect($application->environment)->toBeInstanceOf(Environment::class);
 | 
						|
    
 | 
						|
    // Test cascade deletion
 | 
						|
    $application->server->delete();
 | 
						|
    expect(Application::find($application->id))->toBeNull();
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Factory Testing
 | 
						|
```php
 | 
						|
// Testing model factories
 | 
						|
test('application factory creates valid models', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    
 | 
						|
    expect($application->name)->toBeString();
 | 
						|
    expect($application->git_repository)->toStartWith('https://');
 | 
						|
    expect($application->server_id)->toBeInt();
 | 
						|
    expect($application->environment_id)->toBeInt();
 | 
						|
});
 | 
						|
 | 
						|
test('application factory can create with custom attributes', function () {
 | 
						|
    $application = Application::factory()->create([
 | 
						|
        'name' => 'Custom App',
 | 
						|
        'git_branch' => 'develop'
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    expect($application->name)->toBe('Custom App');
 | 
						|
    expect($application->git_branch)->toBe('develop');
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Queue Testing
 | 
						|
 | 
						|
### Job Testing
 | 
						|
```php
 | 
						|
// Testing background jobs
 | 
						|
test('deploy application job processes successfully', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    $deployment = ApplicationDeploymentQueue::factory()->create([
 | 
						|
        'application_id' => $application->id,
 | 
						|
        'status' => 'queued'
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $job = new DeployApplicationJob($deployment);
 | 
						|
    
 | 
						|
    // Mock external dependencies
 | 
						|
    $this->mock(DockerService::class, function ($mock) {
 | 
						|
        $mock->shouldReceive('buildImage')->andReturn('app:latest');
 | 
						|
        $mock->shouldReceive('deployContainer')->andReturn(true);
 | 
						|
    });
 | 
						|
    
 | 
						|
    $job->handle();
 | 
						|
    
 | 
						|
    $deployment->refresh();
 | 
						|
    expect($deployment->status)->toBe('success');
 | 
						|
});
 | 
						|
 | 
						|
test('failed job is retried with exponential backoff', function () {
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    $deployment = ApplicationDeploymentQueue::factory()->create([
 | 
						|
        'application_id' => $application->id
 | 
						|
    ]);
 | 
						|
    
 | 
						|
    $job = new DeployApplicationJob($deployment);
 | 
						|
    
 | 
						|
    // Mock failure
 | 
						|
    $this->mock(DockerService::class, function ($mock) {
 | 
						|
        $mock->shouldReceive('buildImage')->andThrow(new Exception('Network error'));
 | 
						|
    });
 | 
						|
    
 | 
						|
    expect(fn() => $job->handle())->toThrow(Exception::class);
 | 
						|
    
 | 
						|
    // Job should be retried
 | 
						|
    expect($job->tries)->toBe(3);
 | 
						|
    expect($job->backoff())->toBe([1, 5, 10]);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Security Testing
 | 
						|
 | 
						|
### Authentication Testing
 | 
						|
```php
 | 
						|
// Testing authentication and authorization
 | 
						|
test('unauthenticated users cannot access protected routes', function () {
 | 
						|
    $response = $this->get('/dashboard');
 | 
						|
    $response->assertRedirect('/login');
 | 
						|
});
 | 
						|
 | 
						|
test('users can only access their team resources', function () {
 | 
						|
    $user1 = User::factory()->create();
 | 
						|
    $user2 = User::factory()->create();
 | 
						|
    
 | 
						|
    $team1 = Team::factory()->create();
 | 
						|
    $team2 = Team::factory()->create();
 | 
						|
    
 | 
						|
    $user1->teams()->attach($team1);
 | 
						|
    $user2->teams()->attach($team2);
 | 
						|
    
 | 
						|
    $application = Application::factory()->create(['team_id' => $team1->id]);
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user2)
 | 
						|
        ->get("/applications/{$application->id}");
 | 
						|
    
 | 
						|
    $response->assertStatus(403);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Input Validation Testing
 | 
						|
```php
 | 
						|
// Testing input validation and sanitization
 | 
						|
test('application creation validates required fields', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user)
 | 
						|
        ->postJson('/api/v1/applications', []);
 | 
						|
    
 | 
						|
    $response->assertStatus(422)
 | 
						|
        ->assertJsonValidationErrors(['name', 'git_repository', 'server_id']);
 | 
						|
});
 | 
						|
 | 
						|
test('malicious input is properly sanitized', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user)
 | 
						|
        ->postJson('/api/v1/applications', [
 | 
						|
            'name' => '<script>alert("xss")</script>',
 | 
						|
            'git_repository' => 'javascript:alert("xss")',
 | 
						|
            'server_id' => 'invalid'
 | 
						|
        ]);
 | 
						|
    
 | 
						|
    $response->assertStatus(422);
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Performance Testing
 | 
						|
 | 
						|
### Load Testing
 | 
						|
```php
 | 
						|
// Testing application performance under load
 | 
						|
test('application list endpoint handles concurrent requests', function () {
 | 
						|
    $user = User::factory()->create();
 | 
						|
    $applications = Application::factory(100)->create(['team_id' => $user->currentTeam->id]);
 | 
						|
    
 | 
						|
    $startTime = microtime(true);
 | 
						|
    
 | 
						|
    $response = $this->actingAs($user)
 | 
						|
        ->getJson('/api/v1/applications');
 | 
						|
    
 | 
						|
    $endTime = microtime(true);
 | 
						|
    $responseTime = ($endTime - $startTime) * 1000; // Convert to milliseconds
 | 
						|
    
 | 
						|
    $response->assertStatus(200);
 | 
						|
    expect($responseTime)->toBeLessThan(500); // Should respond within 500ms
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Memory Usage Testing
 | 
						|
```php
 | 
						|
// Testing memory efficiency
 | 
						|
test('deployment process does not exceed memory limits', function () {
 | 
						|
    $initialMemory = memory_get_usage();
 | 
						|
    
 | 
						|
    $application = Application::factory()->create();
 | 
						|
    $deployment = $application->deploy();
 | 
						|
    
 | 
						|
    // Process deployment
 | 
						|
    $this->artisan('queue:work --once');
 | 
						|
    
 | 
						|
    $finalMemory = memory_get_usage();
 | 
						|
    $memoryIncrease = $finalMemory - $initialMemory;
 | 
						|
    
 | 
						|
    expect($memoryIncrease)->toBeLessThan(50 * 1024 * 1024); // Less than 50MB
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
## Test Utilities and Helpers
 | 
						|
 | 
						|
### Custom Assertions
 | 
						|
```php
 | 
						|
// Custom test assertions
 | 
						|
expect()->extend('toBeValidDockerCompose', function () {
 | 
						|
    $yaml = yaml_parse($this->value);
 | 
						|
    
 | 
						|
    return $yaml !== false &&
 | 
						|
           isset($yaml['version']) &&
 | 
						|
           isset($yaml['services']) &&
 | 
						|
           is_array($yaml['services']);
 | 
						|
});
 | 
						|
 | 
						|
expect()->extend('toHaveValidSshConnection', function () {
 | 
						|
    $server = $this->value;
 | 
						|
    
 | 
						|
    try {
 | 
						|
        $connection = new SshConnection($server);
 | 
						|
        return $connection->test();
 | 
						|
    } catch (Exception $e) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
});
 | 
						|
```
 | 
						|
 | 
						|
### Test Traits
 | 
						|
```php
 | 
						|
// Shared testing functionality
 | 
						|
trait CreatesTestServers
 | 
						|
{
 | 
						|
    protected function createTestServer(array $attributes = []): Server
 | 
						|
    {
 | 
						|
        return Server::factory()->create(array_merge([
 | 
						|
            'name' => 'Test Server',
 | 
						|
            'ip' => '127.0.0.1',
 | 
						|
            'port' => 22,
 | 
						|
            'team_id' => $this->user->currentTeam->id
 | 
						|
        ], $attributes));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
trait MocksDockerOperations
 | 
						|
{
 | 
						|
    protected function mockDockerService(): void
 | 
						|
    {
 | 
						|
        $this->mock(DockerService::class, function ($mock) {
 | 
						|
            $mock->shouldReceive('buildImage')->andReturn('test:latest');
 | 
						|
            $mock->shouldReceive('createContainer')->andReturn('container_123');
 | 
						|
            $mock->shouldReceive('startContainer')->andReturn(true);
 | 
						|
            $mock->shouldReceive('stopContainer')->andReturn(true);
 | 
						|
        });
 | 
						|
    }
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
## Continuous Integration Testing
 | 
						|
 | 
						|
### GitHub Actions Integration
 | 
						|
```yaml
 | 
						|
# .github/workflows/tests.yml
 | 
						|
name: Tests
 | 
						|
on: [push, pull_request]
 | 
						|
jobs:
 | 
						|
  test:
 | 
						|
    runs-on: ubuntu-latest
 | 
						|
    services:
 | 
						|
      postgres:
 | 
						|
        image: postgres:15
 | 
						|
        env:
 | 
						|
          POSTGRES_PASSWORD: password
 | 
						|
        options: >-
 | 
						|
          --health-cmd pg_isready
 | 
						|
          --health-interval 10s
 | 
						|
          --health-timeout 5s
 | 
						|
          --health-retries 5
 | 
						|
    steps:
 | 
						|
      - uses: actions/checkout@v3
 | 
						|
      - 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/pest
 | 
						|
```
 | 
						|
 | 
						|
### Test Coverage
 | 
						|
```php
 | 
						|
// Generate test coverage reports
 | 
						|
test('application has adequate test coverage', function () {
 | 
						|
    $coverage = $this->getCoverageData();
 | 
						|
    
 | 
						|
    expect($coverage['application'])->toBeGreaterThan(80);
 | 
						|
    expect($coverage['models'])->toBeGreaterThan(90);
 | 
						|
    expect($coverage['actions'])->toBeGreaterThan(85);
 | 
						|
});
 | 
						|
```
 |