@@ -112,6 +112,95 @@ public function testResolveAppliesRequestOverrideWhenAllowed(): void {
112112 $ this ->assertNull ($ resolved ->getBlockedBy ());
113113 }
114114
115+ public function testResolveValueChoiceUnionsConflictingGroupValues (): void {
116+ $ source = new InMemoryPolicySource ();
117+ $ source ->systemLayer = (new PolicyLayer ())
118+ ->setScope ('system ' )
119+ ->setValue ('none ' )
120+ ->setAllowChildOverride (true )
121+ ->setVisibleToChild (true );
122+ $ source ->groupLayers = [
123+ (new PolicyLayer ())
124+ ->setScope ('group ' )
125+ ->setValue ('ordered_numeric ' )
126+ ->setAllowChildOverride (false )
127+ ->setVisibleToChild (true )
128+ ->setAllowedValues (['ordered_numeric ' ]),
129+ (new PolicyLayer ())
130+ ->setScope ('group ' )
131+ ->setValue ('parallel ' )
132+ ->setAllowChildOverride (false )
133+ ->setVisibleToChild (true )
134+ ->setAllowedValues (['parallel ' ]),
135+ ];
136+
137+ $ resolver = new DefaultPolicyResolver ($ source );
138+ $ resolved = $ resolver ->resolve ($ this ->getValueChoiceDefinition (), PolicyContext::fromUserId ('john ' ));
139+
140+ $ this ->assertSame ('parallel ' , $ resolved ->getEffectiveValue ());
141+ $ this ->assertSame ('group ' , $ resolved ->getSourceScope ());
142+ $ this ->assertSame (['parallel ' , 'ordered_numeric ' ], $ resolved ->getAllowedValues ());
143+ $ this ->assertTrue ($ resolved ->isEditableByCurrentActor ());
144+ $ this ->assertTrue ($ resolved ->canSaveAsUserDefault ());
145+ $ this ->assertTrue ($ resolved ->canUseAsRequestOverride ());
146+ }
147+
148+ public function testResolveValueChoiceLetsCustomizableGroupBroadenFixedGroupChoice (): void {
149+ $ source = new InMemoryPolicySource ();
150+ $ source ->systemLayer = (new PolicyLayer ())
151+ ->setScope ('system ' )
152+ ->setValue ('none ' )
153+ ->setAllowChildOverride (true )
154+ ->setVisibleToChild (true );
155+ $ source ->groupLayers = [
156+ (new PolicyLayer ())
157+ ->setScope ('group ' )
158+ ->setValue ('ordered_numeric ' )
159+ ->setAllowChildOverride (false )
160+ ->setVisibleToChild (true )
161+ ->setAllowedValues (['ordered_numeric ' ]),
162+ (new PolicyLayer ())
163+ ->setScope ('group ' )
164+ ->setValue ('ordered_numeric ' )
165+ ->setAllowChildOverride (true )
166+ ->setVisibleToChild (true )
167+ ->setAllowedValues ([]),
168+ ];
169+
170+ $ resolver = new DefaultPolicyResolver ($ source );
171+ $ resolved = $ resolver ->resolve ($ this ->getValueChoiceDefinition (), PolicyContext::fromUserId ('john ' ));
172+
173+ $ this ->assertSame ('ordered_numeric ' , $ resolved ->getEffectiveValue ());
174+ $ this ->assertSame (['parallel ' , 'ordered_numeric ' ], $ resolved ->getAllowedValues ());
175+ $ this ->assertTrue ($ resolved ->canUseAsRequestOverride ());
176+ }
177+
178+ public function testResolveDoesNotApplyGroupValueWhenSystemBlocksOverride (): void {
179+ $ source = new InMemoryPolicySource ();
180+ $ source ->systemLayer = (new PolicyLayer ())
181+ ->setScope ('system ' )
182+ ->setValue ('ordered_numeric ' )
183+ ->setAllowChildOverride (false )
184+ ->setVisibleToChild (true )
185+ ->setAllowedValues (['ordered_numeric ' ]);
186+ $ source ->groupLayers = [
187+ (new PolicyLayer ())
188+ ->setScope ('group ' )
189+ ->setValue ('parallel ' )
190+ ->setAllowChildOverride (true )
191+ ->setVisibleToChild (true )
192+ ->setAllowedValues (['parallel ' , 'ordered_numeric ' ]),
193+ ];
194+
195+ $ resolver = new DefaultPolicyResolver ($ source );
196+ $ resolved = $ resolver ->resolve ($ this ->getDefinition (), PolicyContext::fromUserId ('john ' ));
197+
198+ $ this ->assertSame ('ordered_numeric ' , $ resolved ->getEffectiveValue ());
199+ $ this ->assertSame ('system ' , $ resolved ->getSourceScope ());
200+ $ this ->assertSame (['ordered_numeric ' ], $ resolved ->getAllowedValues ());
201+ $ this ->assertFalse ($ resolved ->canUseAsRequestOverride ());
202+ }
203+
115204 public function testResolveIgnoresCircleLayersInCurrentPhase (): void {
116205 $ source = new InMemoryPolicySource ();
117206 $ source ->systemLayer = (new PolicyLayer ())
@@ -148,6 +237,15 @@ private function getDefinition(): PolicySpec {
148237 allowedValues: ['none ' , 'parallel ' , 'ordered_numeric ' ],
149238 );
150239 }
240+
241+ private function getValueChoiceDefinition (): PolicySpec {
242+ return new PolicySpec (
243+ key: 'signature_flow ' ,
244+ defaultSystemValue: 'none ' ,
245+ allowedValues: ['none ' , 'parallel ' , 'ordered_numeric ' ],
246+ resolutionMode: PolicySpec::RESOLUTION_MODE_VALUE_CHOICE ,
247+ );
248+ }
151249}
152250
153251final class InMemoryPolicySource implements IPolicySource {
0 commit comments