feat(fer-19) : checkHealth sur le PontBasculeService
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Dto\PontBasculeHealth;
|
||||||
use App\Dto\PontBasculeReading;
|
use App\Dto\PontBasculeReading;
|
||||||
use App\Exception\PontBasculeException;
|
use App\Exception\PontBasculeException;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
@@ -28,7 +29,7 @@ final class PontBasculeService
|
|||||||
$body = $this->getBypassPayload();
|
$body = $this->getBypassPayload();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$response = $this->httpClient->request('POST', $this->baseUrl.'/send/dsd');
|
$response = $this->httpClient->request('POST', $this->buildUrl('/send/dsd'));
|
||||||
$body = $response->getContent(false);
|
$body = $response->getContent(false);
|
||||||
} catch (TransportExceptionInterface $exception) {
|
} catch (TransportExceptionInterface $exception) {
|
||||||
throw PontBasculeException::transportFailure($exception->getMessage());
|
throw PontBasculeException::transportFailure($exception->getMessage());
|
||||||
@@ -44,6 +45,54 @@ final class PontBasculeService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkHealth(): PontBasculeHealth
|
||||||
|
{
|
||||||
|
if ($this->bypass) {
|
||||||
|
return new PontBasculeHealth(
|
||||||
|
healthy: true,
|
||||||
|
ok: true,
|
||||||
|
busy: false,
|
||||||
|
portConnected: true,
|
||||||
|
portError: null,
|
||||||
|
hostname: 'bypass',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->httpClient->request('GET', $this->buildUrl('/health'));
|
||||||
|
$body = $response->getContent(false);
|
||||||
|
} catch (TransportExceptionInterface) {
|
||||||
|
return PontBasculeHealth::unhealthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = json_decode($body, true);
|
||||||
|
if (!is_array($payload)) {
|
||||||
|
return PontBasculeHealth::unhealthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
$ok = true === ($payload['ok'] ?? null);
|
||||||
|
$busy = true === ($payload['busy'] ?? null);
|
||||||
|
$portConnected = true === ($payload['port_connected'] ?? null);
|
||||||
|
$portError = $payload['port_error'] ?? null;
|
||||||
|
$hostname = $payload['hostname'] ?? null;
|
||||||
|
|
||||||
|
$healthy = $ok && $portConnected && !$busy && null === $portError;
|
||||||
|
|
||||||
|
return new PontBasculeHealth(
|
||||||
|
healthy: $healthy,
|
||||||
|
ok: $ok,
|
||||||
|
busy: $busy,
|
||||||
|
portConnected: $portConnected,
|
||||||
|
portError: is_string($portError) ? $portError : null,
|
||||||
|
hostname: is_string($hostname) ? $hostname : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildUrl(string $path): string
|
||||||
|
{
|
||||||
|
return rtrim($this->baseUrl, '/').$path;
|
||||||
|
}
|
||||||
|
|
||||||
private function getBypassPayload(): string
|
private function getBypassPayload(): string
|
||||||
{
|
{
|
||||||
return '{"ok":true,"busy":false,"mode":"serial","port":"/dev/ttyUSB0","baudrate":9600,"request_hex":"01 10 39 39 4D 0D 0A","response_hex":"01 02 30 34 30 32 30 30 02 30 31 30 30 31 34 32 30 2E 6B 67 20 02 30 32 30 30 30 30 30 30 2E 6B 67 20 02 30 33 30 30 31 34 32 30 2E 6B 67 20 02 39 39 30 30 31 32 31 0D 0A","response_ascii":"\u0001\u0002040200\u000201001420.kg \u000202000000.kg \u000203001420.kg \u00029900121"}';
|
return '{"ok":true,"busy":false,"mode":"serial","port":"/dev/ttyUSB0","baudrate":9600,"request_hex":"01 10 39 39 4D 0D 0A","response_hex":"01 02 30 34 30 32 30 30 02 30 31 30 30 31 34 32 30 2E 6B 67 20 02 30 32 30 30 30 30 30 30 2E 6B 67 20 02 30 33 30 30 31 34 32 30 2E 6B 67 20 02 39 39 30 30 31 32 31 0D 0A","response_ascii":"\u0001\u0002040200\u000201001420.kg \u000202000000.kg \u000203001420.kg \u00029900121"}';
|
||||||
|
|||||||
@@ -82,4 +82,103 @@ final class PontBasculeServiceTest extends TestCase
|
|||||||
|
|
||||||
$service->fetch();
|
$service->fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthBypassIsHealthyWithoutHttpCall(): void
|
||||||
|
{
|
||||||
|
$httpClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$httpClient->expects(self::never())->method('request');
|
||||||
|
|
||||||
|
$service = new PontBasculeService($httpClient, new PontBasculePayloadDecoder(), 'http://example.test', true);
|
||||||
|
|
||||||
|
$health = $service->checkHealth();
|
||||||
|
|
||||||
|
self::assertTrue($health->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthHealthyPayload(): void
|
||||||
|
{
|
||||||
|
$service = $this->serviceForHealthBody(json_encode([
|
||||||
|
'ok' => true,
|
||||||
|
'busy' => false,
|
||||||
|
'port_connected' => true,
|
||||||
|
'port_error' => null,
|
||||||
|
'hostname' => 'liot-rasp-ferme-01',
|
||||||
|
], JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
$health = $service->checkHealth();
|
||||||
|
|
||||||
|
self::assertTrue($health->isHealthy());
|
||||||
|
self::assertSame('liot-rasp-ferme-01', $health->getHostname());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthUnhealthyWhenPortError(): void
|
||||||
|
{
|
||||||
|
$service = $this->serviceForHealthBody(json_encode([
|
||||||
|
'ok' => true,
|
||||||
|
'busy' => false,
|
||||||
|
'port_connected' => true,
|
||||||
|
'port_error' => 'device disconnected',
|
||||||
|
], JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
self::assertFalse($service->checkHealth()->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthUnhealthyWhenPortNotConnected(): void
|
||||||
|
{
|
||||||
|
$service = $this->serviceForHealthBody(json_encode([
|
||||||
|
'ok' => true,
|
||||||
|
'busy' => false,
|
||||||
|
'port_connected' => false,
|
||||||
|
'port_error' => null,
|
||||||
|
], JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
self::assertFalse($service->checkHealth()->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthUnhealthyWhenBusy(): void
|
||||||
|
{
|
||||||
|
$service = $this->serviceForHealthBody(json_encode([
|
||||||
|
'ok' => true,
|
||||||
|
'busy' => true,
|
||||||
|
'port_connected' => true,
|
||||||
|
'port_error' => null,
|
||||||
|
], JSON_THROW_ON_ERROR));
|
||||||
|
|
||||||
|
self::assertFalse($service->checkHealth()->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthUnhealthyOnTransportFailure(): void
|
||||||
|
{
|
||||||
|
$httpClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$httpClient
|
||||||
|
->expects(self::once())
|
||||||
|
->method('request')
|
||||||
|
->willThrowException($this->createStub(TransportExceptionInterface::class))
|
||||||
|
;
|
||||||
|
|
||||||
|
$service = new PontBasculeService($httpClient, new PontBasculePayloadDecoder(), 'http://example.test', false);
|
||||||
|
|
||||||
|
self::assertFalse($service->checkHealth()->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCheckHealthUnhealthyOnInvalidJson(): void
|
||||||
|
{
|
||||||
|
self::assertFalse($this->serviceForHealthBody('not-json')->checkHealth()->isHealthy());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function serviceForHealthBody(string $body): PontBasculeService
|
||||||
|
{
|
||||||
|
$response = $this->createStub(ResponseInterface::class);
|
||||||
|
$response->method('getContent')->with(false)->willReturn($body);
|
||||||
|
|
||||||
|
$httpClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$httpClient
|
||||||
|
->expects(self::once())
|
||||||
|
->method('request')
|
||||||
|
->with('GET', 'http://example.test/health')
|
||||||
|
->willReturn($response)
|
||||||
|
;
|
||||||
|
|
||||||
|
return new PontBasculeService($httpClient, new PontBasculePayloadDecoder(), 'http://example.test', false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user