## Summary - add tenant triage review-state persistence, fingerprinting, resolver logic, service layer, and migration for current affected-set tracking - surface review-state and affected-set progress across tenant registry, tenant dashboard arrival continuity, and workspace overview - extend RBAC, audit/badge support, specs, and test coverage for portfolio triage review-state workflows - suppress expected hidden-page background transport failures in the global unhandled rejection logger while keeping visible-page failures logged ## Validation - targeted Pest coverage added for tenant registry, workspace overview, arrival context, RBAC authorization, badges, fingerprinting, resolver behavior, and logger asset behavior - code formatted with `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` ## Notes - full suite was not re-run in this final step - branch includes the spec artifacts under `specs/189-portfolio-triage-review-state/` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #220
143 lines
4.4 KiB
PHP
143 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Database\Factories;
|
|
|
|
use App\Models\Tenant;
|
|
use App\Models\TenantTriageReview;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
|
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
|
|
/**
|
|
* @extends Factory<TenantTriageReview>
|
|
*/
|
|
class TenantTriageReviewFactory extends Factory
|
|
{
|
|
protected $model = TenantTriageReview::class;
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function definition(): array
|
|
{
|
|
$snapshot = [
|
|
'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
|
|
'concernState' => 'stale',
|
|
'reasonCode' => 'latest_backup_stale',
|
|
'severityKey' => 'latest_backup_stale',
|
|
'supportingKey' => 'latest_backup_stale',
|
|
];
|
|
|
|
return [
|
|
'tenant_id' => Tenant::factory()->for(Workspace::factory()),
|
|
'workspace_id' => function (array $attributes): int {
|
|
$tenantId = $attributes['tenant_id'] ?? null;
|
|
|
|
if (! is_numeric($tenantId)) {
|
|
return (int) Workspace::factory()->create()->getKey();
|
|
}
|
|
|
|
$tenant = Tenant::query()->whereKey((int) $tenantId)->first();
|
|
|
|
if (! $tenant instanceof Tenant || ! is_numeric($tenant->workspace_id)) {
|
|
return (int) Workspace::factory()->create()->getKey();
|
|
}
|
|
|
|
return (int) $tenant->workspace_id;
|
|
},
|
|
'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
|
|
'current_state' => TenantTriageReview::STATE_REVIEWED,
|
|
'reviewed_at' => now()->subMinutes(5),
|
|
'reviewed_by_user_id' => User::factory(),
|
|
'review_fingerprint' => $this->hashSnapshot($snapshot),
|
|
'review_snapshot' => $snapshot,
|
|
'last_seen_matching_at' => now()->subMinutes(5),
|
|
'resolved_at' => null,
|
|
];
|
|
}
|
|
|
|
public function reviewed(): static
|
|
{
|
|
return $this->state(fn (): array => [
|
|
'current_state' => TenantTriageReview::STATE_REVIEWED,
|
|
]);
|
|
}
|
|
|
|
public function followUpNeeded(): static
|
|
{
|
|
return $this->state(fn (): array => [
|
|
'current_state' => TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
|
]);
|
|
}
|
|
|
|
public function backupHealth(): static
|
|
{
|
|
return $this->state(function (): array {
|
|
$snapshot = [
|
|
'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
|
|
'concernState' => 'stale',
|
|
'reasonCode' => 'latest_backup_stale',
|
|
'severityKey' => 'latest_backup_stale',
|
|
'supportingKey' => 'latest_backup_stale',
|
|
];
|
|
|
|
return [
|
|
'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
|
|
'review_fingerprint' => $this->hashSnapshot($snapshot),
|
|
'review_snapshot' => $snapshot,
|
|
];
|
|
});
|
|
}
|
|
|
|
public function recoveryEvidence(): static
|
|
{
|
|
return $this->state(function (): array {
|
|
$snapshot = [
|
|
'concernFamily' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
|
|
'concernState' => 'weakened',
|
|
'reasonCode' => 'failed',
|
|
'severityKey' => 'failed',
|
|
'supportingKey' => 'failed',
|
|
];
|
|
|
|
return [
|
|
'concern_family' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
|
|
'review_fingerprint' => $this->hashSnapshot($snapshot),
|
|
'review_snapshot' => $snapshot,
|
|
];
|
|
});
|
|
}
|
|
|
|
public function resolved(): static
|
|
{
|
|
return $this->state(fn (): array => [
|
|
'resolved_at' => now()->subMinute(),
|
|
]);
|
|
}
|
|
|
|
public function active(): static
|
|
{
|
|
return $this->state(fn (): array => [
|
|
'resolved_at' => null,
|
|
]);
|
|
}
|
|
|
|
public function changedFingerprint(): static
|
|
{
|
|
return $this->state(fn (): array => [
|
|
'review_fingerprint' => hash('sha256', 'changed-fingerprint-'.fake()->uuid()),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $snapshot
|
|
*/
|
|
private function hashSnapshot(array $snapshot): string
|
|
{
|
|
return hash('sha256', json_encode($snapshot, JSON_THROW_ON_ERROR));
|
|
}
|
|
}
|