Automated PR provided by Codex via Gitea API. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #479
369 lines
15 KiB
PHP
369 lines
15 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\BackupSet;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\StoredReport;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\Operations\Actionability\OperationRunActionabilityResolver;
|
|
use App\Support\Operations\Actionability\OperationRunActionabilityStatus;
|
|
use App\Support\ReviewPacks\ReportProfileRegistry;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
it('resolves old provider connection blockers when the same connection is currently healthy in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$connection = ProviderConnection::factory()->verifiedHealthy()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'provider' => 'microsoft',
|
|
]);
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'provider.connection.check',
|
|
'outcome' => OperationRunOutcome::Blocked->value,
|
|
'context' => [
|
|
'provider' => 'microsoft',
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState)
|
|
->and($result->requiresCurrentFollowUp())->toBeFalse()
|
|
->and($result->resolvingModelType)->toBe('provider_connection')
|
|
->and(OperationRun::query()->whereKey($run->getKey())->terminalFollowUp()->exists())->toBeTrue()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse();
|
|
});
|
|
|
|
it('supersedes old provider blockers with later same-scope successful checks in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$connection = ProviderConnection::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'provider' => 'microsoft',
|
|
]);
|
|
|
|
$oldRun = spec367TerminalRun($tenant, [
|
|
'type' => 'provider.connection.check',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'completed_at' => now()->subHours(2),
|
|
'created_at' => now()->subHours(2),
|
|
'context' => [
|
|
'provider' => 'microsoft',
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
],
|
|
]);
|
|
|
|
$laterRun = spec367TerminalRun($tenant, [
|
|
'type' => 'provider.connection.check',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now()->subHour(),
|
|
'created_at' => now()->subHour(),
|
|
'context' => [
|
|
'provider' => 'microsoft',
|
|
'provider_connection_id' => (int) $connection->getKey(),
|
|
],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::SupersededByLaterSuccess)
|
|
->and($result->supersedingRunId)->toBe((int) $laterRun->getKey())
|
|
->and($result->requiresCurrentFollowUp())->toBeFalse();
|
|
});
|
|
|
|
it('does not supersede repeatable runs from a different scope or correlation in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$otherTenant = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
]);
|
|
|
|
$oldRun = spec367TerminalRun($tenant, [
|
|
'type' => 'inventory.sync',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'completed_at' => now()->subHours(2),
|
|
'created_at' => now()->subHours(2),
|
|
'context' => ['selection_hash' => 'selected-family-a'],
|
|
]);
|
|
|
|
spec367TerminalRun($otherTenant, [
|
|
'type' => 'inventory.sync',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now()->subHour(),
|
|
'created_at' => now()->subHour(),
|
|
'context' => ['selection_hash' => 'selected-family-a'],
|
|
]);
|
|
|
|
spec367TerminalRun($tenant, [
|
|
'type' => 'inventory.sync',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now()->subMinutes(30),
|
|
'created_at' => now()->subMinutes(30),
|
|
'context' => ['selection_hash' => 'selected-family-b'],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable)
|
|
->and($result->requiresCurrentFollowUp())->toBeTrue();
|
|
});
|
|
|
|
it('supersedes repeatable runs only with later same-scope success in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
|
|
$oldRun = spec367TerminalRun($tenant, [
|
|
'type' => 'inventory.sync',
|
|
'outcome' => OperationRunOutcome::PartiallySucceeded->value,
|
|
'completed_at' => now()->subHours(2),
|
|
'created_at' => now()->subHours(2),
|
|
'context' => ['selection_hash' => 'selected-family-a'],
|
|
]);
|
|
|
|
$laterRun = spec367TerminalRun($tenant, [
|
|
'type' => 'inventory.sync',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now()->subHour(),
|
|
'created_at' => now()->subHour(),
|
|
'context' => ['selection_hash' => 'selected-family-a'],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::SupersededByLaterSuccess)
|
|
->and($result->supersedingRunId)->toBe((int) $laterRun->getKey())
|
|
->and(OperationRun::query()->whereKey($oldRun->getKey())->currentTerminalFollowUp()->exists())->toBeFalse();
|
|
});
|
|
|
|
it('uses repository artifact proof to resolve terminal backup update history in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$backupSet = BackupSet::factory()->for($tenant)->recentCompleted()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
]);
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'backup_set.update',
|
|
'outcome' => OperationRunOutcome::Blocked->value,
|
|
'context' => [
|
|
'backup_set_id' => (int) $backupSet->getKey(),
|
|
],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState)
|
|
->and($result->resolvingModelType)->toBe('backup_set')
|
|
->and($result->resolvingModelId)->toBe((int) $backupSet->getKey())
|
|
->and($result->requiresCurrentFollowUp())->toBeFalse();
|
|
});
|
|
|
|
it('uses ready management PDF artifact proof to resolve terminal management report generation history in Spec412', function (): void {
|
|
Storage::fake('exports');
|
|
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
]);
|
|
$pdfBytes = spec367ManagementReportPdfBytes('Spec367 ready management report');
|
|
$filePath = sprintf('management-reports/spec367/%d-ready.pdf', (int) $pack->getKey());
|
|
Storage::disk('exports')->put($filePath, $pdfBytes);
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'report.management.generate',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'context' => [
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF,
|
|
],
|
|
]);
|
|
|
|
$report = StoredReport::factory()->managementReportPdf()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'operation_run_id' => null,
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'file_path' => $filePath,
|
|
'file_size' => strlen($pdfBytes),
|
|
'sha256' => hash('sha256', $pdfBytes),
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::ResolvedByCurrentState)
|
|
->and($result->resolvingModelType)->toBe('stored_report')
|
|
->and($result->resolvingModelId)->toBe((int) $report->getKey())
|
|
->and($result->requiresCurrentFollowUp())->toBeFalse()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse();
|
|
});
|
|
|
|
it('keeps terminal management report generation actionable when ready metadata points to a missing PDF in Spec412', function (): void {
|
|
Storage::fake('exports');
|
|
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
]);
|
|
$pdfBytes = spec367ManagementReportPdfBytes('Spec367 missing management report');
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'report.management.generate',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'context' => [
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF,
|
|
],
|
|
]);
|
|
|
|
StoredReport::factory()->managementReportPdf()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'operation_run_id' => null,
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'file_path' => sprintf('management-reports/spec367/%d-missing.pdf', (int) $pack->getKey()),
|
|
'file_size' => strlen($pdfBytes),
|
|
'sha256' => hash('sha256', $pdfBytes),
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable)
|
|
->and($result->requiresCurrentFollowUp())->toBeTrue()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeTrue();
|
|
});
|
|
|
|
it('keeps terminal management report generation actionable when the ready PDF belongs to another tenant scope in Spec412', function (): void {
|
|
Storage::fake('exports');
|
|
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$otherTenant = ManagedEnvironment::factory()->create();
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
]);
|
|
$pdfBytes = spec367ManagementReportPdfBytes('Spec367 cross tenant management report');
|
|
$filePath = sprintf('management-reports/spec367/%d-cross-tenant.pdf', (int) $pack->getKey());
|
|
Storage::disk('exports')->put($filePath, $pdfBytes);
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'report.management.generate',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'context' => [
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'report_type' => StoredReport::REPORT_TYPE_MANAGEMENT_REPORT_PDF,
|
|
],
|
|
]);
|
|
|
|
StoredReport::factory()->managementReportPdf()->create([
|
|
'workspace_id' => (int) $otherTenant->workspace_id,
|
|
'managed_environment_id' => (int) $otherTenant->getKey(),
|
|
'source_review_pack_id' => (int) $pack->getKey(),
|
|
'operation_run_id' => null,
|
|
'profile' => ReportProfileRegistry::CUSTOMER_EXECUTIVE,
|
|
'file_path' => $filePath,
|
|
'file_size' => strlen($pdfBytes),
|
|
'sha256' => hash('sha256', $pdfBytes),
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::Actionable)
|
|
->and($result->requiresCurrentFollowUp())->toBeTrue()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeTrue();
|
|
});
|
|
|
|
it('keeps high-risk terminal operations in manual review even when later runs succeed in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
|
|
$oldRun = spec367TerminalRun($tenant, [
|
|
'type' => 'restore.execute',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'completed_at' => now()->subHours(2),
|
|
'created_at' => now()->subHours(2),
|
|
]);
|
|
|
|
spec367TerminalRun($tenant, [
|
|
'type' => 'restore.execute',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'completed_at' => now()->subHour(),
|
|
'created_at' => now()->subHour(),
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($oldRun->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::RequiresManualReview)
|
|
->and($result->requiresCurrentFollowUp())->toBeTrue()
|
|
->and(OperationRun::query()->whereKey($oldRun->getKey())->currentTerminalFollowUp()->exists())->toBeTrue();
|
|
});
|
|
|
|
it('keeps reconciled successful terminal history out of current follow-up in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'restore.execute',
|
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
|
'context' => [
|
|
'reconciliation' => [
|
|
'reconciled_at' => now()->toIso8601String(),
|
|
'decision' => 'completed',
|
|
'source' => 'restore_run_observer',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::NotTerminal)
|
|
->and($result->requiresCurrentFollowUp())->toBeFalse()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->terminalFollowUp()->exists())->toBeTrue()
|
|
->and(OperationRun::query()->whereKey($run->getKey())->currentTerminalFollowUp()->exists())->toBeFalse();
|
|
});
|
|
|
|
it('fails closed for unknown terminal operation types in Spec367', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
|
|
$run = spec367TerminalRun($tenant, [
|
|
'type' => 'unknown.operation',
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
]);
|
|
|
|
$result = app(OperationRunActionabilityResolver::class)->evaluate($run->fresh());
|
|
|
|
expect($result->status)->toBe(OperationRunActionabilityStatus::RequiresManualReview)
|
|
->and($result->reasonCode)->toBe('unknown_operation_type')
|
|
->and($result->requiresCurrentFollowUp())->toBeTrue();
|
|
});
|
|
|
|
/**
|
|
* @param array<string, mixed> $attributes
|
|
*/
|
|
function spec367TerminalRun(ManagedEnvironment $tenant, array $attributes = []): OperationRun
|
|
{
|
|
$completedAt = $attributes['completed_at'] ?? now()->subMinutes(5);
|
|
|
|
return OperationRun::factory()->forTenant($tenant)->create(array_replace([
|
|
'type' => 'policy.sync',
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'created_at' => $completedAt,
|
|
'completed_at' => $completedAt,
|
|
'context' => [],
|
|
], $attributes));
|
|
}
|
|
|
|
function spec367ManagementReportPdfBytes(string $label): string
|
|
{
|
|
return "%PDF-1.7\n1 0 obj\n<< /Type /Catalog /Title ({$label}) >>\nendobj\nstartxref\n0\n%%EOF";
|
|
}
|