savedContexts[] = [ 'operation' => $context->dbOperation, 'step' => $context->currentStep, 'batchNo' => $context->batchNo, 'processType' => $context->processType, ]; } protected function sendVoice(ProcessContext $context): void { $this->voiceSent[] = $context->getFullVoice(); } protected function sendWebSocketNotification(ProcessContext $context): void { $this->wsSent[] = $context->currentStep; } }; } /** * 测试成功流程触发 saveToDatabase */ public function testSuccessfulFlowSavesToDatabase(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext([ 'readerType' => '清洗', 'currentStep' => '', 'endoscopeId' => '1', ]); // 直接调用 handleResult(绕过 fromPacketContext) $context->needDatabaseOperation = true; $context->dbOperation = DbOperationType::INSERT; $context->currentStep = '清洗'; $context->processType = '手工洗'; $context->batchNo = 'BATCH001'; $this->invokeHandleResult($processor, $context); $this->assertNotEmpty($processor->savedContexts); $this->assertEquals(DbOperationType::INSERT, $processor->savedContexts[0]['operation']); $this->assertEquals('清洗', $processor->savedContexts[0]['step']); } /** * 测试成功流程触发 sendVoice */ public function testSuccessfulFlowSendsVoice(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext([ 'endoscopeName' => '胃镜01', ]); $context->setVoice('清洗完成'); $context->needDatabaseOperation = false; $this->invokeHandleResult($processor, $context); $this->assertNotEmpty($processor->voiceSent); $this->assertStringContainsString('清洗完成', $processor->voiceSent[0]); } /** * 测试失败流程不触发 saveToDatabase */ public function testFailedFlowDoesNotSaveToDatabase(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext(); $context->setError('刷错,清洗剩余120秒'); $context->needDatabaseOperation = true; $this->invokeHandleResult($processor, $context); $this->assertEmpty($processor->savedContexts); } /** * 测试失败流程仍然播报语音 */ public function testFailedFlowStillSendsVoice(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext(); $context->setError('刷错,清洗剩余120秒'); $context->setVoice('刷错,清洗剩余120秒'); $this->invokeHandleResult($processor, $context); $this->assertNotEmpty($processor->voiceSent); } /** * 测试成功流程需要 WebSocket 时触发通知 */ public function testWebSocketNotificationSentWhenNeeded(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext(['currentStep' => '清洗']); $context->needDatabaseOperation = false; $context->needWebSocketNotify = true; $this->invokeHandleResult($processor, $context); $this->assertContains('清洗', $processor->wsSent); } /** * 测试 needWebSocketNotify=false 时不发送 WebSocket */ public function testWebSocketNotificationNotSentWhenNotNeeded(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext(['currentStep' => '清洗']); $context->needDatabaseOperation = false; $context->needWebSocketNotify = false; $this->invokeHandleResult($processor, $context); $this->assertEmpty($processor->wsSent); } /** * 测试结束操作为 update */ public function testEndOperationIsUpdate(): void { $processor = $this->createTestableProcessor(); $context = $this->createContext([ 'currentStep' => '结束', 'batchNo' => 'BATCH001', ]); $context->needDatabaseOperation = true; $context->dbOperation = DbOperationType::UPDATE; $context->actionEndTime = date('Y-m-d H:i:s'); $this->invokeHandleResult($processor, $context); $this->assertNotEmpty($processor->savedContexts); $this->assertEquals(DbOperationType::UPDATE, $processor->savedContexts[0]['operation']); } /** * 测试 getActionType 各流程类型映射 * 注:getActionType 已移至 EctActionsRepository,此测试验证映射逻辑 */ public function testGetActionTypeMapping(): void { $repo = \app\repository\EctActionsRepository::new(); $method = new \ReflectionMethod($repo, 'mapActionType'); $method->setAccessible(true); $this->assertEquals(1, $method->invoke($repo, '手工洗')); $this->assertEquals(1, $method->invoke($repo, '手工洗(晨洗)')); $this->assertEquals(1, $method->invoke($repo, '手工洗(加强)')); $this->assertEquals(2, $method->invoke($repo, '机洗')); $this->assertEquals(2, $method->invoke($repo, '机洗(晨洗)')); $this->assertEquals(2, $method->invoke($repo, '机洗(加强)')); $this->assertEquals(7, $method->invoke($repo, '测漏')); $this->assertEquals(8, $method->invoke($repo, '存储')); $this->assertEquals(0, $method->invoke($repo, '诊疗')); // 未知类型 fallback 到 1 $this->assertEquals(1, $method->invoke($repo, '未知类型')); } /** * 测试 updateConfig 更新引擎配置 */ public function testUpdateConfig(): void { $processor = $this->createTestableProcessor(); $newConfig = ProcessConfig::createMachineWash(); $processor->updateConfig($newConfig); $this->assertFalse($processor->engine->getNode('漂洗')->isEnabled()); $this->assertFalse($processor->engine->getNode('消毒')->isEnabled()); } /** * 测试静态工厂 create */ public function testStaticFactoryCreate(): void { $processor = FlowProcessor::create(ProcessConfig::createStandard()); $this->assertInstanceOf(FlowProcessor::class, $processor); $this->assertInstanceOf(ProcessEngine::class, $processor->engine); } /** * 通过反射调用 handleResult(protected 方法) */ private function invokeHandleResult(FlowProcessor $processor, ProcessContext $context): void { $method = new \ReflectionMethod($processor, 'handleResult'); $method->setAccessible(true); $method->invoke($processor, $context); } }