active()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'name' => 'Aggregate Baseline', ]); $snapshot = BaselineSnapshot::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'baseline_profile_id' => (int) $profile->getKey(), ]); $profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]); BaselineTenantAssignment::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), ]); return [$user, $tenant, $profile, $snapshot]; } /** * @param array $attributes * @param array $compareContext */ function seedTenantGovernanceAggregateRun(Tenant $tenant, BaselineProfile $profile, BaselineSnapshot $snapshot, array $attributes = [], array $compareContext = []): OperationRun { return OperationRun::factory()->create(array_replace_recursive([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCompare->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Succeeded->value, 'completed_at' => now(), 'context' => [ 'baseline_profile_id' => (int) $profile->getKey(), 'baseline_snapshot_id' => (int) $snapshot->getKey(), 'baseline_compare' => array_replace_recursive([ 'reason_code' => BaselineCompareReasonCode::NoDriftDetected->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => [], 'proof' => true, ], ], $compareContext), ], ], $attributes)); } function createTenantGovernanceException(Tenant $tenant, Finding $finding, User $user, string $status, string $validityState, ?\Carbon\CarbonInterface $expiresAt = null): void { FindingException::query()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'finding_id' => (int) $finding->getKey(), 'requested_by_user_id' => (int) $user->getKey(), 'owner_user_id' => (int) $user->getKey(), 'approved_by_user_id' => (int) $user->getKey(), 'status' => $status, 'current_validity_state' => $validityState, 'request_reason' => 'Exception created for tenant governance aggregate coverage', 'approval_reason' => 'Approved for test coverage', 'requested_at' => now()->subDays(2), 'approved_at' => now()->subDay(), 'effective_from' => now()->subDay(), 'expires_at' => $expiresAt, 'review_due_at' => now()->addDay(), 'evidence_summary' => ['reference_count' => 0], ]); } it('resolves an unavailable governance aggregate when the assigned baseline has no snapshot', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'active_snapshot_id' => null, ]); BaselineTenantAssignment::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), ]); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->compareState)->toBe('no_snapshot') ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_UNAVAILABLE) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_LANDING) ->and($aggregate?->headline)->toBe('The current baseline snapshot is not available for compare.'); }); it('resolves an in-progress governance aggregate for queued compare runs', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun( tenant: $tenant, profile: $profile, snapshot: $snapshot, attributes: [ 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'completed_at' => null, ], ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->compareState)->toBe('comparing') ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_IN_PROGRESS) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_RUN) ->and($aggregate?->headline)->toBe('Baseline compare is in progress.'); }); it('resolves a failed governance aggregate with run follow-up', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun( tenant: $tenant, profile: $profile, snapshot: $snapshot, attributes: [ 'outcome' => OperationRunOutcome::Failed->value, 'failure_summary' => ['message' => 'Graph API timeout'], ], ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_RUN) ->and($aggregate?->nextActionLabel)->toBe('Review the failed run') ->and($aggregate?->headline)->toBe('The latest baseline compare failed before it produced a usable result.'); }); it('resolves an action-required aggregate when open drift findings remain', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun($tenant, $profile, $snapshot); Finding::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'finding_type' => Finding::FINDING_TYPE_DRIFT, 'source' => 'baseline.compare', 'scope_key' => 'baseline_profile:'.$profile->getKey(), 'severity' => Finding::SEVERITY_HIGH, 'status' => Finding::STATUS_NEW, ]); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED) ->and($aggregate?->visibleDriftFindingsCount)->toBe(1) ->and($aggregate?->highSeverityActiveFindingsCount)->toBe(1) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_FINDINGS); }); it('resolves overdue workflow pressure as action required even with zero visible drift', function (): void { [$user, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun($tenant, $profile, $snapshot); Finding::factory()->triaged()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), 'due_at' => now()->subDay(), ]); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED) ->and($aggregate?->overdueOpenFindingsCount)->toBe(1) ->and($aggregate?->visibleDriftFindingsCount)->toBe(0) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_FINDINGS) ->and($aggregate?->headline)->toContain('overdue finding'); }); it('resolves lapsed governance as action required even with zero visible drift', function (): void { [$user, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun($tenant, $profile, $snapshot); $finding = Finding::factory()->riskAccepted()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), ]); createTenantGovernanceException( tenant: $tenant, finding: $finding, user: $user, status: FindingException::STATUS_EXPIRED, validityState: FindingException::VALIDITY_EXPIRED, expiresAt: now()->subDay(), ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED) ->and($aggregate?->lapsedGovernanceCount)->toBe(1) ->and($aggregate?->visibleDriftFindingsCount)->toBe(0) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_FINDINGS) ->and($aggregate?->headline)->toContain('Accepted-risk governance has lapsed'); }); it('resolves expiring governance into the shared action-required contract', function (): void { [$user, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun($tenant, $profile, $snapshot); $finding = Finding::factory()->riskAccepted()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'tenant_id' => (int) $tenant->getKey(), ]); createTenantGovernanceException( tenant: $tenant, finding: $finding, user: $user, status: FindingException::STATUS_EXPIRING, validityState: FindingException::VALIDITY_EXPIRING, expiresAt: now()->addDays(2), ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_ACTION_REQUIRED) ->and($aggregate?->expiringGovernanceCount)->toBe(1) ->and($aggregate?->nextActionLabel)->toBe('Open findings'); }); it('resolves limited-confidence zero findings into a caution aggregate', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun( tenant: $tenant, profile: $profile, snapshot: $snapshot, attributes: [ 'outcome' => OperationRunOutcome::PartiallySucceeded->value, ], compareContext: [ 'reason_code' => BaselineCompareReasonCode::CoverageUnproven->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration', 'deviceCompliancePolicy'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => ['deviceCompliancePolicy'], 'proof' => false, ], ], ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_CAUTION) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_RUN) ->and($aggregate?->headline)->toBe('The last compare finished, but normal result output was suppressed.'); }); it('resolves stale no-drift compare results into a stale aggregate', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun( tenant: $tenant, profile: $profile, snapshot: $snapshot, attributes: [ 'completed_at' => now()->subDays(10), ], ); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_STALE) ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_LANDING) ->and($aggregate?->headline)->toBe('The latest baseline compare result is stale.'); }); it('resolves trustworthy no-drift results into a positive all-clear aggregate', function (): void { [, $tenant, $profile, $snapshot] = createTenantGovernanceAggregateTenant(); seedTenantGovernanceAggregateRun($tenant, $profile, $snapshot); $aggregate = app(TenantGovernanceAggregateResolver::class)->forTenant($tenant); expect($aggregate)->not->toBeNull() ->and($aggregate?->stateFamily)->toBe(BaselineCompareSummaryAssessment::STATE_POSITIVE) ->and($aggregate?->positiveClaimAllowed)->toBeTrue() ->and($aggregate?->nextActionTarget)->toBe(BaselineCompareSummaryAssessment::NEXT_TARGET_NONE) ->and($aggregate?->headline)->toBe('No confirmed drift in the latest baseline compare.'); });