@@ -1547,4 +1547,119 @@ public void singlePredicate_stringMatcher_safeRegex_matches() {
15471547 when (context .getMetadata ()).thenReturn (headers );
15481548 assertThat (evaluator .evaluate (context )).isTrue ();
15491549 }
1550+
1551+
1552+
1553+ @ Test
1554+ public void compoundMatchers_tooFewPredicates_throws () {
1555+ // Tests OrMatcher/AndMatcher minimum size (2 predicates)
1556+ Matcher .MatcherList .Predicate p = createHeaderMatchPredicate ("h" , "v" );
1557+ Matcher .MatcherList .Predicate .PredicateList list =
1558+ Matcher .MatcherList .Predicate .PredicateList .newBuilder ().addPredicate (p ).build ();
1559+
1560+ try {
1561+ PredicateEvaluator .fromProto (
1562+ Matcher .MatcherList .Predicate .newBuilder ().setOrMatcher (list ).build ());
1563+ org .junit .Assert .fail ();
1564+ } catch (IllegalArgumentException e ) {
1565+ assertThat (e ).hasMessageThat ().contains ("OrMatcher must have at least 2 predicates" );
1566+ }
1567+
1568+ try {
1569+ PredicateEvaluator .fromProto (
1570+ Matcher .MatcherList .Predicate .newBuilder ().setAndMatcher (list ).build ());
1571+ org .junit .Assert .fail ();
1572+ } catch (IllegalArgumentException e ) {
1573+ assertThat (e ).hasMessageThat ().contains ("AndMatcher must have at least 2 predicates" );
1574+ }
1575+ }
1576+
1577+ @ Test
1578+ public void notMatcher_invertsResult () {
1579+ // Tests NotMatcher coverage
1580+ Matcher .MatcherList .Predicate p = createHeaderMatchPredicate ("h" , "v" );
1581+ PredicateEvaluator eval = PredicateEvaluator .fromProto (
1582+ Matcher .MatcherList .Predicate .newBuilder ().setNotMatcher (p ).build ());
1583+
1584+ // h:v matches -> Not(True) -> False
1585+ assertThat (eval .evaluate (mockContextWith ("h" , "v" ))).isFalse ();
1586+ // h:wrong doesn't match -> Not(False) -> True
1587+ assertThat (eval .evaluate (mockContextWith ("h" , "wrong" ))).isTrue ();
1588+ }
1589+
1590+ private MatchContext mockContextWith (String key , String value ) {
1591+ MatchContext context = mock (MatchContext .class );
1592+ Metadata headers = new Metadata ();
1593+ headers .put (Metadata .Key .of (key , Metadata .ASCII_STRING_MARSHALLER ), value );
1594+ when (context .getMetadata ()).thenReturn (headers );
1595+ return context ;
1596+ }
1597+
1598+
1599+ @ Test
1600+ public void singlePredicate_stringMatcher_suffix_matches () {
1601+ // Verifies valid suffix config
1602+ Matcher .MatcherList .Predicate .SinglePredicate predicate =
1603+ Matcher .MatcherList .Predicate .SinglePredicate .newBuilder ()
1604+ .setInput (TypedExtensionConfig .newBuilder ()
1605+ .setTypedConfig (Any .pack (
1606+ io .envoyproxy .envoy .type .matcher .v3 .HttpRequestHeaderMatchInput .newBuilder ()
1607+ .setHeaderName ("host" ).build ())))
1608+ .setValueMatch (com .github .xds .type .matcher .v3 .StringMatcher .newBuilder ()
1609+ .setSuffix ("bar" ))
1610+ .build ();
1611+
1612+ PredicateEvaluator evaluator = PredicateEvaluator .fromProto (
1613+ Matcher .MatcherList .Predicate .newBuilder ().setSinglePredicate (predicate ).build ());
1614+
1615+ // "foobar" ends with "bar" -> true
1616+ assertThat (evaluator .evaluate (mockContextWith ("host" , "foobar" ))).isTrue ();
1617+ // "foobaz" does not end with "bar" -> false
1618+ assertThat (evaluator .evaluate (mockContextWith ("host" , "foobaz" ))).isFalse ();
1619+ }
1620+
1621+ @ Test
1622+ public void singlePredicate_stringMatcher_prefix_matches () {
1623+ // Verifies valid prefix config
1624+ Matcher .MatcherList .Predicate .SinglePredicate predicate =
1625+ Matcher .MatcherList .Predicate .SinglePredicate .newBuilder ()
1626+ .setInput (TypedExtensionConfig .newBuilder ()
1627+ .setTypedConfig (Any .pack (
1628+ io .envoyproxy .envoy .type .matcher .v3 .HttpRequestHeaderMatchInput .newBuilder ()
1629+ .setHeaderName ("host" ).build ())))
1630+ .setValueMatch (com .github .xds .type .matcher .v3 .StringMatcher .newBuilder ()
1631+ .setPrefix ("foo" ))
1632+ .build ();
1633+
1634+ PredicateEvaluator evaluator = PredicateEvaluator .fromProto (
1635+ Matcher .MatcherList .Predicate .newBuilder ().setSinglePredicate (predicate ).build ());
1636+
1637+ // "foobar" starts with "foo" -> true
1638+ assertThat (evaluator .evaluate (mockContextWith ("host" , "foobar" ))).isTrue ();
1639+ // "barfoo" does not start with "foo" -> false
1640+ assertThat (evaluator .evaluate (mockContextWith ("host" , "barfoo" ))).isFalse ();
1641+ }
1642+
1643+ @ Test
1644+ public void singlePredicate_stringMatcher_contains_matches () {
1645+ // Verifies valid contains config
1646+ Matcher .MatcherList .Predicate .SinglePredicate predicate =
1647+ Matcher .MatcherList .Predicate .SinglePredicate .newBuilder ()
1648+ .setInput (TypedExtensionConfig .newBuilder ()
1649+ .setTypedConfig (Any .pack (
1650+ io .envoyproxy .envoy .type .matcher .v3 .HttpRequestHeaderMatchInput .newBuilder ()
1651+ .setHeaderName ("host" ).build ())))
1652+ .setValueMatch (com .github .xds .type .matcher .v3 .StringMatcher .newBuilder ()
1653+ .setContains ("oba" ))
1654+ .build ();
1655+
1656+ PredicateEvaluator evaluator = PredicateEvaluator .fromProto (
1657+ Matcher .MatcherList .Predicate .newBuilder ().setSinglePredicate (predicate ).build ());
1658+
1659+ // "foobar" contains "oba" -> true
1660+ assertThat (evaluator .evaluate (mockContextWith ("host" , "foobar" ))).isTrue ();
1661+ // "fobar" does not contain "oba" -> false
1662+ assertThat (evaluator .evaluate (mockContextWith ("host" , "none" ))).isFalse ();
1663+ }
15501664}
1665+
0 commit comments