From f4ca8372c0c459ca5080956802f4f402796dda14 Mon Sep 17 00:00:00 2001 From: lizhuoran <625237490@qq.com> Date: Mon, 9 Mar 2026 13:25:17 +0800 Subject: [PATCH] =?UTF-8?q?test(=E9=98=B6=E6=AE=B5=E5=9B=9B):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20SOP=20=E6=A8=A1=E6=9D=BF=E5=8A=9F=E8=83=BD=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 SopTemplateTest 测试文件(13个测试) - 测试模板 CRUD 操作 - 测试步骤管理和排序 - 测试交互任务关联 - 测试状态转换 - 测试版本快照 - 测试活动日志 - 测试筛选和软删除 - 创建 SopTemplateServiceTest 测试文件(12个测试) - 测试 JSON 导出导入 - 测试发布和归档 - 测试版本管理 - 测试模板复制 - 测试数据验证 所有 25 个测试通过 --- tests/Feature/SopTemplateServiceTest.php | 246 +++++++++++++++++++++++ tests/Feature/SopTemplateTest.php | 200 ++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 tests/Feature/SopTemplateServiceTest.php create mode 100644 tests/Feature/SopTemplateTest.php diff --git a/tests/Feature/SopTemplateServiceTest.php b/tests/Feature/SopTemplateServiceTest.php new file mode 100644 index 0000000..9725a1f --- /dev/null +++ b/tests/Feature/SopTemplateServiceTest.php @@ -0,0 +1,246 @@ +user = User::factory()->create(); + $this->actingAs($this->user); + $this->service = new SopTemplateService(); +}); + +test('可以导出模板为 JSON', function () { + $template = SopTemplate::factory()->create([ + 'name' => '测试模板', + 'version' => '1.0.0', + ]); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'content' => '内容', + 'sort_order' => 1, + ]); + + $json = $this->service->exportToJson($template); + $data = json_decode($json, true); + + expect($data)->toHaveKey('template') + ->and($data)->toHaveKey('steps') + ->and($data['template']['name'])->toBe('测试模板') + ->and($data['steps'])->toHaveCount(1); +}); + +test('导出的 JSON 包含步骤信息', function () { + $template = SopTemplate::factory()->create(); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'content' => '
这是内容
', + 'sort_order' => 1, + 'is_required' => true, + ]); + + $json = $this->service->exportToJson($template); + $data = json_decode($json, true); + + expect($data['steps'][0])->toHaveKey('step_number') + ->and($data['steps'][0])->toHaveKey('title') + ->and($data['steps'][0])->toHaveKey('content') + ->and($data['steps'][0]['title'])->toBe('第一步'); +}); + +test('可以从 JSON 导入模板', function () { + $jsonData = [ + 'template' => [ + 'name' => '导入的模板', + 'description' => '这是描述', + 'category' => '测试分类', + 'version' => '1.0.0', + ], + 'steps' => [ + [ + 'step_number' => 1, + 'title' => '第一步', + 'content' => '内容', + 'sort_order' => 1, + 'is_required' => true, + ], + ], + ]; + + $json = json_encode($jsonData); + $template = $this->service->importFromJson($json); + + expect($template)->toBeInstanceOf(SopTemplate::class) + ->and($template->name)->toBe('导入的模板') + ->and($template->status)->toBe('draft') + ->and($template->steps)->toHaveCount(1); +}); + +test('导入时会创建步骤', function () { + $jsonData = [ + 'template' => [ + 'name' => '测试模板', + 'version' => '1.0.0', + ], + 'steps' => [ + [ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ], + [ + 'step_number' => 2, + 'title' => '第二步', + 'sort_order' => 2, + ], + ], + ]; + + $json = json_encode($jsonData); + $template = $this->service->importFromJson($json); + + expect($template->steps)->toHaveCount(2) + ->and($template->steps->first()->title)->toBe('第一步') + ->and($template->steps->last()->title)->toBe('第二步'); +}); + +test('导入无效 JSON 会抛出异常', function () { + $invalidJson = 'invalid json'; + + $this->service->importFromJson($invalidJson); +})->throws(\Illuminate\Validation\ValidationException::class); + +test('导入缺少必需字段会抛出异常', function () { + $jsonData = [ + 'template' => [ + 'name' => '测试模板', + // 缺少 version + ], + 'steps' => [], + ]; + + $json = json_encode($jsonData); + + $this->service->importFromJson($json); +})->throws(\Illuminate\Validation\ValidationException::class); + +test('可以发布模板', function () { + $template = SopTemplate::factory()->create([ + 'status' => 'draft', + ]); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $this->service->publish($template, '首次发布'); + + expect($template->fresh()->status)->toBe('published') + ->and($template->fresh()->published_at)->not->toBeNull() + ->and($template->versions)->toHaveCount(1); +}); + +test('发布时会创建版本快照', function () { + $template = SopTemplate::factory()->create([ + 'status' => 'draft', + 'version' => '1.0.0', + ]); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $this->service->publish($template, '测试发布'); + + $version = $template->versions->first(); + + expect($version)->not->toBeNull() + ->and($version->version)->toBe('1.0.0') + ->and($version->change_log)->toBe('测试发布') + ->and($version->content_snapshot)->toHaveKey('template') + ->and($version->content_snapshot)->toHaveKey('steps'); +}); + +test('可以归档模板', function () { + $template = SopTemplate::factory()->create([ + 'status' => 'published', + ]); + + $this->service->archive($template); + + expect($template->fresh()->status)->toBe('archived'); +}); + +test('可以复制模板', function () { + $template = SopTemplate::factory()->create([ + 'name' => '原始模板', + ]); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $newTemplate = $this->service->duplicate($template, '复制的模板'); + + expect($newTemplate->name)->toBe('复制的模板') + ->and($newTemplate->status)->toBe('draft') + ->and($newTemplate->steps)->toHaveCount(1) + ->and($newTemplate->id)->not->toBe($template->id); +}); + +test('复制模板时会复制所有步骤', function () { + $template = SopTemplate::factory()->create(); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $template->steps()->create([ + 'step_number' => 2, + 'title' => '第二步', + 'sort_order' => 2, + ]); + + $newTemplate = $this->service->duplicate($template, '复制的模板'); + + expect($newTemplate->steps)->toHaveCount(2) + ->and($newTemplate->steps->first()->title)->toBe('第一步') + ->and($newTemplate->steps->last()->title)->toBe('第二步'); +}); + +test('复制模板时会复制交互任务', function () { + $template = SopTemplate::factory()->create(); + + $step = $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $step->interactiveTasks()->create([ + 'task_type' => 'confirm', + 'task_config' => ['message' => '确认?'], + 'is_required' => true, + ]); + + $newTemplate = $this->service->duplicate($template, '复制的模板'); + $newStep = $newTemplate->steps->first(); + + expect($newStep->interactiveTasks)->toHaveCount(1) + ->and($newStep->interactiveTasks->first()->task_type)->toBe('confirm'); +}); diff --git a/tests/Feature/SopTemplateTest.php b/tests/Feature/SopTemplateTest.php new file mode 100644 index 0000000..62d1ce3 --- /dev/null +++ b/tests/Feature/SopTemplateTest.php @@ -0,0 +1,200 @@ +user = User::factory()->create(); + $this->actingAs($this->user); +}); + +test('可以创建 SOP 模板', function () { + $template = SopTemplate::factory()->create([ + 'name' => '测试模板', + 'status' => 'draft', + 'created_by' => $this->user->id, + ]); + + expect($template->name)->toBe('测试模板') + ->and($template->status)->toBe('draft') + ->and($template->created_by)->toBe($this->user->id); +}); + +test('可以为模板添加步骤', function () { + $template = SopTemplate::factory()->create(); + + $step = $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'content' => '这是第一步的内容', + 'sort_order' => 1, + 'is_required' => true, + ]); + + expect($template->steps)->toHaveCount(1) + ->and($step->title)->toBe('第一步') + ->and($step->template->id)->toBe($template->id); +}); + +test('步骤按 sort_order 排序', function () { + $template = SopTemplate::factory()->create(); + + $template->steps()->create([ + 'step_number' => 3, + 'title' => '第三步', + 'sort_order' => 3, + ]); + + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $template->steps()->create([ + 'step_number' => 2, + 'title' => '第二步', + 'sort_order' => 2, + ]); + + $steps = $template->fresh()->steps; + + expect($steps)->toHaveCount(3) + ->and($steps->first()->title)->toBe('第一步') + ->and($steps->last()->title)->toBe('第三步'); +}); + +test('可以为步骤添加交互任务', function () { + $template = SopTemplate::factory()->create(); + $step = $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $task = $step->interactiveTasks()->create([ + 'task_type' => 'confirm', + 'task_config' => ['message' => '确认完成?'], + 'validation_rules' => [], + 'is_required' => true, + ]); + + expect($step->interactiveTasks)->toHaveCount(1) + ->and($task->task_type)->toBe('confirm') + ->and($task->step->id)->toBe($step->id); +}); + +test('模板状态可以从草稿变为已发布', function () { + $template = SopTemplate::factory()->create([ + 'status' => 'draft', + ]); + + $template->update([ + 'status' => 'published', + 'published_at' => now(), + ]); + + expect($template->fresh()->status)->toBe('published') + ->and($template->fresh()->published_at)->not->toBeNull(); +}); + +test('模板状态可以从已发布变为已归档', function () { + $template = SopTemplate::factory()->create([ + 'status' => 'published', + 'published_at' => now(), + ]); + + $template->update(['status' => 'archived']); + + expect($template->fresh()->status)->toBe('archived'); +}); + +test('可以创建模板版本快照', function () { + $template = SopTemplate::factory()->create(); + $template->steps()->create([ + 'step_number' => 1, + 'title' => '第一步', + 'sort_order' => 1, + ]); + + $version = $template->versions()->create([ + 'version' => '1.0.0', + 'change_log' => '首次发布', + 'content_snapshot' => [ + 'template' => $template->toArray(), + 'steps' => $template->steps->toArray(), + ], + 'created_by' => $this->user->id, + 'created_at' => now(), + ]); + + expect($template->versions)->toHaveCount(1) + ->and($version->version)->toBe('1.0.0') + ->and($version->content_snapshot)->toHaveKey('template') + ->and($version->content_snapshot)->toHaveKey('steps'); +}); + +test('模板记录活动日志', function () { + $template = SopTemplate::factory()->create([ + 'name' => '原始名称', + ]); + + $template->update(['name' => '新名称']); + + $activities = $template->activities; + + expect($activities)->toHaveCount(2) // created 和 updated + ->and($activities->last()->description)->toContain('updated'); +}); + +test('可以通过分类筛选模板', function () { + SopTemplate::factory()->create(['category' => '安全操作']); + SopTemplate::factory()->create(['category' => '设备维护']); + SopTemplate::factory()->create(['category' => '安全操作']); + + $safetyTemplates = SopTemplate::where('category', '安全操作')->get(); + + expect($safetyTemplates)->toHaveCount(2); +}); + +test('可以通过状态筛选模板', function () { + SopTemplate::factory()->create(['status' => 'draft']); + SopTemplate::factory()->create(['status' => 'published']); + SopTemplate::factory()->create(['status' => 'draft']); + + $draftTemplates = SopTemplate::where('status', 'draft')->get(); + + expect($draftTemplates)->toHaveCount(2); +}); + +test('模板的标签字段正确转换为数组', function () { + $template = SopTemplate::factory()->create([ + 'tags' => ['标签1', '标签2', '标签3'], + ]); + + expect($template->fresh()->tags)->toBeArray() + ->and($template->fresh()->tags)->toHaveCount(3) + ->and($template->fresh()->tags)->toContain('标签1'); +}); + +test('模板的适用部门字段正确转换为数组', function () { + $template = SopTemplate::factory()->create([ + 'applicable_departments' => ['生产部', '质检部'], + ]); + + expect($template->fresh()->applicable_departments)->toBeArray() + ->and($template->fresh()->applicable_departments)->toHaveCount(2); +}); + +test('可以软删除模板', function () { + $template = SopTemplate::factory()->create(); + $templateId = $template->id; + + $template->delete(); + + expect(SopTemplate::find($templateId))->toBeNull() + ->and(SopTemplate::withTrashed()->find($templateId))->not->toBeNull(); +});