226 lines
8.5 KiB
PHP
226 lines
8.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Integration\Infra\Jobs;
|
|
|
|
use Application\Health\Config\HealthAlertMailConfig;
|
|
use Application\Health\Contracts\HealthChecker;
|
|
use Application\Health\Dto\ArcadiaConformityResult;
|
|
use Application\Health\Dto\DirectoryConformityCheck;
|
|
use Application\Health\Enums\ComponentStatus;
|
|
use Application\Health\Enums\HealthStatus;
|
|
use Illuminate\Contracts\Mail\Mailer;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Infra\Jobs\CheckArcadiaDatabaseConformityJob;
|
|
use Infra\Mail\ArcadiaConformityAlertMail;
|
|
use Psr\Log\LoggerInterface;
|
|
use Shared\Time\Utc;
|
|
use Tests\TestCase;
|
|
|
|
covers(CheckArcadiaDatabaseConformityJob::class);
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
describe('CheckArcadiaDatabaseConformityJob Integration Test', function (): void {
|
|
$tempStateFile = null;
|
|
|
|
beforeEach(function () use (&$tempStateFile): void {
|
|
$tempStateFile = sys_get_temp_dir() . '/arcadia_conformity_test_' . uniqid() . '.json';
|
|
if (File::exists($tempStateFile)) {
|
|
File::delete($tempStateFile);
|
|
}
|
|
Mail::fake();
|
|
});
|
|
|
|
afterEach(function () use (&$tempStateFile): void {
|
|
if ($tempStateFile !== null && File::exists($tempStateFile)) {
|
|
File::delete($tempStateFile);
|
|
}
|
|
});
|
|
|
|
it('does not send email on initial run when conform and saves state', function () use (&$tempStateFile): void {
|
|
// GIVEN
|
|
$job = new CheckArcadiaDatabaseConformityJob($tempStateFile);
|
|
$healthChecker = mock(HealthChecker::class);
|
|
$healthyResult = new ArcadiaConformityResult(
|
|
status: HealthStatus::HEALTHY,
|
|
timestamp: Utc::now(),
|
|
checks: [
|
|
new DirectoryConformityCheck('brands', ComponentStatus::UP, 'MARQUE', 10),
|
|
],
|
|
);
|
|
|
|
$healthChecker->shouldReceive('checkArcadiaConformity')->once()->andReturn($healthyResult);
|
|
|
|
// WHEN
|
|
$job->handle(
|
|
$healthChecker,
|
|
app(Mailer::class),
|
|
app(HealthAlertMailConfig::class),
|
|
app(LoggerInterface::class),
|
|
);
|
|
|
|
// THEN
|
|
expect(File::exists($tempStateFile))->toBeTrue();
|
|
$saved = json_decode((string) File::get($tempStateFile), true);
|
|
expect($saved['status'])->toBe('healthy')
|
|
->and($saved['checksum'])->toBe('healthy');
|
|
Mail::assertNothingSent();
|
|
});
|
|
|
|
it('sends alert email on initial run when non-conform and saves error checksum', function () use (&$tempStateFile): void {
|
|
// GIVEN
|
|
$job = new CheckArcadiaDatabaseConformityJob($tempStateFile);
|
|
$healthChecker = mock(HealthChecker::class);
|
|
$unhealthyResult = new ArcadiaConformityResult(
|
|
status: HealthStatus::UNHEALTHY,
|
|
timestamp: Utc::now(),
|
|
checks: [
|
|
new DirectoryConformityCheck('brands', ComponentStatus::DOWN, 'MARQUE', 0, 'Column LIB_MARQ missing'),
|
|
],
|
|
);
|
|
|
|
$healthChecker->shouldReceive('checkArcadiaConformity')->once()->andReturn($unhealthyResult);
|
|
|
|
// WHEN
|
|
$job->handle(
|
|
$healthChecker,
|
|
app(Mailer::class),
|
|
app(HealthAlertMailConfig::class),
|
|
app(LoggerInterface::class),
|
|
);
|
|
|
|
// THEN
|
|
expect(File::exists($tempStateFile))->toBeTrue();
|
|
$saved = json_decode((string) File::get($tempStateFile), true);
|
|
expect($saved['status'])->toBe('unhealthy')
|
|
->and($saved['checksum'])->toBe($unhealthyResult->checksum());
|
|
|
|
Mail::assertSent(
|
|
ArcadiaConformityAlertMail::class,
|
|
static function (ArcadiaConformityAlertMail $mail) use ($unhealthyResult): bool {
|
|
return $mail->status === HealthStatus::UNHEALTHY
|
|
&& $mail->checksum === $unhealthyResult->checksum()
|
|
&& $mail->previousStatus === null;
|
|
}
|
|
);
|
|
});
|
|
|
|
it('suppresses alert email when error persists with identical checksum', function () use (&$tempStateFile): void {
|
|
// GIVEN: previously saved state with specific checksum
|
|
$unhealthyResult = new ArcadiaConformityResult(
|
|
status: HealthStatus::UNHEALTHY,
|
|
timestamp: Utc::now(),
|
|
checks: [
|
|
new DirectoryConformityCheck(
|
|
'brands',
|
|
ComponentStatus::DOWN,
|
|
'MARQUE',
|
|
0,
|
|
'Column LIB_MARQ missing'
|
|
),
|
|
],
|
|
);
|
|
|
|
File::put($tempStateFile, (string) json_encode([
|
|
'status' => 'unhealthy',
|
|
'checksum' => $unhealthyResult->checksum(),
|
|
'timestamp' => Utc::now()->format(\DateTimeInterface::RFC3339),
|
|
]));
|
|
|
|
$job = new CheckArcadiaDatabaseConformityJob($tempStateFile);
|
|
$healthChecker = mock(HealthChecker::class);
|
|
$healthChecker->shouldReceive('checkArcadiaConformity')->once()->andReturn($unhealthyResult);
|
|
|
|
// WHEN
|
|
$job->handle(
|
|
$healthChecker,
|
|
app(Mailer::class),
|
|
app(HealthAlertMailConfig::class),
|
|
app(LoggerInterface::class),
|
|
);
|
|
|
|
// THEN
|
|
Mail::assertNothingSent();
|
|
});
|
|
|
|
it('sends new alert email when error evolves with different checksum', function () use (&$tempStateFile): void {
|
|
// GIVEN: previous error checksum
|
|
$previousChecksum = 'old_error_checksum_hash_value_12345';
|
|
File::put($tempStateFile, (string) json_encode([
|
|
'status' => 'unhealthy',
|
|
'checksum' => $previousChecksum,
|
|
'timestamp' => Utc::now()->format(\DateTimeInterface::RFC3339),
|
|
]));
|
|
|
|
$newUnhealthyResult = new ArcadiaConformityResult(
|
|
status: HealthStatus::UNHEALTHY,
|
|
timestamp: Utc::now(),
|
|
checks: [
|
|
new DirectoryConformityCheck('brands', ComponentStatus::DOWN, 'MARQUE', 0, 'Column LIB_MARQ missing'),
|
|
new DirectoryConformityCheck('employees', ComponentStatus::DOWN, 'RU_PERSONNE_SIRH', 0, 'Table not found'),
|
|
],
|
|
);
|
|
|
|
$job = new CheckArcadiaDatabaseConformityJob($tempStateFile);
|
|
$healthChecker = mock(HealthChecker::class);
|
|
$healthChecker->shouldReceive('checkArcadiaConformity')->once()->andReturn($newUnhealthyResult);
|
|
|
|
// WHEN
|
|
$job->handle(
|
|
$healthChecker,
|
|
app(Mailer::class),
|
|
app(HealthAlertMailConfig::class),
|
|
app(LoggerInterface::class),
|
|
);
|
|
|
|
// THEN
|
|
Mail::assertSent(ArcadiaConformityAlertMail::class, static function (ArcadiaConformityAlertMail $mail) use ($newUnhealthyResult): bool {
|
|
return $mail->status === HealthStatus::UNHEALTHY
|
|
&& $mail->checksum === $newUnhealthyResult->checksum()
|
|
&& $mail->previousStatus === HealthStatus::UNHEALTHY;
|
|
});
|
|
});
|
|
|
|
it('sends recovery email when transitioning from unhealthy to healthy', function () use (&$tempStateFile): void {
|
|
// GIVEN: previous unhealthy state
|
|
File::put($tempStateFile, (string) json_encode([
|
|
'status' => 'unhealthy',
|
|
'checksum' => 'any_previous_error_checksum',
|
|
'timestamp' => Utc::now()->format(\DateTimeInterface::RFC3339),
|
|
]));
|
|
|
|
$healthyResult = new ArcadiaConformityResult(
|
|
status: HealthStatus::HEALTHY,
|
|
timestamp: Utc::now(),
|
|
checks: [
|
|
new DirectoryConformityCheck('brands', ComponentStatus::UP, 'MARQUE', 10),
|
|
],
|
|
);
|
|
|
|
$job = new CheckArcadiaDatabaseConformityJob($tempStateFile);
|
|
$healthChecker = mock(HealthChecker::class);
|
|
$healthChecker->shouldReceive('checkArcadiaConformity')->once()->andReturn($healthyResult);
|
|
|
|
// WHEN
|
|
$job->handle(
|
|
$healthChecker,
|
|
app(Mailer::class),
|
|
app(HealthAlertMailConfig::class),
|
|
app(LoggerInterface::class),
|
|
);
|
|
|
|
// THEN
|
|
Mail::assertSent(
|
|
ArcadiaConformityAlertMail::class,
|
|
static function (ArcadiaConformityAlertMail $mail): bool {
|
|
return $mail->status === HealthStatus::HEALTHY
|
|
&& $mail->checksum === 'healthy'
|
|
&& $mail->previousStatus === HealthStatus::UNHEALTHY;
|
|
}
|
|
);
|
|
});
|
|
});
|