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'); });