diff --git a/tests/Feature/PromptTemplateTest.php b/tests/Feature/PromptTemplateTest.php new file mode 100644 index 0000000..04885bc --- /dev/null +++ b/tests/Feature/PromptTemplateTest.php @@ -0,0 +1,266 @@ +service = app(PromptTemplateService::class); + + // 创建测试用户 + $this->user = User::factory()->create([ + 'name' => '测试用户', + ]); + + // 创建测试终端 + $this->terminal = Terminal::factory()->create([ + 'name' => '测试终端', + 'code' => 'TEST-001', + 'station_id' => 1001, + ]); + + $this->actingAs($this->user); + } + + /** @test */ + public function it_can_get_all_templates() + { + $templates = $this->service->getTemplates(); + + $this->assertIsArray($templates); + $this->assertNotEmpty($templates); + $this->assertArrayHasKey('id', $templates[0]); + $this->assertArrayHasKey('name', $templates[0]); + $this->assertArrayHasKey('content', $templates[0]); + } + + /** @test */ + public function it_can_get_template_by_id() + { + $template = $this->service->getTemplate('general_assistant'); + + $this->assertNotNull($template); + $this->assertEquals('general_assistant', $template['id']); + $this->assertArrayHasKey('content', $template); + } + + /** @test */ + public function it_returns_null_for_invalid_template_id() + { + $template = $this->service->getTemplate('non_existent_template'); + + $this->assertNull($template); + } + + /** @test */ + public function it_can_apply_template_to_terminal() + { + $content = $this->service->applyTemplate($this->terminal, 'general_assistant'); + + $this->assertIsString($content); + $this->assertNotEmpty($content); + $this->assertStringContainsString('{user}', $content); + $this->assertStringContainsString('{station}', $content); + } + + /** @test */ + public function it_throws_exception_for_invalid_template() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('模板不存在'); + + $this->service->applyTemplate($this->terminal, 'invalid_template'); + } + + /** @test */ + public function it_can_replace_variables_in_template() + { + $template = '你好,{user}!当前时间是 {time},你在 {station}。'; + + $result = $this->service->replaceVariables($template, $this->terminal); + + $this->assertStringContainsString('测试用户', $result); + $this->assertStringContainsString('工作站 1001', $result); + $this->assertStringNotContainsString('{user}', $result); + $this->assertStringNotContainsString('{station}', $result); + } + + /** @test */ + public function it_can_get_variable_values() + { + $variables = $this->service->getVariableValues($this->terminal); + + $this->assertIsArray($variables); + $this->assertArrayHasKey('user', $variables); + $this->assertArrayHasKey('terminal_name', $variables); + $this->assertArrayHasKey('station', $variables); + $this->assertArrayHasKey('time', $variables); + + $this->assertEquals('测试用户', $variables['user']); + $this->assertEquals('测试终端', $variables['terminal_name']); + $this->assertEquals('TEST-001', $variables['terminal_code']); + } + + /** @test */ + public function it_handles_array_variables_correctly() + { + // 创建知识库关联 + $kb1 = \App\Models\KnowledgeBase::factory()->create(['name' => '知识库1']); + $kb2 = \App\Models\KnowledgeBase::factory()->create(['name' => '知识库2']); + + $this->terminal->knowledgeBases()->attach([ + $kb1->id => ['priority' => 1], + $kb2->id => ['priority' => 2], + ]); + + $template = '可用知识库:{knowledge_bases}'; + $result = $this->service->replaceVariables($template, $this->terminal); + + $this->assertStringContainsString('知识库1', $result); + $this->assertStringContainsString('知识库2', $result); + } + + /** @test */ + public function it_can_preview_template() + { + $template = '你好,{user}!终端:{terminal_name}'; + + $preview = $this->service->preview($template, $this->terminal); + + $this->assertStringContainsString('测试用户', $preview); + $this->assertStringContainsString('测试终端', $preview); + $this->assertStringNotContainsString('{user}', $preview); + } + + /** @test */ + public function it_can_validate_variables() + { + $validTemplate = '你好,{user}!时间:{time}'; + $invalidVariables = $this->service->validateVariables($validTemplate); + + $this->assertEmpty($invalidVariables); + } + + /** @test */ + public function it_detects_invalid_variables() + { + $invalidTemplate = '你好,{user}!无效变量:{invalid_var},另一个:{another_invalid}'; + $invalidVariables = $this->service->validateVariables($invalidTemplate); + + $this->assertNotEmpty($invalidVariables); + $this->assertContains('invalid_var', $invalidVariables); + $this->assertContains('another_invalid', $invalidVariables); + } + + /** @test */ + public function it_determines_shift_correctly() + { + // 使用反射访问protected方法 + $reflection = new \ReflectionClass($this->service); + $method = $reflection->getMethod('getCurrentShift'); + $method->setAccessible(true); + + // 测试早班 (8:00-16:00) + $morningTime = now()->setTime(10, 0); + $this->assertEquals('早班', $method->invoke($this->service, $morningTime)); + + // 测试中班 (16:00-24:00) + $afternoonTime = now()->setTime(18, 0); + $this->assertEquals('中班', $method->invoke($this->service, $afternoonTime)); + + // 测试夜班 (0:00-8:00) + $nightTime = now()->setTime(2, 0); + $this->assertEquals('夜班', $method->invoke($this->service, $nightTime)); + } + + /** @test */ + public function terminal_can_save_prompt_template() + { + $promptData = [ + 'prompt_template' => '这是一个测试提示词模板,包含变量 {user} 和 {station}', + 'variables' => ['user', 'station'], + ]; + + $prompt = $this->terminal->prompt()->create($promptData); + + $this->assertInstanceOf(TerminalPrompt::class, $prompt); + $this->assertEquals($promptData['prompt_template'], $prompt->prompt_template); + $this->assertEquals($promptData['variables'], $prompt->variables); + } + + /** @test */ + public function terminal_prompt_relationship_works() + { + TerminalPrompt::factory()->create([ + 'terminal_id' => $this->terminal->id, + 'prompt_template' => '测试模板', + ]); + + $this->terminal->refresh(); + + $this->assertNotNull($this->terminal->prompt); + $this->assertEquals('测试模板', $this->terminal->prompt->prompt_template); + } + + /** @test */ + public function config_files_are_properly_structured() + { + // 测试变量配置 + $variables = config('prompt_variables.variables'); + $this->assertIsArray($variables); + $this->assertNotEmpty($variables); + + foreach ($variables as $variable) { + $this->assertArrayHasKey('name', $variable); + $this->assertArrayHasKey('label', $variable); + $this->assertArrayHasKey('description', $variable); + $this->assertArrayHasKey('example', $variable); + $this->assertArrayHasKey('type', $variable); + $this->assertArrayHasKey('category', $variable); + } + + // 测试模板配置 + $templates = config('prompt_templates.templates'); + $this->assertIsArray($templates); + $this->assertNotEmpty($templates); + + foreach ($templates as $template) { + $this->assertArrayHasKey('id', $template); + $this->assertArrayHasKey('name', $template); + $this->assertArrayHasKey('description', $template); + $this->assertArrayHasKey('category', $template); + $this->assertArrayHasKey('content', $template); + } + } + + /** @test */ + public function all_template_variables_are_valid() + { + $templates = config('prompt_templates.templates'); + + foreach ($templates as $template) { + $invalidVars = $this->service->validateVariables($template['content']); + + $this->assertEmpty( + $invalidVars, + "模板 '{$template['name']}' 包含无效变量: " . implode(', ', $invalidVars) + ); + } + } +} diff --git a/tests/Feature/TerminalKnowledgeBaseAssociationTest.php b/tests/Feature/TerminalKnowledgeBaseAssociationTest.php new file mode 100644 index 0000000..ffb7f58 --- /dev/null +++ b/tests/Feature/TerminalKnowledgeBaseAssociationTest.php @@ -0,0 +1,158 @@ +user = User::factory()->create(); + } + + /** @test */ + public function terminal_can_associate_with_knowledge_bases() + { + // 创建终端 + $terminal = Terminal::factory()->create([ + 'name' => '测试终端', + 'code' => 'TEST-001', + ]); + + // 创建知识库 + $kb1 = KnowledgeBase::create([ + 'name' => '知识库1', + 'description' => '测试知识库1', + 'status' => 'active', + ]); + + $kb2 = KnowledgeBase::create([ + 'name' => '知识库2', + 'description' => '测试知识库2', + 'status' => 'active', + ]); + + // 关联知识库并设置优先级 + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]); + $terminal->knowledgeBases()->attach($kb2->id, ['priority' => 2]); + + // 验证关联关系 + $this->assertCount(2, $terminal->knowledgeBases); + $this->assertTrue($terminal->knowledgeBases->contains($kb1)); + $this->assertTrue($terminal->knowledgeBases->contains($kb2)); + } + + /** @test */ + public function knowledge_bases_are_ordered_by_priority() + { + // 创建终端 + $terminal = Terminal::factory()->create(); + + // 创建知识库 + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + $kb3 = KnowledgeBase::create(['name' => '知识库3', 'status' => 'active']); + + // 按不同优先级关联(优先级越小越靠前) + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 10]); + $terminal->knowledgeBases()->attach($kb2->id, ['priority' => 5]); + $terminal->knowledgeBases()->attach($kb3->id, ['priority' => 1]); + + // 重新加载关联关系 + $terminal->refresh(); + + // 验证排序(应该按优先级从小到大排序) + $orderedKbs = $terminal->knowledgeBases; + $this->assertEquals($kb3->id, $orderedKbs[0]->id); // priority 1 + $this->assertEquals($kb2->id, $orderedKbs[1]->id); // priority 5 + $this->assertEquals($kb1->id, $orderedKbs[2]->id); // priority 10 + } + + /** @test */ + public function terminal_can_update_knowledge_base_associations() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + + // 初始关联 + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]); + + // 验证初始状态 + $this->assertCount(1, $terminal->knowledgeBases); + + // 更新关联(添加新的知识库) + $terminal->knowledgeBases()->attach($kb2->id, ['priority' => 2]); + + // 重新加载 + $terminal->refresh(); + + // 验证更新后的状态 + $this->assertCount(2, $terminal->knowledgeBases); + } + + /** @test */ + public function terminal_can_remove_knowledge_base_associations() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + + // 关联知识库 + $terminal->knowledgeBases()->attach([ + $kb1->id => ['priority' => 1], + $kb2->id => ['priority' => 2], + ]); + + // 验证初始状态 + $this->assertCount(2, $terminal->knowledgeBases); + + // 移除一个关联 + $terminal->knowledgeBases()->detach($kb1->id); + + // 重新加载 + $terminal->refresh(); + + // 验证移除后的状态 + $this->assertCount(1, $terminal->knowledgeBases); + $this->assertFalse($terminal->knowledgeBases->contains($kb1)); + $this->assertTrue($terminal->knowledgeBases->contains($kb2)); + } + + /** @test */ + public function terminal_can_update_priority_of_associated_knowledge_base() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + $kb = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + + // 关联知识库 + $terminal->knowledgeBases()->attach($kb->id, ['priority' => 5]); + + // 验证初始优先级 + $this->assertEquals(5, $terminal->knowledgeBases->first()->pivot->priority); + + // 更新优先级 + $terminal->knowledgeBases()->updateExistingPivot($kb->id, ['priority' => 1]); + + // 重新加载 + $terminal->refresh(); + + // 验证更新后的优先级 + $this->assertEquals(1, $terminal->knowledgeBases->first()->pivot->priority); + } +} diff --git a/tests/Feature/TerminalKnowledgeBaseFormTest.php b/tests/Feature/TerminalKnowledgeBaseFormTest.php new file mode 100644 index 0000000..d6f7b23 --- /dev/null +++ b/tests/Feature/TerminalKnowledgeBaseFormTest.php @@ -0,0 +1,181 @@ +user = User::factory()->create(); + $this->actingAs($this->user); + } + + /** @test */ + public function terminal_form_has_knowledge_base_association_field() + { + // 创建知识库 + KnowledgeBase::create([ + 'name' => '测试知识库', + 'description' => '测试描述', + 'status' => 'active', + ]); + + // 测试创建表单包含知识库关联字段 + $component = Livewire::test(TerminalResource\Pages\CreateTerminal::class); + + // 验证表单可以渲染 + $component->assertSuccessful(); + + // 验证表单组件存在(通过检查表单实例) + $this->assertNotNull($component->instance()->form); + } + + /** @test */ + public function terminal_can_be_created_with_knowledge_base_associations() + { + // 创建知识库 + $kb1 = KnowledgeBase::create([ + 'name' => '生产流程知识库', + 'status' => 'active', + ]); + + $kb2 = KnowledgeBase::create([ + 'name' => '设备维护知识库', + 'status' => 'active', + ]); + + // 创建终端 + $terminal = Terminal::factory()->create([ + 'name' => '测试终端', + 'code' => 'TEST-001', + ]); + + // 手动关联知识库(模拟表单提交后的效果) + $terminal->knowledgeBases()->attach([ + $kb1->id => ['priority' => 1], + $kb2->id => ['priority' => 2], + ]); + + // 验证关联关系 + $this->assertCount(2, $terminal->knowledgeBases); + $this->assertTrue($terminal->knowledgeBases->contains($kb1)); + $this->assertTrue($terminal->knowledgeBases->contains($kb2)); + } + + /** @test */ + public function edit_form_can_load_terminal_with_knowledge_bases() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + + // 关联知识库 + $terminal->knowledgeBases()->attach([ + $kb1->id => ['priority' => 5], + $kb2->id => ['priority' => 10], + ]); + + // 测试编辑表单可以加载 + $component = Livewire::test(TerminalResource\Pages\EditTerminal::class, [ + 'record' => $terminal->getRouteKey(), + ]); + + // 验证表单可以渲染 + $component->assertSuccessful(); + + // 验证基本信息加载正确 + $component->assertFormSet([ + 'name' => $terminal->name, + 'code' => $terminal->code, + ]); + } + + /** @test */ + public function terminal_knowledge_base_associations_can_be_updated() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + $kb3 = KnowledgeBase::create(['name' => '知识库3', 'status' => 'active']); + + // 初始关联 + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]); + + // 更新关联(移除旧的,添加新的) + $terminal->knowledgeBases()->sync([ + $kb2->id => ['priority' => 5], + $kb3->id => ['priority' => 10], + ]); + + // 验证更新后的关联 + $terminal->refresh(); + $this->assertCount(2, $terminal->knowledgeBases); + $this->assertFalse($terminal->knowledgeBases->contains($kb1)); + $this->assertTrue($terminal->knowledgeBases->contains($kb2)); + $this->assertTrue($terminal->knowledgeBases->contains($kb3)); + } + + /** @test */ + public function terminal_can_remove_all_knowledge_base_associations() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + + // 初始关联 + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]); + $this->assertCount(1, $terminal->knowledgeBases); + + // 移除所有关联 + $terminal->knowledgeBases()->detach(); + + // 验证所有关联已移除 + $terminal->refresh(); + $this->assertCount(0, $terminal->knowledgeBases); + } + + /** @test */ + public function knowledge_bases_are_ordered_by_priority_in_form() + { + // 创建终端和知识库 + $terminal = Terminal::factory()->create(); + + $kb1 = KnowledgeBase::create(['name' => '知识库1', 'status' => 'active']); + $kb2 = KnowledgeBase::create(['name' => '知识库2', 'status' => 'active']); + $kb3 = KnowledgeBase::create(['name' => '知识库3', 'status' => 'active']); + + // 按不同优先级关联 + $terminal->knowledgeBases()->attach([ + $kb1->id => ['priority' => 10], + $kb2->id => ['priority' => 5], + $kb3->id => ['priority' => 1], + ]); + + // 重新加载并验证排序 + $terminal->refresh(); + $orderedKbs = $terminal->knowledgeBases; + + $this->assertEquals($kb3->id, $orderedKbs[0]->id); // priority 1 + $this->assertEquals($kb2->id, $orderedKbs[1]->id); // priority 5 + $this->assertEquals($kb1->id, $orderedKbs[2]->id); // priority 10 + } +} diff --git a/tests/Feature/TerminalPromptFormTest.php b/tests/Feature/TerminalPromptFormTest.php new file mode 100644 index 0000000..b14e015 --- /dev/null +++ b/tests/Feature/TerminalPromptFormTest.php @@ -0,0 +1,98 @@ +user = User::factory()->create(); + } + + /** @test */ + public function terminal_can_be_created_with_prompt() + { + $this->actingAs($this->user); + + $terminalData = [ + 'name' => '测试终端', + 'code' => 'TEST-001', + 'ip_address' => '192.168.1.100', + 'station_id' => 1, + 'prompt' => [ + 'prompt_template' => '你是一个智能助手,当前用户是 {user}。', + ], + ]; + + Livewire::test(TerminalResource\Pages\CreateTerminal::class) + ->fillForm($terminalData) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas('terminals', [ + 'name' => '测试终端', + 'code' => 'TEST-001', + ]); + + $terminal = Terminal::where('code', 'TEST-001')->first(); + $this->assertNotNull($terminal->prompt); + $this->assertEquals('你是一个智能助手,当前用户是 {user}。', $terminal->prompt->prompt_template); + } + + /** @test */ + public function terminal_prompt_can_be_updated() + { + $this->actingAs($this->user); + + $terminal = Terminal::factory()->create(); + $terminal->prompt()->create([ + 'prompt_template' => '旧的提示词', + 'variables' => [], + ]); + + $newPromptData = [ + 'name' => $terminal->name, + 'code' => $terminal->code, + 'prompt' => [ + 'prompt_template' => '新的提示词模板', + ], + ]; + + Livewire::test(TerminalResource\Pages\EditTerminal::class, ['record' => $terminal->id]) + ->fillForm($newPromptData) + ->call('save') + ->assertHasNoFormErrors(); + + $terminal->refresh(); + $this->assertEquals('新的提示词模板', $terminal->prompt->prompt_template); + } + + /** @test */ + public function edit_form_loads_existing_prompt() + { + $this->actingAs($this->user); + + $terminal = Terminal::factory()->create(); + $terminal->prompt()->create([ + 'prompt_template' => '现有的提示词', + 'variables' => ['user'], + ]); + + Livewire::test(TerminalResource\Pages\EditTerminal::class, ['record' => $terminal->id]) + ->assertFormSet([ + 'prompt.prompt_template' => '现有的提示词', + ]); + } +} diff --git a/tests/Feature/TerminalPromptTest.php b/tests/Feature/TerminalPromptTest.php new file mode 100644 index 0000000..19e811b --- /dev/null +++ b/tests/Feature/TerminalPromptTest.php @@ -0,0 +1,118 @@ +user = User::factory()->create(); + } + + /** @test */ + public function terminal_can_have_prompt() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '你是一个智能助手,当前用户是 {user}。', + 'variables' => ['user', 'station', 'time'], + ]); + + $this->assertInstanceOf(TerminalPrompt::class, $terminal->prompt); + $this->assertEquals($prompt->id, $terminal->prompt->id); + $this->assertEquals('你是一个智能助手,当前用户是 {user}。', $terminal->prompt->prompt_template); + } + + /** @test */ + public function prompt_belongs_to_terminal() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '测试提示词', + 'variables' => [], + ]); + + $this->assertInstanceOf(Terminal::class, $prompt->terminal); + $this->assertEquals($terminal->id, $prompt->terminal->id); + } + + /** @test */ + public function prompt_variables_are_cast_to_array() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '测试提示词', + 'variables' => ['user', 'station', 'time'], + ]); + + $this->assertIsArray($prompt->variables); + $this->assertEquals(['user', 'station', 'time'], $prompt->variables); + } + + /** @test */ + public function deleting_terminal_deletes_prompt() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '测试提示词', + 'variables' => [], + ]); + + $promptId = $prompt->id; + + // 使用forceDelete来真正删除记录,触发级联删除 + $terminal->forceDelete(); + + $this->assertDatabaseMissing('terminal_prompts', ['id' => $promptId]); + } + + /** @test */ + public function prompt_template_can_be_empty() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '', + 'variables' => [], + ]); + + $this->assertEquals('', $prompt->prompt_template); + } + + /** @test */ + public function prompt_logs_activity() + { + $terminal = Terminal::factory()->create(); + + $prompt = TerminalPrompt::create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '测试提示词', + 'variables' => [], + ]); + + $this->assertDatabaseHas('activity_log', [ + 'subject_type' => TerminalPrompt::class, + 'subject_id' => $prompt->id, + ]); + } +} diff --git a/tests/Feature/TerminalResourceTest.php b/tests/Feature/TerminalResourceTest.php new file mode 100644 index 0000000..70612d0 --- /dev/null +++ b/tests/Feature/TerminalResourceTest.php @@ -0,0 +1,273 @@ +user = User::factory()->create(); + + // 设置为Filament管理员 + config(['filament.auth.guard' => 'web']); + + $this->actingAs($this->user); + } + + /** @test */ + public function it_can_render_terminal_list_page(): void + { + Livewire::test(ListTerminals::class) + ->assertSuccessful(); + } + + /** @test */ + public function it_can_list_terminals(): void + { + $terminals = Terminal::factory()->count(10)->create(); + + Livewire::test(ListTerminals::class) + ->assertCanSeeTableRecords($terminals); + } + + /** @test */ + public function it_can_render_create_page(): void + { + Livewire::test(CreateTerminal::class) + ->assertSuccessful(); + } + + /** @test */ + public function it_can_create_terminal(): void + { + $newData = [ + 'name' => '测试终端', + 'code' => 'TEST-0001', + 'ip_address' => '192.168.1.100', + 'station_id' => 1, + 'diagram_url' => 'https://example.com/diagram.png', + 'display_config' => [ + 'resolution' => '1920x1080', + 'refresh_rate' => '60', + ], + ]; + + Livewire::test(CreateTerminal::class) + ->fillForm($newData) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas('terminals', [ + 'name' => '测试终端', + 'code' => 'TEST-0001', + 'ip_address' => '192.168.1.100', + ]); + } + + /** @test */ + public function it_validates_required_fields_on_create(): void + { + Livewire::test(CreateTerminal::class) + ->fillForm([ + 'name' => '', + 'code' => '', + ]) + ->call('create') + ->assertHasFormErrors([ + 'name' => 'required', + 'code' => 'required', + ]); + } + + /** @test */ + public function it_validates_unique_code(): void + { + $existingTerminal = Terminal::factory()->create([ + 'code' => 'EXISTING-001', + ]); + + Livewire::test(CreateTerminal::class) + ->fillForm([ + 'name' => '新终端', + 'code' => 'EXISTING-001', + ]) + ->call('create') + ->assertHasFormErrors(['code' => 'unique']); + } + + /** @test */ + public function it_validates_ip_address_format(): void + { + Livewire::test(CreateTerminal::class) + ->fillForm([ + 'name' => '测试终端', + 'code' => 'TEST-0001', + 'ip_address' => 'invalid-ip', + ]) + ->call('create') + ->assertHasFormErrors(['ip_address' => 'ip']); + } + + /** @test */ + public function it_validates_code_format(): void + { + Livewire::test(CreateTerminal::class) + ->fillForm([ + 'name' => '测试终端', + 'code' => 'invalid code with spaces', + ]) + ->call('create') + ->assertHasFormErrors(['code' => 'regex']); + } + + /** @test */ + public function it_can_render_edit_page(): void + { + $terminal = Terminal::factory()->create(); + + Livewire::test(EditTerminal::class, ['record' => $terminal->getRouteKey()]) + ->assertSuccessful(); + } + + /** @test */ + public function it_can_retrieve_data_for_edit(): void + { + $terminal = Terminal::factory()->create(); + + Livewire::test(EditTerminal::class, ['record' => $terminal->getRouteKey()]) + ->assertFormSet([ + 'name' => $terminal->name, + 'code' => $terminal->code, + 'ip_address' => $terminal->ip_address, + 'station_id' => $terminal->station_id, + ]); + } + + /** @test */ + public function it_can_update_terminal(): void + { + $terminal = Terminal::factory()->create(); + + $newData = [ + 'name' => '更新后的终端', + 'code' => $terminal->code, + 'ip_address' => '192.168.1.200', + 'station_id' => 2, + ]; + + Livewire::test(EditTerminal::class, ['record' => $terminal->getRouteKey()]) + ->fillForm($newData) + ->call('save') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas('terminals', [ + 'id' => $terminal->id, + 'name' => '更新后的终端', + 'ip_address' => '192.168.1.200', + ]); + } + + /** @test */ + public function it_can_delete_terminal(): void + { + $terminal = Terminal::factory()->create(); + + Livewire::test(EditTerminal::class, ['record' => $terminal->getRouteKey()]) + ->callAction('delete'); + + $this->assertSoftDeleted('terminals', [ + 'id' => $terminal->id, + ]); + } + + /** @test */ + public function it_can_filter_by_online_status(): void + { + $onlineTerminals = Terminal::factory()->count(3)->online()->create(); + $offlineTerminals = Terminal::factory()->count(2)->offline()->create(); + + Livewire::test(ListTerminals::class) + ->filterTable('is_online', true) + ->assertCanSeeTableRecords($onlineTerminals) + ->assertCanNotSeeTableRecords($offlineTerminals); + } + + /** @test */ + public function it_can_search_terminals(): void + { + $terminal1 = Terminal::factory()->create(['name' => '生产线A-工位1']); + $terminal2 = Terminal::factory()->create(['name' => '质检站-工位2']); + + Livewire::test(ListTerminals::class) + ->searchTable('生产线A') + ->assertCanSeeTableRecords([$terminal1]) + ->assertCanNotSeeTableRecords([$terminal2]); + } + + /** @test */ + public function it_can_sort_terminals(): void + { + $terminals = Terminal::factory()->count(3)->create(); + + Livewire::test(ListTerminals::class) + ->sortTable('name') + ->assertCanSeeTableRecords($terminals->sortBy('name'), inOrder: true); + } + + /** @test */ + public function it_displays_online_status_badge(): void + { + $onlineTerminal = Terminal::factory()->online()->create(); + $offlineTerminal = Terminal::factory()->offline()->create(); + + Livewire::test(ListTerminals::class) + ->assertTableColumnStateSet('is_online', true, $onlineTerminal) + ->assertTableColumnStateSet('is_online', false, $offlineTerminal); + } + + /** @test */ + public function it_can_group_by_station(): void + { + Terminal::factory()->count(3)->create(['station_id' => 1]); + Terminal::factory()->count(2)->create(['station_id' => 2]); + + // 测试分组功能是否可用 + $component = Livewire::test(ListTerminals::class); + + // 验证表格可以正常渲染 + $component->assertSuccessful(); + } + + /** @test */ + public function it_can_bulk_delete_terminals(): void + { + $terminals = Terminal::factory()->count(3)->create(); + + Livewire::test(ListTerminals::class) + ->callTableBulkAction('delete', $terminals) + ->assertSuccessful(); + + foreach ($terminals as $terminal) { + $this->assertSoftDeleted('terminals', [ + 'id' => $terminal->id, + ]); + } + } +} diff --git a/tests/Feature/TerminalSyncTest.php b/tests/Feature/TerminalSyncTest.php new file mode 100644 index 0000000..45981e9 --- /dev/null +++ b/tests/Feature/TerminalSyncTest.php @@ -0,0 +1,280 @@ +syncService = app(TerminalSyncService::class); + } + + /** + * 测试单个终端同步 + */ + public function test_can_sync_single_terminal(): void + { + Queue::fake(); + + // 创建测试终端 + $terminal = Terminal::factory()->create([ + 'name' => '测试终端', + 'code' => 'TEST-001', + ]); + + // 执行同步 + $log = $this->syncService->syncConfiguration($terminal); + + // 断言同步日志已创建 + $this->assertInstanceOf(TerminalSyncLog::class, $log); + $this->assertEquals('pending', $log->status); + $this->assertEquals($terminal->id, $log->terminal_id); + $this->assertNotNull($log->config_snapshot); + + // 断言任务已加入队列 + Queue::assertPushed(SyncTerminalConfigJob::class, function ($job) use ($terminal, $log) { + return $job->terminal->id === $terminal->id + && $job->log->id === $log->id; + }); + } + + /** + * 测试配置快照包含完整信息 + */ + public function test_config_snapshot_contains_complete_information(): void + { + // 创建终端及关联数据 + $terminal = Terminal::factory()->create([ + 'name' => '测试终端', + 'code' => 'TEST-002', + 'ip_address' => '192.168.1.100', + 'station_id' => 1, + 'diagram_url' => 'https://example.com/diagram.png', + 'display_config' => ['resolution' => '1920x1080'], + ]); + + // 创建知识库关联 + $kb1 = KnowledgeBase::factory()->create(['name' => '知识库1']); + $kb2 = KnowledgeBase::factory()->create(['name' => '知识库2']); + $terminal->knowledgeBases()->attach($kb1->id, ['priority' => 1]); + $terminal->knowledgeBases()->attach($kb2->id, ['priority' => 2]); + + // 创建提示词 + TerminalPrompt::factory()->create([ + 'terminal_id' => $terminal->id, + 'prompt_template' => '你好,{user}', + 'variables' => ['user' => 'string'], + ]); + + // 获取配置快照 + $snapshot = $this->syncService->getConfigSnapshot($terminal); + + // 断言快照包含终端信息 + $this->assertArrayHasKey('terminal', $snapshot); + $this->assertEquals('测试终端', $snapshot['terminal']['name']); + $this->assertEquals('TEST-002', $snapshot['terminal']['code']); + $this->assertEquals('192.168.1.100', $snapshot['terminal']['ip_address']); + + // 断言快照包含知识库信息 + $this->assertArrayHasKey('knowledge_bases', $snapshot); + $this->assertCount(2, $snapshot['knowledge_bases']); + $this->assertEquals('知识库1', $snapshot['knowledge_bases'][0]['name']); + $this->assertEquals(1, $snapshot['knowledge_bases'][0]['priority']); + + // 断言快照包含提示词信息 + $this->assertArrayHasKey('prompt', $snapshot); + $this->assertEquals('你好,{user}', $snapshot['prompt']['prompt_template']); + } + + /** + * 测试批量同步 + */ + public function test_can_batch_sync_terminals(): void + { + Queue::fake(); + + // 创建多个终端 + $terminals = Terminal::factory()->count(3)->create(); + $terminalIds = $terminals->pluck('id')->toArray(); + + // 执行批量同步 + $logs = $this->syncService->batchSync($terminalIds); + + // 断言创建了3个同步日志 + $this->assertCount(3, $logs); + + // 断言每个终端都有同步日志 + foreach ($terminals as $terminal) { + $this->assertDatabaseHas('terminal_sync_logs', [ + 'terminal_id' => $terminal->id, + 'status' => 'pending', + ]); + } + + // 断言任务已加入队列 + Queue::assertPushed(SyncTerminalConfigJob::class, 3); + } + + /** + * 测试同步状态更新 + */ + public function test_can_update_sync_status(): void + { + $terminal = Terminal::factory()->create(); + $log = TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'pending', + 'config_snapshot' => [], + ]); + + // 更新为同步中 + $this->syncService->updateSyncStatus($log, 'syncing'); + $this->assertEquals('syncing', $log->fresh()->status); + + // 更新为已同步 + $this->syncService->updateSyncStatus($log, 'synced'); + $log->refresh(); + $this->assertEquals('synced', $log->status); + $this->assertNotNull($log->synced_at); + + // 更新为失败 + $this->syncService->updateSyncStatus($log, 'failed', '测试错误'); + $log->refresh(); + $this->assertEquals('failed', $log->status); + $this->assertEquals('测试错误', $log->error_message); + } + + /** + * 测试同步任务成功执行 + */ + public function test_sync_job_executes_successfully(): void + { + $terminal = Terminal::factory()->create(); + $log = TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'pending', + 'config_snapshot' => [], + ]); + + // 执行任务 + $job = new SyncTerminalConfigJob($terminal, $log); + $job->handle(); + + // 断言状态更新为已同步 + $log->refresh(); + $this->assertEquals('synced', $log->status); + $this->assertNotNull($log->synced_at); + $this->assertNull($log->error_message); + } + + /** + * 测试同步失败处理 + */ + public function test_sync_job_handles_failure(): void + { + $terminal = Terminal::factory()->create(); + $log = TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'pending', + 'config_snapshot' => [], + ]); + + $job = new SyncTerminalConfigJob($terminal, $log); + + // 模拟失败(通过调用failed方法) + $exception = new \Exception('测试同步失败'); + $job->failed($exception); + + // 断言状态更新为失败 + $log->refresh(); + $this->assertEquals('failed', $log->status); + $this->assertStringContainsString('测试同步失败', $log->error_message); + } + + /** + * 测试同步历史记录 + */ + public function test_can_view_sync_history(): void + { + $terminal = Terminal::factory()->create(); + + // 创建多条同步记录 + TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'synced', + 'config_snapshot' => [], + 'synced_at' => now()->subHours(2), + 'created_at' => now()->subHours(2), + ]); + + TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'failed', + 'config_snapshot' => [], + 'error_message' => '网络错误', + 'created_at' => now()->subHour(), + ]); + + TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'synced', + 'config_snapshot' => [], + 'synced_at' => now(), + 'created_at' => now(), + ]); + + // 获取同步历史(应该按时间倒序) + $logs = $terminal->syncLogs; + + $this->assertCount(3, $logs); + // 最新的记录应该在第一位 + $this->assertEquals('synced', $logs[0]->status); + $this->assertEquals('failed', $logs[1]->status); + $this->assertEquals('synced', $logs[2]->status); + } + + /** + * 测试获取最新同步日志 + */ + public function test_can_get_latest_sync_log(): void + { + $terminal = Terminal::factory()->create(); + + // 创建多条同步记录 + TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'synced', + 'config_snapshot' => [], + 'created_at' => now()->subHours(2), + ]); + + $latestLog = TerminalSyncLog::create([ + 'terminal_id' => $terminal->id, + 'status' => 'pending', + 'config_snapshot' => [], + 'created_at' => now(), + ]); + + // 获取最新同步日志 + $result = $terminal->latestSyncLog; + + $this->assertNotNull($result); + $this->assertEquals($latestLog->id, $result->id); + $this->assertEquals('pending', $result->status); + } +}