Files
tcpserver-flow/tests/flow/FlowProcessorTest.php
T
2026-03-08 22:58:56 +08:00

241 lines
7.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace tests\flow;
use app\flow\FlowProcessor;
use app\flow\ProcessContext;
use app\flow\ProcessEngine;
use app\flow\config\ProcessConfig;
use app\flow\DbOperationType;
/**
* FlowProcessor 单元测试
*
* 通过继承并覆盖 saveToDatabase / sendVoice / sendWebSocketNotification
* 隔离外部依赖(数据库、语音服务、WebSocket),专注业务逻辑验证。
*/
class FlowProcessorTest extends TestCase
{
/**
* 创建可测试的 FlowProcessor(覆盖外部依赖方法)
*/
protected function createTestableProcessor(ProcessConfig $config = null): FlowProcessor
{
$config = $config ?? ProcessConfig::createNoMorningWash();
return new class($config) extends FlowProcessor {
public array $savedContexts = [];
public array $voiceSent = [];
public array $wsSent = [];
protected function saveToDatabase(ProcessContext $context): void
{
$this->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);
}
/**
* 通过反射调用 handleResultprotected 方法)
*/
private function invokeHandleResult(FlowProcessor $processor, ProcessContext $context): void
{
$method = new \ReflectionMethod($processor, 'handleResult');
$method->setAccessible(true);
$method->invoke($processor, $context);
}
}