TenantAtlas/tests/Feature/Baselines/Support/BaselineSubjectResolutionFixtures.php
ahmido c17255f854 feat: implement baseline subject resolution semantics (#193)
## Summary
- add the structured subject-resolution foundation for baseline compare and baseline capture, including capability guards, subject descriptors, resolution outcomes, and operator action categories
- persist structured evidence-gap subject records and update compare/capture surfaces, landing projections, and cleanup tooling to use the new contract
- add Spec 163 artifacts and focused Pest coverage for classification, determinism, cleanup, and DB-only rendering

## Validation
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Unit/Support/Baselines tests/Feature/Baselines tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php`

## Notes
- verified locally that a fresh post-restart baseline compare run now writes structured `baseline_compare.evidence_gaps.subjects` records instead of the legacy broad payload shape
- excluded the separate `docs/product/spec-candidates.md` worktree change from this branch commit and PR

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #193
2026-03-25 12:40:45 +00:00

86 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Baselines\Support;
use App\Support\Baselines\OperatorActionCategory;
use App\Support\Baselines\ResolutionOutcome;
use App\Support\Baselines\ResolutionPath;
use App\Support\Baselines\SubjectClass;
final class BaselineSubjectResolutionFixtures
{
/**
* @param array<string, mixed> $overrides
* @return array<string, mixed>
*/
public static function structuredGap(array $overrides = []): array
{
return array_replace([
'policy_type' => 'deviceConfiguration',
'subject_external_id' => 'subject-1',
'subject_key' => 'deviceconfiguration|subject-1',
'subject_class' => SubjectClass::PolicyBacked->value,
'resolution_path' => ResolutionPath::Policy->value,
'resolution_outcome' => ResolutionOutcome::PolicyRecordMissing->value,
'reason_code' => 'policy_record_missing',
'operator_action_category' => OperatorActionCategory::RunPolicySyncOrBackup->value,
'structural' => false,
'retryable' => false,
'source_model_expected' => 'policy',
'source_model_found' => null,
], $overrides);
}
/**
* @param list<array<string, mixed>> $subjects
* @param array<string, mixed> $overrides
* @return array<string, mixed>
*/
public static function compareContext(array $subjects, array $overrides = []): array
{
$byReason = [];
foreach ($subjects as $subject) {
$reasonCode = is_string($subject['reason_code'] ?? null) ? $subject['reason_code'] : 'unknown';
$byReason[$reasonCode] = ($byReason[$reasonCode] ?? 0) + 1;
}
return array_replace_recursive([
'baseline_compare' => [
'evidence_gaps' => [
'count' => count($subjects),
'by_reason' => $byReason,
'subjects' => $subjects,
],
],
], $overrides);
}
/**
* @param list<array<string, mixed>> $subjects
* @param array<string, mixed> $overrides
* @return array<string, mixed>
*/
public static function captureContext(array $subjects, array $overrides = []): array
{
$byReason = [];
foreach ($subjects as $subject) {
$reasonCode = is_string($subject['reason_code'] ?? null) ? $subject['reason_code'] : 'unknown';
$byReason[$reasonCode] = ($byReason[$reasonCode] ?? 0) + 1;
}
return array_replace_recursive([
'baseline_capture' => [
'gaps' => [
'count' => count($subjects),
'by_reason' => $byReason,
'subjects' => $subjects,
],
],
], $overrides);
}
}