compare( 'conditionalAccessPolicy', spec421ComparablePayload(['modifiedDateTime' => '2026-06-27T10:00:00Z']), spec421ComparablePayload(['modifiedDateTime' => '2026-06-27T11:00:00Z']), ); expect($result['changed'])->toBeFalse() ->and($result['classification'])->toBe('unchanged') ->and(collect($result['changes'])->pluck('classification'))->toContain('ignored_volatile'); }); it('Spec421 detects material Conditional Access changes with bounded importance', function (array $after, string $field, string $importance): void { $result = app(EntraCoverageComparator::class)->compare( 'conditionalAccessPolicy', spec421ComparablePayload(), spec421ComparablePayload($after), ); $change = collect($result['changes'])->firstWhere('field', $field); expect($result['changed'])->toBeTrue() ->and($result['classification'])->toBe('changed') ->and($change)->not->toBeNull() ->and($change['classification'])->toBe('changed') ->and($change['importance'])->toBe($importance); })->with([ 'state' => [['state' => 'disabled'], 'state', 'critical'], 'target users' => [['conditions' => ['users' => ['includeUsers' => ['All', 'group-a']]]], 'targets.users.include_users', 'important'], 'grant controls' => [['grantControls' => ['operator' => 'OR', 'builtInControls' => ['mfa', 'compliantDevice']]], 'grant_controls.built_in_controls', 'important'], 'session controls' => [['sessionControls' => ['signInFrequency' => ['value' => 4, 'type' => 'hours', 'isEnabled' => true]]], 'session_controls.signInFrequency.value', 'important'], ]); it('Spec421 records redacted and unsupported fields as non-material diagnostics', function (): void { $result = app(EntraCoverageComparator::class)->compare( 'conditionalAccessPolicy', spec421ComparablePayload(), spec421ComparablePayload(['clientSecret' => 'spec421-client-secret']), ); expect($result['changed'])->toBeFalse() ->and(collect($result['changes'])->pluck('classification'))->toContain('redacted', 'unsupported_field') ->and(json_encode($result, JSON_THROW_ON_ERROR))->not->toContain('spec421-client-secret'); }); function spec421ComparablePayload(array $overrides = []): array { return array_replace_recursive([ 'id' => 'cap-1', 'displayName' => 'Require MFA', 'state' => 'enabled', 'conditions' => [ 'users' => ['includeUsers' => ['All']], 'applications' => ['includeApplications' => ['Office365']], ], 'grantControls' => [ 'operator' => 'OR', 'builtInControls' => ['mfa'], ], 'sessionControls' => [ 'signInFrequency' => ['value' => 8, 'type' => 'hours', 'isEnabled' => true], ], ], $overrides); }