compare( 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), Spec425::fixture('conditional-access', $fixture), ); $change = collect($result['changes'])->firstWhere('field', $field); expect($result['changed'])->toBeTrue() ->and($result['classification'])->toBe('changed') ->and($change)->not->toBeNull() ->and($change['importance'])->toBe($importance); })->with([ 'state' => ['state-change', 'state', 'critical'], 'grant controls' => ['grant-controls-change', 'grant_controls.built_in_controls', 'important'], 'included actors' => ['included-actor-change', 'targets.users.include_groups', 'important'], 'excluded actors' => ['excluded-actor-change', 'targets.users.exclude_groups', 'important'], 'app targeting' => ['app-targeting-change', 'targets.applications.exclude_applications', 'important'], 'conditions' => ['condition-change', 'conditions.sign_in_risk_levels', 'important'], 'device conditions' => ['device-condition-change', 'conditions.devices.device_filter.rule', 'important'], 'session controls' => ['session-control-change', 'session_controls.persistentBrowser', 'important'], ]); it('Spec425 treats Conditional Access volatile-only differences as non-material', function (): void { $result = app(EntraCoverageComparator::class)->compare( 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), Spec425::fixture('conditional-access', 'volatile-only-change'), ); expect($result['changed'])->toBeFalse() ->and($result['classification'])->toBe('unchanged') ->and(collect($result['changes'])->pluck('classification'))->toContain('ignored_volatile'); }); it('Spec425 keeps Conditional Access unsupported and redacted fields diagnostic and secret-free', function (): void { $unsupported = app(EntraCoverageComparator::class)->compare( 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), Spec425::fixture('conditional-access', 'unsupported-field'), ); $redacted = app(EntraCoverageComparator::class)->compare( 'conditionalAccessPolicy', Spec425::fixture('conditional-access', 'no-change'), Spec425::fixture('conditional-access', 'redaction'), ); expect(collect($unsupported['changes'])->pluck('classification'))->toContain('unsupported_field') ->and(collect($unsupported['changes'])->pluck('field'))->toContain('conditions.devices.deviceFilter.previewRuleId') ->and($unsupported['changed'])->toBeFalse() ->and(collect($redacted['changes'])->pluck('classification'))->toContain('redacted', 'unsupported_field') ->and(json_encode($redacted, JSON_THROW_ON_ERROR)) ->not->toContain('spec425-ca-secret') ->not->toContain('spec425-ca-token'); });