67 lines
2.9 KiB
PHP
67 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\EnvironmentReview;
|
|
use App\Models\OperationRun;
|
|
use App\Services\AdapterRunReconciler;
|
|
use App\Services\EnvironmentReviews\EnvironmentReviewFingerprint;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('finalizes unique-write review compose failures through the canonical adapter seam in Spec360', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$snapshot = seedEnvironmentReviewEvidence($tenant);
|
|
$fingerprint = app(EnvironmentReviewFingerprint::class)->forSnapshot($tenant, $snapshot);
|
|
|
|
$run = OperationRun::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'initiator_name' => $user->name,
|
|
'type' => 'environment.review.compose',
|
|
'status' => OperationRunStatus::Queued->value,
|
|
'outcome' => OperationRunOutcome::Pending->value,
|
|
'context' => [
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'review_fingerprint' => $fingerprint,
|
|
],
|
|
]);
|
|
|
|
$review = EnvironmentReview::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'fingerprint' => $fingerprint,
|
|
'status' => EnvironmentReviewStatus::Draft->value,
|
|
]);
|
|
|
|
$previous = new \PDOException('duplicate key value violates unique constraint');
|
|
$previous->errorInfo = ['23505'];
|
|
$exception = new QueryException('pgsql', 'insert into environment_reviews ...', [], $previous);
|
|
|
|
$change = app(AdapterRunReconciler::class)->reconcileOperationRunFailure($run, $exception);
|
|
|
|
$run->refresh();
|
|
$review->refresh();
|
|
|
|
expect($change['applied'] ?? null)->toBeTrue()
|
|
->and($run->status)->toBe(OperationRunStatus::Completed->value)
|
|
->and($run->outcome)->toBe(OperationRunOutcome::Failed->value)
|
|
->and($run->reconciliationAdapter())->toBe('environment_review_compose')
|
|
->and($run->reconciliationDecision())->toBe('attention_required')
|
|
->and(data_get($run->context, 'reconciliation.reason_code'))->toBe('run.adapter_out_of_sync')
|
|
->and((string) data_get($run->failure_summary, '0.message'))->not->toContain('SQLSTATE')
|
|
->and((string) data_get($run->failure_summary, '0.message'))->not->toContain('duplicate key')
|
|
->and($review->status)->toBe(EnvironmentReviewStatus::Draft->value);
|
|
});
|