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
235 lines
9.1 KiB
PHP
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();
|
|
}
|