315 lines
8.9 KiB
PHP
315 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace tests\flow;
|
|
|
|
use app\flow\ProcessContext;
|
|
use app\flow\ProcessEngine;
|
|
use app\flow\config\ProcessConfig;
|
|
|
|
/**
|
|
* 边界条件和异常场景测试
|
|
*/
|
|
class EdgeCaseTest extends TestCase
|
|
{
|
|
/**
|
|
* 测试空上下文
|
|
*/
|
|
public function testEmptyContext(): void
|
|
{
|
|
$context = ProcessContext::create([]);
|
|
|
|
$this->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);
|
|
}
|
|
}
|