feat: 新增 Docker 部署支持、Swoole/Octane 集成及相关优化
- 添加 Dockerfile 与多套 docker-compose 配置(开发/生产环境) - 集成 Laravel Octane (Swoole) 提升性能 - 新增健康检查、监控脚本及部署文档 - 新增 Docker 镜像离线导入包(MySQL/Redis/Meilisearch) - 优化文档转换、预览服务及队列任务 - 添加 CreateAdminUser 命令与路由健康检查接口 - 新增 Swoole 队列兼容性测试套件 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
246
tests/Feature/SwooleQueueCompatibilityTest.php
Normal file
246
tests/Feature/SwooleQueueCompatibilityTest.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Jobs\ConvertDocumentToMarkdown;
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use App\Services\DocumentConversionService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Swoole 队列系统兼容性测试
|
||||
*
|
||||
* 验证现有队列任务在 Swoole 环境下的正常运行
|
||||
*/
|
||||
class SwooleQueueCompatibilityTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// 设置测试存储磁盘
|
||||
Storage::fake('documents');
|
||||
Storage::fake('markdown');
|
||||
|
||||
// 禁用搜索功能以避免 Meilisearch 连接问题
|
||||
config(['scout.driver' => 'null']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试文档转换队列任务可以正常分发
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_document_conversion_job_can_be_dispatched()
|
||||
{
|
||||
// 创建测试用户和文档
|
||||
$user = User::factory()->create();
|
||||
$document = Document::factory()->create([
|
||||
'uploaded_by' => $user->id,
|
||||
'title' => '测试文档',
|
||||
'file_path' => 'test-document.docx',
|
||||
]);
|
||||
|
||||
// 模拟队列
|
||||
Queue::fake();
|
||||
|
||||
// 分发队列任务
|
||||
ConvertDocumentToMarkdown::dispatch($document);
|
||||
|
||||
// 验证任务已被分发
|
||||
Queue::assertPushed(ConvertDocumentToMarkdown::class, function ($job) use ($document) {
|
||||
// 使用反射来访问受保护的属性
|
||||
$reflection = new \ReflectionClass($job);
|
||||
$documentProperty = $reflection->getProperty('document');
|
||||
$documentProperty->setAccessible(true);
|
||||
$jobDocument = $documentProperty->getValue($job);
|
||||
return $jobDocument->id === $document->id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列任务在 Swoole 环境下的执行
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_job_execution_in_swoole_environment()
|
||||
{
|
||||
// 创建测试用户和文档
|
||||
$user = User::factory()->create();
|
||||
$document = Document::factory()->create([
|
||||
'uploaded_by' => $user->id,
|
||||
'title' => '测试文档转换',
|
||||
'file_path' => 'test-conversion.docx',
|
||||
]);
|
||||
|
||||
// 创建模拟的文档文件
|
||||
Storage::disk('documents')->put($document->file_path, 'test content');
|
||||
|
||||
// 模拟转换服务
|
||||
$conversionService = $this->createMock(DocumentConversionService::class);
|
||||
$conversionService->expects($this->once())
|
||||
->method('convertToMarkdown')
|
||||
->with($document)
|
||||
->willReturn([
|
||||
'markdown' => '# 测试文档\n\n这是测试内容',
|
||||
'tempDir' => '/tmp/test',
|
||||
]);
|
||||
|
||||
$conversionService->expects($this->once())
|
||||
->method('saveMarkdownToFile')
|
||||
->willReturn('markdown/test-document.md');
|
||||
|
||||
$conversionService->expects($this->once())
|
||||
->method('updateDocumentMarkdown');
|
||||
|
||||
$this->app->instance(DocumentConversionService::class, $conversionService);
|
||||
|
||||
// 执行队列任务
|
||||
$job = new ConvertDocumentToMarkdown($document);
|
||||
$job->handle($conversionService);
|
||||
|
||||
// 验证任务执行成功(没有抛出异常)
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列任务失败处理
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_job_failure_handling()
|
||||
{
|
||||
// 创建测试用户和文档
|
||||
$user = User::factory()->create();
|
||||
$document = Document::factory()->create([
|
||||
'uploaded_by' => $user->id,
|
||||
'title' => '失败测试文档',
|
||||
'file_path' => 'fail-test.docx',
|
||||
]);
|
||||
|
||||
// 模拟转换服务抛出异常
|
||||
$conversionService = $this->createMock(DocumentConversionService::class);
|
||||
$conversionService->expects($this->once())
|
||||
->method('convertToMarkdown')
|
||||
->willThrowException(new \Exception('转换失败'));
|
||||
|
||||
$this->app->instance(DocumentConversionService::class, $conversionService);
|
||||
|
||||
// 执行队列任务并期望异常
|
||||
$job = new ConvertDocumentToMarkdown($document);
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('转换失败');
|
||||
|
||||
$job->handle($conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列任务的重试机制
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_job_retry_mechanism()
|
||||
{
|
||||
// 创建测试文档
|
||||
$user = User::factory()->create();
|
||||
$document = Document::factory()->create([
|
||||
'uploaded_by' => $user->id,
|
||||
'title' => '重试测试文档',
|
||||
]);
|
||||
|
||||
// 创建队列任务实例
|
||||
$job = new ConvertDocumentToMarkdown($document);
|
||||
|
||||
// 验证重试配置
|
||||
$this->assertEquals(config('documents.conversion.retry_times', 3), $job->tries);
|
||||
$this->assertEquals(config('documents.conversion.timeout', 300), $job->timeout);
|
||||
$this->assertEquals(config('documents.conversion.retry_delay', 60), $job->backoff);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列连接配置
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_connection_configuration()
|
||||
{
|
||||
// 验证队列连接配置
|
||||
$defaultConnection = config('queue.default');
|
||||
$this->assertNotEmpty($defaultConnection);
|
||||
|
||||
// 验证数据库队列连接配置
|
||||
$databaseConfig = config('queue.connections.database');
|
||||
$this->assertIsArray($databaseConfig);
|
||||
$this->assertEquals('database', $databaseConfig['driver']);
|
||||
$this->assertEquals('jobs', $databaseConfig['table']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列在 Swoole 环境下的内存管理
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_memory_management_in_swoole()
|
||||
{
|
||||
// 获取初始内存使用量
|
||||
$initialMemory = memory_get_usage();
|
||||
|
||||
// 创建多个队列任务
|
||||
$user = User::factory()->create();
|
||||
$documents = Document::factory()->count(5)->create(['uploaded_by' => $user->id]);
|
||||
|
||||
Queue::fake();
|
||||
|
||||
// 分发多个任务
|
||||
foreach ($documents as $document) {
|
||||
ConvertDocumentToMarkdown::dispatch($document);
|
||||
}
|
||||
|
||||
// 验证任务都被分发
|
||||
Queue::assertPushed(ConvertDocumentToMarkdown::class, 5);
|
||||
|
||||
// 检查内存使用是否在合理范围内
|
||||
$currentMemory = memory_get_usage();
|
||||
$memoryIncrease = $currentMemory - $initialMemory;
|
||||
|
||||
// 内存增长应该在合理范围内(小于 10MB)
|
||||
$this->assertLessThan(10 * 1024 * 1024, $memoryIncrease, '队列任务内存使用过多');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试队列任务的序列化和反序列化
|
||||
*
|
||||
* @test
|
||||
*/
|
||||
public function test_queue_job_serialization()
|
||||
{
|
||||
// 创建测试文档
|
||||
$user = User::factory()->create();
|
||||
$document = Document::factory()->create(['uploaded_by' => $user->id]);
|
||||
|
||||
// 创建队列任务
|
||||
$job = new ConvertDocumentToMarkdown($document);
|
||||
|
||||
// 序列化任务
|
||||
$serialized = serialize($job);
|
||||
$this->assertIsString($serialized);
|
||||
|
||||
// 反序列化任务
|
||||
$unserialized = unserialize($serialized);
|
||||
$this->assertInstanceOf(ConvertDocumentToMarkdown::class, $unserialized);
|
||||
|
||||
// 使用反射来访问受保护的属性
|
||||
$reflection = new \ReflectionClass($unserialized);
|
||||
$documentProperty = $reflection->getProperty('document');
|
||||
$documentProperty->setAccessible(true);
|
||||
$jobDocument = $documentProperty->getValue($unserialized);
|
||||
$this->assertEquals($document->id, $jobDocument->id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user