TenantAtlas/apps/platform/tests/Browser/Spec359ReviewComposeReconciliationSmokeTest.php
ahmido 3a750726fd feat: implement review compose reconciliation adapter (spec 359) (#430)
Spec 359: add a narrow review-compose reconciliation path, deterministic duplicate/superseded recovery, shared review truth resolution, and bounded unit/feature/browser coverage. PGSQL validation remains locally blocked because the pgsql host/Docker runtime was unavailable.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #430
2026-06-06 14:58:16 +00:00

235 lines
9.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\User;
use App\Services\OperationRunService;
use App\Support\EnvironmentReviewStatus;
use App\Support\OperationRunLinks;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
pest()->browser()->timeout(60_000);
it('Spec359 smokes attention-required review-compose guidance on the operations hub', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
spec359AuthenticateBrowser($this, $user, $environment);
$run = spec359BrowserCreateReconciledReviewComposeRun(
environment: $environment,
user: $user,
decision: 'attention_required',
outcome: OperationRunOutcome::Failed->value,
reasonCode: 'review.compose.ambiguous_truth',
reasonMessage: 'Multiple matching reviews are available, so this run needs manual review.',
failures: [
[
'code' => 'review.compose.ambiguous_truth',
'message' => 'Multiple matching reviews are available, so this run needs manual review.',
],
],
);
visit(OperationRunLinks::index($environment))
->resize(1440, 1100)
->waitForText('Operations Hub')
->assertSee('Inspect the recorded review lineage before retrying.')
->assertSee('Failed')
->assertSee('Completed')
->assertSee($environment->name)
->assertDontSee('SQLSTATE')
->assertDontSee('duplicate key')
->assertDontSee('environment_reviews_fingerprint_mutable_unique')
->assertDontSee('worker crash')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(OperationRunLinks::tenantlessView($run))
->waitForText('Monitoring detail')
->assertSee('Automatically reconciled')
->assertSee('TenantPilot found matching review activity, but it could not resolve the final review truth safely.')
->assertSee('Inspect the recorded review lineage before retrying.')
->assertDontSee('SQLSTATE')
->assertDontSee('duplicate key')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
});
it('Spec359 smokes matching-review reconciliation wording on the tenantless run detail', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
spec359AuthenticateBrowser($this, $user, $environment);
$review = spec359BrowserCreatePublishedMatchingReview($environment, $user, 'spec359-browser-reused');
$run = spec359BrowserCreateReconciledReviewComposeRun(
environment: $environment,
user: $user,
decision: 'reconciled_succeeded',
outcome: OperationRunOutcome::Succeeded->value,
reasonCode: 'review.compose.matching_review_available',
reasonMessage: 'A matching review was already available for this run.',
relatedReview: $review,
evidence: [
'fingerprint' => 'spec359-browser-reused',
],
);
visit(OperationRunLinks::tenantlessView($run))
->resize(1440, 1100)
->waitForText('Monitoring detail')
->assertSee(OperationRunLinks::identifier($run))
->assertSee('A matching review was already available.')
->assertSee('No action needed. A matching review was already available.')
->assertSee('Automatically reconciled')
->assertSee(sprintf('TenantPilot confirmed review #%d and closed this run from existing review truth.', (int) $review->getKey()))
->assertDontSee('SQLSTATE')
->assertDontSee('duplicate key')
->assertDontSee('environment_reviews_fingerprint_mutable_unique')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
});
it('Spec359 smokes duplicate-recovered review-compose wording without leaking raw constraint text', function (): void {
[$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
spec359AuthenticateBrowser($this, $user, $environment);
$review = spec359BrowserCreatePublishedMatchingReview($environment, $user, 'spec359-browser-duplicate');
$run = spec359BrowserCreateReconciledReviewComposeRun(
environment: $environment,
user: $user,
decision: 'reconciled_succeeded',
outcome: OperationRunOutcome::Succeeded->value,
reasonCode: 'review.compose.duplicate_recovered',
reasonMessage: 'A duplicate compose attempt was recovered from matching review truth.',
relatedReview: $review,
evidence: [
'fingerprint' => 'spec359-browser-duplicate',
'duplicate_recovered' => true,
],
);
visit(OperationRunLinks::tenantlessView($run))
->resize(1440, 1100)
->waitForText('Monitoring detail')
->assertSee('A matching review was already available.')
->assertSee('No action needed. A matching review was already available.')
->assertSee(sprintf('TenantPilot confirmed review #%d and closed this run from existing review truth.', (int) $review->getKey()))
->assertDontSee('duplicate key')
->assertDontSee('environment_reviews_fingerprint_mutable_unique')
->assertDontSee('SQLSTATE')
->assertDontSee('23505')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
});
function spec359AuthenticateBrowser(mixed $test, User $user, ManagedEnvironment $environment): void
{
$workspaceId = (int) $environment->workspace_id;
$test->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => $workspaceId,
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $workspaceId => (int) $environment->getKey(),
],
]);
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
(string) $workspaceId => (int) $environment->getKey(),
]);
setAdminPanelContext($environment);
}
function spec359BrowserCreatePublishedMatchingReview(
ManagedEnvironment $environment,
User $user,
string $fingerprint,
): EnvironmentReview {
$snapshot = seedEnvironmentReviewEvidence($environment, operationRunCount: 1);
$publishedRun = OperationRun::factory()->forTenant($environment)->create([
'user_id' => (int) $user->getKey(),
'initiator_name' => $user->name,
'type' => 'environment.review.compose',
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Succeeded->value,
'completed_at' => now()->subMinutes(5),
'context' => [
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'review_fingerprint' => $fingerprint,
],
]);
return EnvironmentReview::factory()->published()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'operation_run_id' => (int) $publishedRun->getKey(),
'fingerprint' => $fingerprint,
'status' => EnvironmentReviewStatus::Published->value,
'published_by_user_id' => (int) $user->getKey(),
]);
}
function spec359BrowserCreateReconciledReviewComposeRun(
ManagedEnvironment $environment,
User $user,
string $decision,
string $outcome,
string $reasonCode,
string $reasonMessage,
?EnvironmentReview $relatedReview = null,
array $failures = [],
array $evidence = [],
): OperationRun {
$fingerprint = (string) ($evidence['fingerprint'] ?? 'spec359-browser-run');
$run = OperationRun::factory()->forTenant($environment)->create([
'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) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'review_fingerprint' => $fingerprint,
],
]);
return app(OperationRunService::class)->updateRunWithReconciliation(
run: $run,
status: OperationRunStatus::Completed->value,
outcome: $outcome,
summaryCounts: ['finding_count' => 2],
failures: $failures,
reasonCode: $reasonCode,
reasonMessage: $reasonMessage,
source: 'adapter_reconciler',
evidence: $evidence,
adapter: 'environment_review_compose',
decision: $decision,
related: $relatedReview instanceof EnvironmentReview
? [
'review' => [
'id' => (int) $relatedReview->getKey(),
],
]
: [],
)->fresh();
}