TenantAtlas/apps/platform/tests/Feature/Governance/CanonicalControlResolutionIntegrationTest.php
ahmido 6a5b8a3a11
Some checks failed
Main Confidence / confidence (push) Failing after 50s
feat: canonical control catalog foundation (#272)
## Summary
- add a config-seeded canonical control catalog plus shared resolution primitives and Microsoft subject bindings
- propagate canonical control references into findings-derived evidence snapshots and tenant review composition
- add the feature spec artifacts and focused Pest coverage, plus the supporting workspace and Sail helper adjustments included in this branch

## Testing
- cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Governance/CanonicalControlCatalogTest.php tests/Unit/Governance/CanonicalControlResolverTest.php tests/Feature/Governance/CanonicalControlResolutionIntegrationTest.php tests/Feature/Evidence/EvidenceSnapshotCanonicalControlReferenceTest.php tests/Feature/TenantReview/TenantReviewCanonicalControlReferenceTest.php tests/Feature/PlatformRelocation/CommandModelSmokeTest.php
- cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #272
2026-04-24 12:26:02 +00:00

76 lines
2.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Support\Governance\Controls\CanonicalControlCatalog;
use App\Support\Governance\Controls\CanonicalControlResolutionRequest;
use App\Support\Governance\Controls\CanonicalControlResolver;
it('lists seeded canonical controls in the logical contract shape', function (): void {
$payload = [
'controls' => app(CanonicalControlCatalog::class)->listPayload(),
];
expect($payload['controls'])->not->toBeEmpty();
foreach ($payload['controls'] as $control) {
expect($control)->toHaveKeys([
'control_key',
'name',
'domain_key',
'subdomain_key',
'control_class',
'summary',
'operator_description',
'detectability_class',
'evaluation_strategy',
'evidence_archetypes',
'artifact_suitability',
'historical_status',
])->and($control['artifact_suitability'])->toHaveKeys([
'baseline',
'drift',
'finding',
'exception',
'evidence',
'review',
'report',
]);
}
});
it('returns resolved, unresolved, and ambiguous resolution shapes without guessing', function (): void {
$resolver = app(CanonicalControlResolver::class);
$resolved = $resolver->resolve(new CanonicalControlResolutionRequest(
provider: 'microsoft',
consumerContext: 'review',
subjectFamilyKey: 'entra_admin_roles',
workload: 'entra',
signalKey: 'entra_admin_roles.global_admin_assignment',
))->toArray();
$unresolved = $resolver->resolve(new CanonicalControlResolutionRequest(
provider: 'microsoft',
consumerContext: 'review',
subjectFamilyKey: 'not_bound',
))->toArray();
$ambiguous = $resolver->resolve(new CanonicalControlResolutionRequest(
provider: 'microsoft',
consumerContext: 'review',
subjectFamilyKey: 'conditional_access_policy',
workload: 'entra',
))->toArray();
expect($resolved)->toHaveKeys(['status', 'control'])
->and($resolved['status'])->toBe('resolved')
->and($resolved['control']['control_key'])->toBe('privileged_access_governance')
->and($unresolved)->toHaveKeys(['status', 'reason_code', 'binding_context'])
->and($unresolved['reason_code'])->toBe('missing_binding')
->and($ambiguous)->toHaveKeys(['status', 'reason_code', 'candidate_control_keys', 'binding_context'])
->and($ambiguous['status'])->toBe('ambiguous')
->and($ambiguous['candidate_control_keys'])->toEqualCanonicalizing([
'conditional_access_enforcement',
'strong_authentication',
]);
});