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; } ); }); });