196 lines
8.0 KiB
PHP
196 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use App\Services\AgentConsumerDelayMonitorService;
|
|
use App\Services\ConfigService;
|
|
use App\Services\DingTalkService;
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Database\Connection;
|
|
use Illuminate\Database\DatabaseManager;
|
|
use Illuminate\Database\Query\Builder;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
class AgentConsumerDelayMonitorServiceTest extends TestCase
|
|
{
|
|
public function test_it_alerts_when_the_latest_pending_message_is_more_than_five_minutes_old(): void
|
|
{
|
|
CarbonImmutable::setTestNow('2026-09-01 15:00:00');
|
|
|
|
try {
|
|
[$database, $query] = $this->mockLatestPendingQuery();
|
|
$dingTalk = Mockery::mock(DingTalkService::class);
|
|
$config = Mockery::mock(ConfigService::class);
|
|
|
|
$query->shouldReceive('first')->once()->andReturn((object) [
|
|
'event_name' => 'CASE_CREATE',
|
|
'created' => '2026-09-01 14:52:00',
|
|
]);
|
|
$config->shouldReceive('get')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [])
|
|
->andReturn([]);
|
|
$dingTalk->shouldReceive('sendText')
|
|
->once()
|
|
->with(Mockery::on(fn (string $message) => str_contains($message, 'Agent 消费延迟告警')
|
|
&& str_contains($message, 'CASE_CREATE')
|
|
&& str_contains($message, '14:52:00')
|
|
&& str_contains($message, '8 分钟')))
|
|
->andReturnTrue();
|
|
$config->shouldReceive('set')
|
|
->once()
|
|
->with(
|
|
AgentConsumerDelayMonitorService::STATE_CONFIG_KEY,
|
|
['event_name' => 'CASE_CREATE', 'delayed_since' => '2026-09-01 14:52:00'],
|
|
'Agent 消费延迟告警状态'
|
|
);
|
|
|
|
$result = (new AgentConsumerDelayMonitorService($database, $dingTalk, $config))->check();
|
|
|
|
$this->assertSame('CASE_CREATE', $result['event_name']);
|
|
$this->assertSame(8, $result['delay_minutes']);
|
|
$this->assertSame(1, $result['alerted_count']);
|
|
$this->assertSame(0, $result['recovered_count']);
|
|
} finally {
|
|
CarbonImmutable::setTestNow();
|
|
}
|
|
}
|
|
|
|
public function test_it_does_not_repeat_an_active_delay_alert(): void
|
|
{
|
|
CarbonImmutable::setTestNow('2026-09-01 15:00:00');
|
|
|
|
try {
|
|
[$database, $query] = $this->mockLatestPendingQuery();
|
|
$dingTalk = Mockery::mock(DingTalkService::class);
|
|
$config = Mockery::mock(ConfigService::class);
|
|
$state = ['event_name' => 'CASE_CREATE', 'delayed_since' => '2026-09-01 14:50:00'];
|
|
|
|
$query->shouldReceive('first')->once()->andReturn((object) [
|
|
'event_name' => 'DOCTOR_CREATE',
|
|
'created' => '2026-09-01 14:52:00',
|
|
]);
|
|
$config->shouldReceive('get')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [])
|
|
->andReturn($state);
|
|
$dingTalk->shouldNotReceive('sendText');
|
|
$config->shouldNotReceive('set');
|
|
|
|
$result = (new AgentConsumerDelayMonitorService($database, $dingTalk, $config))->check();
|
|
|
|
$this->assertSame('DOCTOR_CREATE', $result['event_name']);
|
|
$this->assertSame(0, $result['alerted_count']);
|
|
$this->assertSame(0, $result['recovered_count']);
|
|
} finally {
|
|
CarbonImmutable::setTestNow();
|
|
}
|
|
}
|
|
|
|
public function test_it_notifies_recovery_when_the_latest_pending_message_is_within_five_minutes(): void
|
|
{
|
|
CarbonImmutable::setTestNow('2026-09-01 15:00:00');
|
|
|
|
try {
|
|
[$database, $query] = $this->mockLatestPendingQuery();
|
|
$dingTalk = Mockery::mock(DingTalkService::class);
|
|
$config = Mockery::mock(ConfigService::class);
|
|
|
|
$query->shouldReceive('first')->once()->andReturn((object) [
|
|
'event_name' => 'CASE_CREATE',
|
|
'created' => '2026-09-01 14:57:00',
|
|
]);
|
|
$config->shouldReceive('get')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [])
|
|
->andReturn(['event_name' => 'CASE_CREATE', 'delayed_since' => '2026-09-01 14:50:00']);
|
|
$dingTalk->shouldReceive('sendText')
|
|
->once()
|
|
->with(Mockery::on(fn (string $message) => str_contains($message, 'Agent 消费延迟恢复')))
|
|
->andReturnTrue();
|
|
$config->shouldReceive('set')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [], 'Agent 消费延迟告警状态');
|
|
|
|
$result = (new AgentConsumerDelayMonitorService($database, $dingTalk, $config))->check();
|
|
|
|
$this->assertSame(0, $result['alerted_count']);
|
|
$this->assertSame(1, $result['recovered_count']);
|
|
} finally {
|
|
CarbonImmutable::setTestNow();
|
|
}
|
|
}
|
|
|
|
public function test_it_notifies_recovery_when_there_are_no_pending_messages(): void
|
|
{
|
|
CarbonImmutable::setTestNow('2026-09-01 15:00:00');
|
|
|
|
try {
|
|
[$database, $query] = $this->mockLatestPendingQuery();
|
|
$dingTalk = Mockery::mock(DingTalkService::class);
|
|
$config = Mockery::mock(ConfigService::class);
|
|
|
|
$query->shouldReceive('first')->once()->andReturnNull();
|
|
$config->shouldReceive('get')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [])
|
|
->andReturn(['event_name' => 'CASE_CREATE', 'delayed_since' => '2026-09-01 14:50:00']);
|
|
$dingTalk->shouldReceive('sendText')->once()->andReturnTrue();
|
|
$config->shouldReceive('set')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [], 'Agent 消费延迟告警状态');
|
|
|
|
$result = (new AgentConsumerDelayMonitorService($database, $dingTalk, $config))->check();
|
|
|
|
$this->assertNull($result['event_name']);
|
|
$this->assertSame(1, $result['recovered_count']);
|
|
} finally {
|
|
CarbonImmutable::setTestNow();
|
|
}
|
|
}
|
|
|
|
public function test_it_retries_notifications_when_dingtalk_sending_fails(): void
|
|
{
|
|
CarbonImmutable::setTestNow('2026-09-01 15:00:00');
|
|
|
|
try {
|
|
[$database, $query] = $this->mockLatestPendingQuery();
|
|
$dingTalk = Mockery::mock(DingTalkService::class);
|
|
$config = Mockery::mock(ConfigService::class);
|
|
|
|
$query->shouldReceive('first')->once()->andReturn((object) [
|
|
'event_name' => 'CASE_CREATE',
|
|
'created' => '2026-09-01 14:50:00',
|
|
]);
|
|
$config->shouldReceive('get')
|
|
->once()
|
|
->with(AgentConsumerDelayMonitorService::STATE_CONFIG_KEY, [])
|
|
->andReturn([]);
|
|
$dingTalk->shouldReceive('sendText')->once()->andReturnFalse();
|
|
$config->shouldNotReceive('set');
|
|
|
|
$result = (new AgentConsumerDelayMonitorService($database, $dingTalk, $config))->check();
|
|
|
|
$this->assertSame(0, $result['alerted_count']);
|
|
} finally {
|
|
CarbonImmutable::setTestNow();
|
|
}
|
|
}
|
|
|
|
private function mockLatestPendingQuery(): array
|
|
{
|
|
$database = Mockery::mock(DatabaseManager::class);
|
|
$connection = Mockery::mock(Connection::class);
|
|
$query = Mockery::mock(Builder::class);
|
|
|
|
$database->shouldReceive('connection')->once()->with('agentslave')->andReturn($connection);
|
|
$connection->shouldReceive('table')->once()->with('crm_event_consumer')->andReturn($query);
|
|
$query->shouldReceive('select')->once()->with(['event_name', 'created'])->andReturnSelf();
|
|
$query->shouldReceive('where')->once()->with('status', '<>', 1)->andReturnSelf();
|
|
$query->shouldReceive('orderByDesc')->once()->with('id')->andReturnSelf();
|
|
|
|
return [$database, $query];
|
|
}
|
|
}
|