assertEquals('', $context->endoscopeId); $this->assertEquals('', $context->currentStep); $this->assertTrue($context->success); } /** * 测试超长内镜名称 */ public function testLongEndoscopeName(): void { $longName = str_repeat('北院电子胃镜', 10); $context = $this->createContext([ 'endoscopeName' => $longName, ]); $context->setVoice('测试'); $fullVoice = $context->getFullVoice(); // 当前实现返回的是语音消息本身 $this->assertEquals('测试', $fullVoice); } /** * 测试特殊字符内镜名称 */ public function testSpecialCharactersInName(): void { $specialName = '北院<电子>胃镜&001"\''; $context = $this->createContext([ 'endoscopeName' => $specialName, ]); $context->setVoice('测试'); $fullVoice = $context->getFullVoice(); // 当前实现返回的是语音消息本身 $this->assertEquals('测试', $fullVoice); } /** * 测试批次号生成(边界值) */ public function testBatchNoGenerationEdgeCases(): void { // 内镜ID为0 $context1 = $this->createContext(['endoscopeId' => '0']); $batchNo1 = $context1->generateBatchNo(); $this->assertEquals(24, strlen($batchNo1)); $this->assertStringContainsString('000000', $batchNo1); // 内镜ID超长 $context2 = $this->createContext(['endoscopeId' => '123456789012345']); $batchNo2 = $context2->generateBatchNo(); $this->assertEquals(24, strlen($batchNo2)); // 内镜ID为空 $context3 = $this->createContext(['endoscopeId' => '']); $batchNo3 = $context3->generateBatchNo(); $this->assertEquals(24, strlen($batchNo3)); } /** * 测试时间边界值 */ public function testTimeBoundaryValues(): void { $context = $this->createContext(); // 时间为0 $this->assertEquals(0, $context->getStepDuration('不存在的步骤')); // 设置时间为未来(异常情况) $futureTime = date('Y-m-d H:i:s', time() + 3600); $context->setStepLastTime('测试', $futureTime); $this->assertEquals($futureTime, $context->getStepLastTime('测试')); } /** * 测试空责任链 */ public function testEmptyChain(): void { $config = ProcessConfig::fromArray([ 'steps' => [], ]); $engine = new ProcessEngine($config); $context = $this->createContext([ 'readerType' => '清洗', 'currentStep' => '', ]); $result = $engine->execute($context); // 空链应该返回原始上下文 $this->assertEquals('', $result->currentStep); } /** * 测试所有节点禁用 */ public function testAllNodesDisabled(): void { $config = ProcessConfig::createStandard(); $config->setNodeEnabled('晨洗', false); $config->setNodeEnabled('清洗', false); $config->setNodeEnabled('漂洗', false); $config->setNodeEnabled('消毒', false); $config->setNodeEnabled('终末漂洗', false); $config->setNodeEnabled('干燥', false); $config->setNodeEnabled('结束', false); $engine = new ProcessEngine($config); $context = $this->createContext([ 'readerType' => '清洗', 'currentStep' => '', 'morningWashed' => true, ]); $result = $engine->execute($context); // 没有节点能处理,保持原样 $this->assertEquals('', $result->currentStep); } /** * 测试重复设置步骤时间 */ public function testRepeatedStepTimeSetting(): void { $context = $this->createContext(); $time1 = date('Y-m-d H:i:s', time() - 100); $time2 = date('Y-m-d H:i:s', time() - 200); $context->setStepLastTime('清洗', $time1); $this->assertEquals($time1, $context->getStepLastTime('清洗')); $context->setStepLastTime('清洗', $time2); $this->assertEquals($time2, $context->getStepLastTime('清洗')); } /** * 测试链式调用边界 */ public function testChainingEdgeCases(): void { $context = $this->createContext(); // 多次链式调用 $result = $context ->setVoice('语音1') ->setVoice('语音2') ->setVoice('语音3'); $this->assertSame($context, $result); $this->assertEquals('语音3', $context->voiceMessage); } /** * 测试错误状态覆盖 */ public function testErrorStateOverwrite(): void { $context = $this->createContext(); $context->setError('错误1'); $this->assertFalse($context->success); $this->assertEquals('错误1', $context->errorMessage); // 再次设置错误 $context->setError('错误2'); $this->assertFalse($context->success); $this->assertEquals('错误2', $context->errorMessage); } /** * 测试从错误恢复 */ public function testRecoveryFromError(): void { $context = $this->createContext(); $context->setError('测试错误'); $this->assertFalse($context->success); // 手动重置成功状态(实际业务中可能不需要) $context->success = true; $context->errorMessage = ''; $this->assertTrue($context->success); } /** * 测试无效的配置项 */ public function testInvalidConfigItems(): void { $config = new ProcessConfig(); // 添加不存在的节点类 $config->addStep('无效步骤', 'NonExistentNode'); $step = $config->getStep('无效步骤'); $this->assertNotNull($step); $this->assertEquals('NonExistentNode', $step['class']); } /** * 测试并发批次号生成(模拟) */ public function testConcurrentBatchNoGeneration(): void { $batchNos = []; for ($i = 0; $i < 50; $i++) { // 每个批次号使用不同的上下文(模拟不同内镜) $context = $this->createContext(['endoscopeId' => (string)$i]); $batchNo = $context->generateBatchNo(); $batchNos[] = $batchNo; } // 所有批次号应该唯一(不同内镜ID保证唯一性) $uniqueBatchNos = array_unique($batchNos); $this->assertCount(50, $uniqueBatchNos, '批次号应该唯一'); } /** * 测试晨洗策略边界 - 凌晨时间 */ public function testMorningWashBoundaryTime(): void { $strategy = new \app\flow\strategies\MorningWashStrategy([ 'mode' => 'daily_first', 'morning_start_time' => '06:00:00', ]); $node = new \app\flow\nodes\WashNode(); // 凌晨5点(应该需要晨洗) $context1 = $this->createContext([ 'todayWashRecords' => 0, ]); $result1 = $strategy->execute($context1, $node); // daily_first 模式下,第一条记录需要晨洗 $this->assertTrue($result1->needMorningWash); } /** * 测试存储时间模式边界 - 刚好4小时 */ public function testStorageTimeBoundary(): void { $strategy = new \app\flow\strategies\MorningWashStrategy([ 'mode' => 'storage_time', 'storage_threshold' => 4, ]); $node = new \app\flow\nodes\WashNode(); // 刚好4小时前 $context = $this->createContext([ 'lastActionType' => '存储', 'lastProcessName' => '内镜放入', 'storageInTime' => date('Y-m-d H:i:s', time() - 4 * 3600), ]); $result = $strategy->execute($context, $node); // 刚好4小时,应该不需要晨洗(超过才需要) $this->assertFalse($result->needMorningWash); } /** * 测试存储时间模式 - 超过4小时 */ public function testStorageTimeOverThreshold(): void { $strategy = new \app\flow\strategies\MorningWashStrategy([ 'mode' => 'storage_time', 'storage_threshold' => 4, ]); $node = new \app\flow\nodes\WashNode(); // 4小时1秒前 $context = $this->createContext([ 'lastActionType' => '存储', 'lastProcessName' => '内镜放入', 'storageInTime' => date('Y-m-d H:i:s', time() - 4 * 3600 - 1), ]); $result = $strategy->execute($context, $node); $this->assertTrue($result->needMorningWash); } }