1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.core.model.constraint;
7   
8   import static org.hamcrest.MatcherAssert.assertThat;
9   import static org.hamcrest.Matchers.hasItem;
10  import static org.hamcrest.Matchers.hasProperty;
11  import static org.hamcrest.Matchers.hasSize;
12  import static org.hamcrest.Matchers.is;
13  import static org.junit.jupiter.api.Assertions.assertAll;
14  import static org.junit.jupiter.api.Assertions.assertFalse;
15  import static org.junit.jupiter.api.Assertions.assertTrue;
16  import static org.mockito.ArgumentMatchers.any;
17  import static org.mockito.Mockito.doReturn;
18  import static org.mockito.Mockito.mock;
19  
20  import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
21  import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
22  import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
23  import gov.nist.secauto.metaschema.core.metapath.StaticContext;
24  import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem;
25  import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
26  import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
27  import gov.nist.secauto.metaschema.core.metapath.item.node.IFieldNodeItem;
28  import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem;
29  import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
30  import gov.nist.secauto.metaschema.core.model.IFlagDefinition;
31  import gov.nist.secauto.metaschema.core.model.IFlagInstance;
32  import gov.nist.secauto.metaschema.core.model.ISource;
33  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level;
34  import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
35  import gov.nist.secauto.metaschema.core.testsupport.MockedModelTestSupport;
36  import gov.nist.secauto.metaschema.core.testsupport.mocking.MockNodeItemFactory;
37  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
38  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
39  
40  import org.junit.jupiter.api.Test;
41  
42  import java.net.URI;
43  
44  import edu.umd.cs.findbugs.annotations.NonNull;
45  
46  /**
47   * Comprehensive tests for expect constraint validation.
48   */
49  @SuppressWarnings("PMD.TooManyStaticImports")
50  class ExpectConstraintTest {
51    @NonNull
52    private static final String NS = ObjectUtils.notNull(URI.create("http://example.com/ns").toASCIIString());
53  
54    @NonNull
55    private static IEnhancedQName qname(@NonNull String name) {
56      return IEnhancedQName.of(NS, name);
57    }
58  
59    /**
60     * Mock constraint methods on a flag definition to prevent null pointer
61     * exceptions during validation.
62     *
63     * @param flag
64     *          the flag node item whose definition to mock
65     */
66    @SuppressWarnings("null")
67    private static void mockFlagDefinitionConstraints(@NonNull IFlagNodeItem flag) {
68      IFlagDefinition definition = flag.getDefinition();
69      doReturn(CollectionUtil.emptyMap()).when(definition).getLetExpressions();
70      doReturn(CollectionUtil.emptyList()).when(definition).getAllowedValuesConstraints();
71      doReturn(CollectionUtil.emptyList()).when(definition).getExpectConstraints();
72      doReturn(CollectionUtil.emptyList()).when(definition).getMatchesConstraints();
73      doReturn(CollectionUtil.emptyList()).when(definition).getIndexHasKeyConstraints();
74    }
75  
76    /**
77     * Test expect constraint that passes when test expression evaluates to true.
78     *
79     * @throws ConstraintValidationException
80     *           if an error occurred during validation
81     */
82    @SuppressWarnings("null")
83    @Test
84    void testExpectConstraintPasses() throws ConstraintValidationException {
85      MockNodeItemFactory itemFactory = new MockNodeItemFactory();
86  
87      IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test-value"));
88  
89      IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
90  
91      ISource source = mock(ISource.class);
92  
93      // Create expect constraint with test that evaluates to true
94      IExpectConstraint expectConstraint = IExpectConstraint.builder()
95          .source(source)
96          .test(IMetapathExpression.compile("string-length(.) > 0"))
97          .build();
98  
99      doReturn(flagDefinition).when(flag).getDefinition();
100     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
101 
102     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
103     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
104     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
105     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
106     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
107 
108     StaticContext staticContext = StaticContext.instance();
109     doReturn(staticContext).when(source).getStaticContext();
110 
111     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
112     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
113     DynamicContext dynamicContext = new DynamicContext(staticContext);
114     validator.validate(flag, dynamicContext);
115     validator.finalizeValidation(dynamicContext);
116 
117     assertTrue(handler.isPassing(), "constraint should pass when test expression evaluates to true");
118   }
119 
120   /**
121    * Test expect constraint that fails when test expression evaluates to false.
122    *
123    * @throws ConstraintValidationException
124    *           if an error occurred during validation
125    */
126   @SuppressWarnings("null")
127   @Test
128   void testExpectConstraintFails() throws ConstraintValidationException {
129     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
130 
131     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test"));
132 
133     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
134 
135     ISource source = mock(ISource.class);
136 
137     // Create expect constraint with test that evaluates to false
138     IExpectConstraint expectConstraint = IExpectConstraint.builder()
139         .source(source)
140         .test(IMetapathExpression.compile("string-length(.) > 10"))
141         .build();
142 
143     doReturn(flagDefinition).when(flag).getDefinition();
144     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
145 
146     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
147     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
148     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
149     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
150     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
151 
152     StaticContext staticContext = StaticContext.instance();
153     doReturn(staticContext).when(source).getStaticContext();
154 
155     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
156     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
157     DynamicContext dynamicContext = new DynamicContext(staticContext);
158     validator.validate(flag, dynamicContext);
159     validator.finalizeValidation(dynamicContext);
160 
161     assertAll(
162         () -> assertFalse(handler.isPassing(), "constraint should fail when test expression evaluates to false"),
163         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
164         () -> assertThat("finding should be for the flag node", handler.getFindings(),
165             hasItem(hasProperty("node", is(flag)))));
166   }
167 
168   /**
169    * Test expect constraint with custom message.
170    *
171    * @throws ConstraintValidationException
172    *           if an error occurred during validation
173    */
174   @SuppressWarnings("null")
175   @Test
176   void testExpectConstraintWithCustomMessage() throws ConstraintValidationException {
177     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
178 
179     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("short"));
180 
181     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
182 
183     ISource source = mock(ISource.class);
184 
185     String customMessage = "Value must be at least 10 characters long";
186 
187     // Create expect constraint with custom message
188     IExpectConstraint expectConstraint = IExpectConstraint.builder()
189         .source(source)
190         .test(IMetapathExpression.compile("string-length(.) >= 10"))
191         .message(customMessage)
192         .build();
193 
194     doReturn(flagDefinition).when(flag).getDefinition();
195     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
196 
197     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
198     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
199     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
200     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
201     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
202 
203     StaticContext staticContext = StaticContext.instance();
204     doReturn(staticContext).when(source).getStaticContext();
205 
206     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
207     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
208     DynamicContext dynamicContext = new DynamicContext(staticContext);
209     validator.validate(flag, dynamicContext);
210     validator.finalizeValidation(dynamicContext);
211 
212     assertAll(
213         () -> assertFalse(handler.isPassing(), "constraint should fail"),
214         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
215         () -> assertThat("finding should contain custom message", handler.getFindings(),
216             hasItem(hasProperty("message", is(customMessage)))));
217   }
218 
219   /**
220    * Test expect constraint targeting specific Metapath using DM data model.
221    *
222    * @throws ConstraintValidationException
223    *           if an error occurred during validation
224    */
225   @Test
226   void testExpectConstraintWithTargetMetapath() throws ConstraintValidationException {
227     MockedModelTestSupport mocking = new MockedModelTestSupport();
228     ISource source = ISource.externalSource("https://example.com/module");
229 
230     // Create field definition with a "status" flag
231     IFieldDefinition fieldDef = mocking.field()
232         .qname(qname("item"))
233         .source(source)
234         .toDefinition();
235 
236     // Create flag instance on the field definition
237     IFlagInstance statusFlagInstance = mocking.flag()
238         .qname(IEnhancedQName.of("status"))
239         .source(source)
240         .toInstance(fieldDef);
241 
242     // Create a StaticContext for Metapath compilation
243     StaticContext staticContext = StaticContext.builder()
244         .defaultModelNamespace(NS)
245         .build();
246 
247     // Create field node with "status" flag set to "active"
248     IDMFieldNodeItem field = IDMFieldNodeItem.newInstance(fieldDef, IStringItem.valueOf("value"), staticContext);
249     field.newFlag(statusFlagInstance, IStringItem.valueOf("active"));
250 
251     // Create expect constraint targeting the @status flag, expecting 'inactive'
252     IExpectConstraint expectConstraint = IExpectConstraint.builder()
253         .source(source)
254         .target(IMetapathExpression.compile("@status", staticContext))
255         .test(IMetapathExpression.compile(". = 'inactive'", staticContext))
256         .build();
257     fieldDef.addConstraint(expectConstraint);
258 
259     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
260     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
261     DynamicContext dynamicContext = new DynamicContext(staticContext);
262     validator.validate(field, dynamicContext);
263     validator.finalizeValidation(dynamicContext);
264 
265     assertAll(
266         () -> assertFalse(handler.isPassing(), "constraint should fail when target test fails"),
267         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)));
268   }
269 
270   /**
271    * Test expect constraint with WARNING severity level.
272    *
273    * @throws ConstraintValidationException
274    *           if an error occurred during validation
275    */
276   @SuppressWarnings("null")
277   @Test
278   void testExpectConstraintWithWarningSeverity() throws ConstraintValidationException {
279     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
280 
281     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test"));
282 
283     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
284 
285     ISource source = mock(ISource.class);
286 
287     // Create expect constraint with WARNING level
288     IExpectConstraint expectConstraint = IExpectConstraint.builder()
289         .source(source)
290         .level(Level.WARNING)
291         .test(IMetapathExpression.compile("string-length(.) > 10"))
292         .build();
293 
294     doReturn(flagDefinition).when(flag).getDefinition();
295     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
296 
297     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
298     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
299     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
300     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
301     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
302 
303     StaticContext staticContext = StaticContext.instance();
304     doReturn(staticContext).when(source).getStaticContext();
305 
306     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
307     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
308     DynamicContext dynamicContext = new DynamicContext(staticContext);
309     validator.validate(flag, dynamicContext);
310     validator.finalizeValidation(dynamicContext);
311 
312     assertAll(
313         () -> assertTrue(handler.isPassing(), "validation should pass with WARNING level violation"),
314         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
315         () -> assertThat("finding should have WARNING severity", handler.getFindings(),
316             hasItem(hasProperty("severity", is(Level.WARNING)))));
317   }
318 
319   /**
320    * Test expect constraint with ERROR severity level.
321    *
322    * @throws ConstraintValidationException
323    *           if an error occurred during validation
324    */
325   @SuppressWarnings("null")
326   @Test
327   void testExpectConstraintWithErrorSeverity() throws ConstraintValidationException {
328     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
329 
330     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test"));
331 
332     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
333 
334     ISource source = mock(ISource.class);
335 
336     // Create expect constraint with ERROR level (default)
337     IExpectConstraint expectConstraint = IExpectConstraint.builder()
338         .source(source)
339         .level(Level.ERROR)
340         .test(IMetapathExpression.compile("string-length(.) > 10"))
341         .build();
342 
343     doReturn(flagDefinition).when(flag).getDefinition();
344     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
345 
346     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
347     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
348     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
349     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
350     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
351 
352     StaticContext staticContext = StaticContext.instance();
353     doReturn(staticContext).when(source).getStaticContext();
354 
355     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
356     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
357     DynamicContext dynamicContext = new DynamicContext(staticContext);
358     validator.validate(flag, dynamicContext);
359     validator.finalizeValidation(dynamicContext);
360 
361     assertAll(
362         () -> assertFalse(handler.isPassing(), "validation should fail with ERROR level violation"),
363         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
364         () -> assertThat("finding should have ERROR severity", handler.getFindings(),
365             hasItem(hasProperty("severity", is(Level.ERROR)))));
366   }
367 
368   /**
369    * Test expect constraint with CRITICAL severity level.
370    *
371    * @throws ConstraintValidationException
372    *           if an error occurred during validation
373    */
374   @SuppressWarnings("null")
375   @Test
376   void testExpectConstraintWithCriticalSeverity() throws ConstraintValidationException {
377     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
378 
379     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test"));
380 
381     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
382 
383     ISource source = mock(ISource.class);
384 
385     // Create expect constraint with CRITICAL level
386     IExpectConstraint expectConstraint = IExpectConstraint.builder()
387         .source(source)
388         .level(Level.CRITICAL)
389         .test(IMetapathExpression.compile("string-length(.) > 10"))
390         .build();
391 
392     doReturn(flagDefinition).when(flag).getDefinition();
393     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
394 
395     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
396     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
397     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
398     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
399     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
400 
401     StaticContext staticContext = StaticContext.instance();
402     doReturn(staticContext).when(source).getStaticContext();
403 
404     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
405     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
406     DynamicContext dynamicContext = new DynamicContext(staticContext);
407     validator.validate(flag, dynamicContext);
408     validator.finalizeValidation(dynamicContext);
409 
410     assertAll(
411         () -> assertFalse(handler.isPassing(), "validation should fail with CRITICAL level violation"),
412         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
413         () -> assertThat("finding should have CRITICAL severity", handler.getFindings(),
414             hasItem(hasProperty("severity", is(Level.CRITICAL)))));
415   }
416 
417   /**
418    * Test expect constraint with complex test expression.
419    *
420    * @throws ConstraintValidationException
421    *           if an error occurred during validation
422    */
423   @SuppressWarnings("null")
424   @Test
425   void testExpectConstraintWithComplexExpression() throws ConstraintValidationException {
426     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
427 
428     IFlagNodeItem flag = itemFactory.flag(qname("email"), IStringItem.valueOf("user@example.com"));
429 
430     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
431 
432     ISource source = mock(ISource.class);
433 
434     // Create expect constraint with complex test expression
435     IExpectConstraint expectConstraint = IExpectConstraint.builder()
436         .source(source)
437         .test(IMetapathExpression.compile("contains(., '@') and string-length(.) > 5"))
438         .build();
439 
440     doReturn(flagDefinition).when(flag).getDefinition();
441     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
442 
443     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
444     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
445     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
446     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
447     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
448 
449     StaticContext staticContext = StaticContext.instance();
450     doReturn(staticContext).when(source).getStaticContext();
451 
452     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
453     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
454     DynamicContext dynamicContext = new DynamicContext(staticContext);
455     validator.validate(flag, dynamicContext);
456     validator.finalizeValidation(dynamicContext);
457 
458     assertTrue(handler.isPassing(), "constraint should pass when complex expression evaluates to true");
459   }
460 
461   /**
462    * Test expect constraint with formal name and description.
463    *
464    * @throws ConstraintValidationException
465    *           if an error occurred during validation
466    */
467   @SuppressWarnings("null")
468   @Test
469   void testExpectConstraintWithMetadata() throws ConstraintValidationException {
470     MockNodeItemFactory itemFactory = new MockNodeItemFactory();
471 
472     IFlagNodeItem flag = itemFactory.flag(qname("value"), IStringItem.valueOf("test"));
473 
474     IFlagDefinition flagDefinition = mock(IFlagDefinition.class);
475 
476     ISource source = mock(ISource.class);
477 
478     // Create expect constraint with metadata
479     IExpectConstraint expectConstraint = IExpectConstraint.builder()
480         .source(source)
481         .identifier("expect-001")
482         .formalName("Minimum Length Constraint")
483         .description(MarkupLine.fromMarkdown("Ensures value has minimum length of 10 characters"))
484         .test(IMetapathExpression.compile("string-length(.) >= 10"))
485         .message("Value is too short")
486         .build();
487 
488     doReturn(flagDefinition).when(flag).getDefinition();
489     doReturn("flag/path").when(flag).toPath(any(IPathFormatter.class));
490 
491     doReturn(CollectionUtil.emptyMap()).when(flagDefinition).getLetExpressions();
492     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getAllowedValuesConstraints();
493     doReturn(CollectionUtil.singletonList(expectConstraint)).when(flagDefinition).getExpectConstraints();
494     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getMatchesConstraints();
495     doReturn(CollectionUtil.emptyList()).when(flagDefinition).getIndexHasKeyConstraints();
496 
497     StaticContext staticContext = StaticContext.instance();
498     doReturn(staticContext).when(source).getStaticContext();
499 
500     FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler();
501     DefaultConstraintValidator validator = new DefaultConstraintValidator(handler);
502     DynamicContext dynamicContext = new DynamicContext(staticContext);
503     validator.validate(flag, dynamicContext);
504     validator.finalizeValidation(dynamicContext);
505 
506     assertAll(
507         () -> assertFalse(handler.isPassing(), "constraint should fail"),
508         () -> assertThat("should have 1 finding", handler.getFindings(), hasSize(1)),
509         () -> assertThat("constraint should have id", expectConstraint.getId(), is("expect-001")),
510         () -> assertThat("constraint should have formal name", expectConstraint.getFormalName(),
511             is("Minimum Length Constraint")));
512   }
513 }