EBernhardson has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/326057 )

Change subject: Add warnings for various query parsing failures
......................................................................

Add warnings for various query parsing failures

Bug: T149142
Change-Id: Ia574888bc5ee2669701ead8b2f0aec1d9d5c49ea
---
M i18n/en.json
M i18n/qqq.json
M includes/Query/FileNumericFeature.php
M includes/Query/GeoFeature.php
M includes/Query/InCategoryFeature.php
M includes/Query/LanguageFeature.php
M includes/Query/MoreLikeFeature.php
M includes/Query/RegexInSourceFeature.php
M includes/Search/SearchContext.php
M includes/Searcher.php
M tests/unit/Query/BaseSimpleKeywordFeatureTest.php
M tests/unit/Query/FileFeatureTest.php
M tests/unit/Query/GeoFeatureTest.php
M tests/unit/Query/InCategoryFeatureTest.php
A tests/unit/Query/LanguageFeatureTest.php
M tests/unit/Query/MoreLikeFeatureTest.php
A tests/unit/Query/RegexInSourceFeatureTest.php
17 files changed, 378 insertions(+), 46 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CirrusSearch 
refs/changes/57/326057/1

diff --git a/i18n/en.json b/i18n/en.json
index 389ba76..e83d58e 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -55,5 +55,16 @@
        "cirrussearch-completion-profile-classic-pref-name": "Classic prefix 
search",
        "cirrussearch-completion-profile-classic-pref-desc": "No typo 
correction. Matches the beginning of titles.",
        "cirrussearch-timed-out": "The search timed out, only partial results 
are available.",
-       "cirrussearch-regex-timed-out": "The regex search timed out, only 
partial results are available. Try simplifying your regular expression to get 
complete results."
+       "cirrussearch-regex-timed-out": "The regex search timed out, only 
partial results are available. Try simplifying your regular expression to get 
complete results.",
+       "cirrussearch-file-numeric-feature-not-a-number": "The search term '$1' 
requires numeric values, but '$2' was provided.",
+       "cirrusesarch-file-numeric-feature-multi-argument-w-sign": "The search 
term '$1' doesn't allow providing multiple arguments and a sign with '$2'.",
+       "cirrussearch-feature-not-available": "The search keyword '$1' is not 
enabled on this wiki.",
+       "cirrussearch-geo-feature-invalid-coordinates": "The geo coordinates 
'$2' provided to '$1' could not be parsed.",
+       "cirrussearch-geo-feature-invalid-distance": "The geo distance '$2' 
provided to '$1' could not be parsed.",
+       "cirrussearch-geo-feature-unknown-title": "The title '$2' provided to 
'$1' does not exist.",
+       "cirrussearch-geo-feature-title-no-coordinates": "The title '$2' 
provided to '$1' does not have any coordinates",
+       "cirrussearch-feature-too-many-conditions": "Too many conditions 
provided to '$1', truncating to $2.",
+       "cirrussearch-incategory-feature-no-valid-categories": "No valid 
categories were provided to '$1'.",
+       "cirrussearch-mlt-feature-no-valid-titles": "No valid titles provided 
to '$1'.",
+       "cirrussearch-mlt-not-configured": "The '$1' feature is misconfigured. 
Ask admin to fix $wgCirrusSearchMoreLikeThisFields"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 3df6adc..59aa699 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -65,4 +65,15 @@
        "cirrussearch-completion-profile-classic-pref-desc": "Description of 
the completion profile classic.",
        "cirrussearch-timed-out": "Message displayed to user when the search 
query gave up due to timeout before completing.",
        "cirrussearch-regex-timed-out": "Message displayed to user when the 
regex search query gave up due to timeout before completing."
+       "cirrussearch-file-numeric-feature-not-a-number": "Warning shown to 
users when a search keyword for numeric file features used something other than 
a number.",
+       "cirrusesarch-file-numeric-feature-multi-argument-w-sign": "Warning 
shown to users when a search keyword for numeric file features used multiple 
arguments with an invalid qualifier.",
+       "cirrussearch-feature-not-available": "Warning shown to users when an 
unavailable search keyword is used.",
+       "cirrussearch-geo-feature-invalid-coordinates": "Warning shown to users 
when providing invalid coordinates to a geo keyword.",
+       "cirrussearch-geo-feature-invalid-distance": "Warning shown to users 
when providing invalid distance qualifier to a geo keyword.",
+       "cirrussearch-geo-feature-unknown-title": "Warning shown to users when 
providing an unknown title to a geo keyword.",
+       "cirrussearch-geo-feature-title-no-coordinates": "Warning shown to 
users when providing an article title without coordinates to a geo keyword.",
+       "cirrussearch-feature-too-many-conditions": "Warning shown to users 
when more conditions than are allowed were provided to a keyword feature.",
+       "cirrussearch-incategory-feature-no-valid-categories": "Warning shown 
to users when no valid categories were found with their incategory keyword.",
+       "cirrussearch-mlt-feature-no-valid-titles": "Warning shown to users 
when no valid titles were found with their more like keyword.",
+       "cirrussearch-mlt-not-configured": "Warning shown to users when 
issueing a more like this query and the wiki hasn't configured it."
 }
diff --git a/includes/Query/FileNumericFeature.php 
b/includes/Query/FileNumericFeature.php
index 594e728..a8c3d3f 100644
--- a/includes/Query/FileNumericFeature.php
+++ b/includes/Query/FileNumericFeature.php
@@ -51,15 +51,19 @@
 
                $field = $this->keyTable[$key];
 
-               $sign = $this->extractSign( $value );
+               list( $sign, $number ) = $this->extractSign( $value );
 
                // filesize treats no sign as >, since exact file size matches 
make no sense
-               if ( !$sign && $key === 'filesize' && strpos( $value, ',' ) === 
false ) {
+               if ( !$sign && $key === 'filesize' && strpos( $number, ',' ) 
=== false ) {
                        $sign = 1;
                }
 
+               if ( !$this->validate( $context, $key, $value, $sign, $number ) 
) {
+                       return [ null, false ];
+               }
+
                $query =
-                       $this->buildNumericQuery( $field, $sign, $value, ( $key 
=== 'filesize' ) ? 1024 : 1 );
+                       $this->buildNumericQuery( $field, $sign, $number, ( 
$key === 'filesize' ) ? 1024 : 1 );
 
                return [ $query, false ];
        }
@@ -68,16 +72,63 @@
         * Extract sign prefix which can be < or > or nothing.
         * @param     $value
         * @param int $default
-        * @return int  0 is equal, 1 is more, -1 is less
+        * @return array Two element array, first the sign: 0 is equal, 1 is 
more, -1 is less,
+        *  then the number to be compared.
         */
-       protected function extractSign( &$value, $default = 0 ) {
+       protected function extractSign( $value, $default = 0 ) {
                if ( $value[0] == '>' || $value[0] == '<' ) {
                        $sign = ( $value[0] == '>' ) ? 1 : - 1;
-                       $value = substr( $value, 1 );
+                       return [$sign, substr( $value, 1 )];
                } else {
-                       return $default;
+                       return [$default, $value];
                }
-               return $sign;
+       }
+
+       /**
+        * Validate that input arguments will construct a valid query
+        *
+        * @param SearchContext $context
+        * @param string $key The matched query keyword
+        * @param string $value The original value provided by user
+        * @param int $sign
+        * @param string $number
+        * @return bool
+        */
+       protected function validate( SearchContext $context, $key, $value, 
$sign, $number ) {
+               $valid = true;
+               if ( $sign && strpos( $number, ',' ) !== false ) {
+                       $context->addWarning(
+                               
'cirrussearch-file-numeric-feature-multi-argument-w-sign',
+                               $key,
+                               $number
+                       );
+                       $valid = false;
+               } elseif ( $sign || strpos( $number, ',' ) === false ) {
+                       if ( !is_numeric( $number ) ) {
+                               $this->nanWarning( $context, $key, $number );
+                               $valid = false;
+                       }
+               } else {
+                       $numbers = explode( ',', $number, 2 );
+                       if ( !is_numeric( $numbers[0] ) ) {
+                               $this->nanWarning( $context, $key, $numbers[0] 
);
+                               $valid = false;
+                       }
+                       if ( !is_numeric( $numbers[1] ) ) {
+                               $this->nanWarning( $context, $key, $numbers[1] 
);
+                               $valid = false;
+                       }
+               }
+
+               return $valid;
+       }
+
+       protected function nanWarning( $context, $key, $number ) {
+               $context->addWarning(
+                       'cirrussearch-file-numeric-feature-not-a-number',
+                       $key,
+                       $number
+               );
        }
 
        /**
@@ -90,9 +141,6 @@
         */
        protected function buildNumericQuery( $field, $sign, $number, 
$multiplier = 1 ) {
                if ( $sign ) {
-                       if ( !is_numeric( $number ) ) {
-                               return null;
-                       }
                        $number = intval( $number );
                        if ( $sign < 0 ) {
                                $range = [ 'lte' => $number * $multiplier ];
@@ -102,17 +150,11 @@
                        return new Query\Range( $field, $range );
                } else {
                        if ( strpos( $number, ',' ) !== false ) {
-                               $numbers = explode( ',', $number );
-                               if ( !is_numeric( $numbers[0] ) || !is_numeric( 
$numbers[1] ) ) {
-                                       return null;
-                               }
+                               $numbers = explode( ',', $number, 2 );
                                return new Query\Range( $field, [
                                        'gte' => intval( $numbers[0] ) * 
$multiplier,
                                        'lte' => intval( $numbers[1] ) * 
$multiplier
                                ] );
-                       }
-                       if ( !is_numeric( $number ) ) {
-                               return null;
                        }
                        $query = new  Query\Match();
                        $query->setFieldQuery( $field, (string)( $number * 
$multiplier ) );
diff --git a/includes/Query/GeoFeature.php b/includes/Query/GeoFeature.php
index f55fd10..260bff9 100644
--- a/includes/Query/GeoFeature.php
+++ b/includes/Query/GeoFeature.php
@@ -56,16 +56,18 @@
         */
        protected function doApply( SearchContext $context, $key, $value, 
$quotedValue, $negated ) {
                if ( !class_exists( GeoData::class ) ) {
+                       $context->addWarning( 
'cirrussearch-feature-not-available', $key );
                        return [ null, false ];
                }
 
                if ( substr( $key, -5 ) === 'title' ) {
                        list( $coord, $radius, $excludeDocId ) = 
$this->parseGeoNearbyTitle(
-                               $context->getConfig(),
+                               $context,
+                               $key,
                                $value
                        );
                } else {
-                       list( $coord, $radius ) = $this->parseGeoNearby( $value 
);
+                       list( $coord, $radius ) = $this->parseGeoNearby( 
$context, $key, $value );
                        $excludeDocId = '';
                }
 
@@ -87,13 +89,14 @@
         *   <title>
         *   <radius>,<title>
         *
-        * @param SearchConfig $config the Cirrus config object
+        * @param SearchContext $context
+        * @param string $key Key used to trigger feature
         * @param string $text user input to parse
         * @return array Three member array with Coordinate object, integer 
radius
         *  in meters, and page id to exclude from results.. When invalid the
         *  Coordinate returned will be null.
         */
-       public function parseGeoNearbyTitle( SearchConfig $config, $text ) {
+       public function parseGeoNearbyTitle( SearchContext $context, $key, 
$text ) {
                $title = Title::newFromText( $text );
                if ( $title && $title->exists() ) {
                        // Default radius if not provided: 5km
@@ -104,24 +107,40 @@
                        // remaining text is a valid title to use.
                        $pieces = explode( ',', $text, 2 );
                        if ( count( $pieces ) !== 2 ) {
+                               $context->addWarning(
+                                       
"cirrussearch-geo-feature-invalid-coordinates",
+                                       $key, $text
+                               );
                                return [ null, 0, '' ];
                        }
                        $radius = $this->parseDistance( $pieces[0] );
                        if ( $radius === null ) {
+                               $context->addWarning(
+                                       
"cirrussearch-geo-feature-invalid-distance",
+                                       $key, $pieces[0]
+                               );
                                return [ null, 0, '' ];
                        }
                        $title = Title::newFromText( $pieces[1] );
                        if ( !$title || !$title->exists() ) {
+                               $context->addWarning(
+                                       
"cirrussearch-geo-feature-unknown-title",
+                                       $key, $pieces[1]
+                               );
                                return [ null, 0, '' ];
                        }
                }
 
                $coord = GeoData::getPageCoordinates( $title );
                if ( !$coord ) {
+                       $context->addWarning(
+                               'cirrussearch-geo-feature-title-no-coordinates',
+                               (string)$title
+                       );
                        return [ null, 0, '' ];
                }
 
-               return [ $coord, $radius, $config->makeId( 
$title->getArticleID() ) ];
+               return [ $coord, $radius, $context->getConfig()->makeId( 
$title->getArticleID() ) ];
        }
 
        /**
@@ -131,17 +150,23 @@
         *   <lat>,<lon>
         *   <radius>,<lat>,<lon>
         *
+        * @param SearchContext $context
+        * @param string $key
         * @param string $text
         * @return array Two member array with Coordinate object, and integer 
radius
         *  in meters. When invalid the Coordinate returned will be null.
         */
-       public function parseGeoNearby( $text ) {
+       public function parseGeoNearby( SearchContext $context, $key, $text ) {
                $pieces = explode( ',', $text, 3 );
                // Default radius if not provided: 5km
                $radius = self::DEFAULT_RADIUS;
                if ( count( $pieces ) === 3 ) {
                        $radius = $this->parseDistance( $pieces[0] );
                        if ( $radius === null ) {
+                               $context->addWarning(
+                                       
'cirrussearch-geo-feature-invalid-distance',
+                                       $key, $pieces[0]
+                               );
                                return [ null, 0 ];
                        }
                        $lat = $pieces[1];
@@ -150,11 +175,19 @@
                        $lat = $pieces[0];
                        $lon = $pieces[1];
                } else {
+                       $context->addWarning(
+                               'cirrussearch-geo-feature-invalid-coordinates',
+                               $key, $text
+                       );
                        return [ null, 0 ];
                }
 
                $globe = new Globe( self::DEFAULT_GLOBE );
                if ( !$globe->coordinatesAreValid( $lat, $lon ) ) {
+                       $context->addWarning(
+                               'cirrussearch-geo-feature-invalid-coordinates',
+                               $key, $text
+                       );
                        return [ null, 0 ];
                }
 
diff --git a/includes/Query/InCategoryFeature.php 
b/includes/Query/InCategoryFeature.php
index 905e608..c07b14d 100644
--- a/includes/Query/InCategoryFeature.php
+++ b/includes/Query/InCategoryFeature.php
@@ -55,14 +55,26 @@
         *  string.
         */
        protected function doApply( SearchContext $context, $key, $value, 
$quotedValue, $negated ) {
-               $categories = array_slice(
-                       explode( '|', $value ),
-                       0,
-                       $this->maxConditions
-               );
+               $categories = explode( '|', $value );
+               if ( count( $categories ) > $this->maxConditions ) {
+                       $context->addWarning(
+                               'cirrussearch-feature-too-many-conditions',
+                               $key,
+                               $this->maxConditions
+                       );
+                       $categories = array_slice(
+                               $categories,
+                               0,
+                               $this->maxConditions
+                       );
+               }
                $filter = $this->matchPageCategories( $categories );
                if ( $filter === null ) {
                        $context->setResultsPossible( false );
+                       $context->addWarning(
+                               
'cirrussearch-incategory-feature-no-valid-categories',
+                               $key
+                       );
                }
 
                return [ $filter, false ];
diff --git a/includes/Query/LanguageFeature.php 
b/includes/Query/LanguageFeature.php
index a4356cc..537ab00 100644
--- a/includes/Query/LanguageFeature.php
+++ b/includes/Query/LanguageFeature.php
@@ -42,7 +42,15 @@
        protected function doApply( SearchContext $context, $key, $value, 
$quotedValue, $negated ) {
                $queries = [];
 
-               $langs = array_slice( explode( ',', $value ), 0, 
self::QUERY_LIMIT );
+               $langs = explode( ',', $value );
+               if ( count( $langs ) > self::QUERY_LIMIT ) {
+                       $context->addWarning(
+                               'cirrussearch-feature-too-many-conditions',
+                               $key,
+                               self::QUERY_LIMIT
+                       );
+                       $langs = array_slice( $langs, 0, self::QUERY_LIMIT );
+               }
                foreach ( $langs as $lang ) {
                        if ( strlen( trim( $lang ) ) > 0 ) {
                                $query = new \Elastica\Query\Match();
diff --git a/includes/Query/MoreLikeFeature.php 
b/includes/Query/MoreLikeFeature.php
index 01837b7..016cfdf 100644
--- a/includes/Query/MoreLikeFeature.php
+++ b/includes/Query/MoreLikeFeature.php
@@ -51,7 +51,7 @@
                        // of input order when the result would be the same.
                        if ( $pos === 0 ) {
                                $titleString = substr( $term, $pos + strlen( 
$prefix ) );
-                               $this->doApply( $context, $titleString, 
$options );
+                               $this->doApply( $context, $prefix, 
$titleString, $options );
 
                                return "";
                        }
@@ -61,14 +61,22 @@
                return $term;
        }
 
-       private function doApply( SearchContext $context, $titleString, 
$options ) {
+       private function doApply( SearchContext $context, $prefix, 
$titleString, $options ) {
                $titles = $this->collectTitles( $titleString );
                if ( !count( $titles ) ) {
+                       $context->addWarning(
+                               "cirrussearch-mlt-feature-no-valid-titles",
+                               substr( $prefix, 0, -1 )
+                       );
                        $context->setResultsPossible( false );
                        return;
                }
                $query = $this->buildMoreLikeQuery( $context, $titles, $options 
);
                if ( $query === null ) {
+                       $context->addWarning(
+                               "cirrussearch-mlt-not-configured",
+                               $prefix
+                       );
                        $context->setResultsPossible( false );
                        return;
                }
diff --git a/includes/Query/RegexInSourceFeature.php 
b/includes/Query/RegexInSourceFeature.php
index acd2c9c..5521a05 100644
--- a/includes/Query/RegexInSourceFeature.php
+++ b/includes/Query/RegexInSourceFeature.php
@@ -64,6 +64,10 @@
             
'/(?<not>-)?insource:\/(?<pattern>(?:[^\\\\\/]|\\\\.)+)\/(?<insensitive>i)? ?/',
                        function ( $matches ) use ( $context ) {
                                if ( !$this->enabled ) {
+                                       $context->addWarning(
+                                               
'cirrussearch-feature-not-available',
+                                               'insource regex'
+                                       );
                                        return '';
                                }
 
diff --git a/includes/Search/SearchContext.php 
b/includes/Search/SearchContext.php
index 8fe33dc..8e66345 100644
--- a/includes/Search/SearchContext.php
+++ b/includes/Search/SearchContext.php
@@ -180,6 +180,11 @@
        private $escaper;
 
        /**
+        * @var array[][] Warnings to be passed into StatusValue::warning()
+        */
+       private $warnings = [];
+
+       /**
         * @param SearchConfig $config
         * @param int[]|null $namespaces
         */
@@ -679,4 +684,19 @@
        public function escaper() {
                return $this->escaper;
        }
+
+       /**
+        * @param string $message i18n message key
+        */
+       public function addWarning( $message /*, parameters... */ ) {
+               $this->warnings[] = func_get_args();
+       }
+
+       /**
+        * @return array[] Array of arrays. Each sub array is a set of values
+        *  suitable for creating an i18n message.
+        */
+       public function getWarnings() {
+               return $this->warnings;
+       }
 }
diff --git a/includes/Searcher.php b/includes/Searcher.php
index ec9ebe6..45391f4 100644
--- a/includes/Searcher.php
+++ b/includes/Searcher.php
@@ -365,6 +365,10 @@
                        }
                }
 
+               foreach ( $this->searchContext->getWarnings() as $warning ) {
+                       call_user_func_array( [ $status, 'warning' ], $warning 
);
+               }
+
                return $status;
        }
 
diff --git a/tests/unit/Query/BaseSimpleKeywordFeatureTest.php 
b/tests/unit/Query/BaseSimpleKeywordFeatureTest.php
index 9ee0e83..869c39c 100644
--- a/tests/unit/Query/BaseSimpleKeywordFeatureTest.php
+++ b/tests/unit/Query/BaseSimpleKeywordFeatureTest.php
@@ -11,11 +11,18 @@
  * SimpleKeywordFeature
  */
 abstract class BaseSimpleKeywordFeatureTest extends CirrusTestCase {
-       protected function mockContextExpectingAddFilter( array $expectedQuery 
= null ) {
+
+       protected function mockContext() {
                $context = $this->getMockBuilder( SearchContext::class )
                        ->disableOriginalConstructor()
                        ->getMock();
+               $context->expects( $this->any() )->method( 'getConfig' 
)->willReturn( new SearchConfig() );
 
+               return $context;
+       }
+
+       protected function mockContextExpectingAddFilter( array $expectedQuery 
= null ) {
+               $context = $this->mockContext();
                if ( $expectedQuery === null ) {
                        $context->expects( $this->never() )
                                ->method( 'addFilter' );
@@ -27,8 +34,19 @@
                                        return true;
                                } ) );
                }
-               $context->expects( $this->any() )->method( 'getConfig' 
)->willReturn( new SearchConfig() );
 
                return $context;
        }
+
+       protected function assertWarnings( KeywordFeature $feature, $expected, 
$term ) {
+               $warnings = [];
+               $context = $this->mockContext();
+               $context->expects( $this->any() )
+                       ->method( 'addWarning' )
+                       ->will( $this->returnCallback( function () use ( 
&$warnings ) {
+                               $warnings[] = func_get_args();
+                       } ) );
+               $feature->apply( $context, $term );
+               $this->assertEquals( $expected, $warnings );
+       }
 }
diff --git a/tests/unit/Query/FileFeatureTest.php 
b/tests/unit/Query/FileFeatureTest.php
index de9fc1e..88270a2 100644
--- a/tests/unit/Query/FileFeatureTest.php
+++ b/tests/unit/Query/FileFeatureTest.php
@@ -17,6 +17,15 @@
                                ] ],
                                'filesize:10',
                        ],
+                       'filesize allows multi-argument' => [
+                               [ 'range' => [
+                                       'file_size' => [
+                                               'gte' => 125952,
+                                               'lte' => 328704,
+                                       ]
+                               ] ],
+                               'filesize:123,321',
+                       ],
                        'numeric with with no sign - exact match' => [
                                [ 'match' => [
                                        'file_bits' => [
@@ -124,4 +133,31 @@
                $feature = new FileTypeFeature();
                $feature->apply( $context, $term );
        }
+
+       public function warningNumericProvider() {
+               return [
+                       'arguments must be numeric' => [
+                               [ [ 
'cirrussearch-file-numeric-feature-not-a-number', 'filebits', 'celery' ] ],
+                               'filebits:celery'
+                       ],
+                       'each argument in a multi-value must be a number' => [
+                               [
+                                       [ 
'cirrussearch-file-numeric-feature-not-a-number', 'fileheight', 'something' ],
+                                       [ 
'cirrussearch-file-numeric-feature-not-a-number', 'fileheight', 'voodoo' ]
+                               ],
+                               'fileheight:something,voodoo',
+                       ],
+                       'multi-argument with a sign is invalid' => [
+                               [ [ 
'cirrussearch-file-numeric-feature-multi-argument-w-sign', 'fileh', '200,400' ] 
],
+                               'fileh:>200,400',
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider warningNumericProvider
+        */
+       public function testWarningNumeric( $expected, $term ) {
+               $this->assertWarnings( new FileNumericFeature(), $expected, 
$term );
+       }
 }
diff --git a/tests/unit/Query/GeoFeatureTest.php 
b/tests/unit/Query/GeoFeatureTest.php
index 126657c..ec846d1 100644
--- a/tests/unit/Query/GeoFeatureTest.php
+++ b/tests/unit/Query/GeoFeatureTest.php
@@ -30,7 +30,7 @@
  *
  * @group CirrusSearch
  */
-class GeoFeatureTest extends CirrusTestCase {
+class GeoFeatureTest extends BaseSimpleKeywordFeatureTest {
 
        public function parseDistanceProvider() {
                return [
@@ -164,7 +164,8 @@
        public function testParseGeoNearby( $expected, $value ) {
                if ( class_exists( Coord::class ) ) {
                        $feature = new GeoFeature;
-                       $result = $feature->parseGeoNearby( $value );
+                       $context = $this->mockContextExpectingAddFilter();
+                       $result = $feature->parseGeoNearby( $context, 
'nearcoord', $value );
                        if ( $result[0] instanceof Coord ) {
                                $result[0] = [ 'lat' => $result[0]->lat, 'lon' 
=> $result[0]->lon ];
                        }
@@ -272,21 +273,54 @@
                // Inject fake page with comma in it as well
                MediaWikiServices::getInstance()->getLinkCache()
                        ->addGoodLinkObj( 1234567, Title::newFromText( 
'Washington, D.C.' ) );
-
-               $config = $this->getMock( SearchConfig::class );
-               $config->expects( $this->any() )
-                       ->method( 'makeId' )
-                       ->will( $this->returnCallback( function ( $id ) {
-                               return $id;
-                       } ) );
+               $context = $this->mockContextExpectingAddFilter();
 
                // Finally run the test
                $feature = new GeoFeature;
-               $result = $feature->parseGeoNearbyTitle( $config, $value );
+               $result = $feature->parseGeoNearbyTitle( $context, 'neartitle', 
$value );
                if ( $result[0] instanceof Coord ) {
                        $result[0] = [ 'lat' => $result[0]->lat, 'lon' => 
$result[0]->lon ];
                }
 
                $this->assertEquals( $expected, $result );
        }
+
+       public function geoWarningsProvider() {
+               return [
+                       'coordinates must be two or three pieces' => [
+                               [ [ 
'cirrussearch-geo-feature-invalid-coordinates', 'nearcoord', 'hi' ] ],
+                               'nearcoord:hi',
+                       ],
+                       'three piece coordinates must use valid radius with 
qualifier' => [
+                               [ [ 
'cirrussearch-geo-feature-invalid-distance', 'nearcoord', '40s' ] ],
+                               'nearcoord:40s,12,21'
+                       ],
+                       'coordinates must be valid earth coordinates' => [
+                               [ [ 
'cirrussearch-geo-feature-invalid-coordinates', 'boost-nearcoord', '12345,123' 
] ],
+                               'boost-nearcoord:12345,123',
+                       ],
+                       'titles must be known' => [
+                               [ [ 'cirrussearch-geo-feature-unknown-title', 
'neartitle', 'Some unknown page' ] ],
+                               'neartitle:"10km,Some unknown page"',
+                       ],
+                       'titles must have coordinates' => [
+                               [ [ 
'cirrussearch-geo-feature-title-no-coordinates', 'Foobar' ] ],
+                               'neartitle:Foobar',
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider geoWarningsProvider
+        */
+       public function testGeoWarnings( $expected, $term ) {
+               if ( ! class_exists( Coord::class ) ) {
+                       $this->markTestSkipped( 'GeoData extension must be 
installed' );
+                       return;
+               }
+               // Inject fake San Francisco page into LinkCache so it "exists"
+               MediaWikiServices::getInstance()->getLinkCache()
+                       ->addGoodLinkObj( 98765, Title::newFromText( 'Foobar' ) 
);
+               $this->assertWarnings( new GeoFeature(), $expected, $term );
+       }
 }
diff --git a/tests/unit/Query/InCategoryFeatureTest.php 
b/tests/unit/Query/InCategoryFeatureTest.php
index 774257d..7d6658c 100644
--- a/tests/unit/Query/InCategoryFeatureTest.php
+++ b/tests/unit/Query/InCategoryFeatureTest.php
@@ -157,4 +157,24 @@
                        ->will( $this->returnValue( $db ) );
                $this->setService( 'DBLoadBalancer', $lb );
        }
+
+       public function testTooManyCategoriesWarning() {
+               $this->assertWarnings(
+                       new InCategoryFeature( new \HashConfig( [
+                               'CirrusSearchMaxIncategoryOptions' => 2,
+                       ] ) ),
+                       [ [ 'cirrussearch-feature-too-many-conditions', 
'incategory', 2 ] ],
+                       'incategory:a|b|c'
+               );
+       }
+
+       public function testCategoriesMustExistWarning() {
+               $this->assertWarnings(
+                       new InCategoryFeature( new \HashConfig( [
+                               'CirrusSearchMaxIncategoryOptions' => 2,
+                       ] ) ),
+                       [ [ 
'cirrussearch-incategory-feature-no-valid-categories', 'incategory' ] ],
+                       'incategory:id:74,id:18'
+               );
+       }
 }
diff --git a/tests/unit/Query/LanguageFeatureTest.php 
b/tests/unit/Query/LanguageFeatureTest.php
new file mode 100644
index 0000000..795fa40
--- /dev/null
+++ b/tests/unit/Query/LanguageFeatureTest.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace CirrusSearch\Query;
+
+/**
+ * @group CirrusSearch
+ */
+class LanguageFeatureText extends BaseSimpleKeywordFeatureTest {
+
+       public function testTooManyLanguagesWarning() {
+               $this->assertWarnings(
+                       new LanguageFeature(),
+                       [ [ 'cirrussearch-feature-too-many-conditions', 
'inlanguage', LanguageFeature::QUERY_LIMIT ] ],
+                       'inlanguage:' . implode( ',', range(1,40))
+               );
+       }
+
+       public function testCategoriesMustExistWarning() {
+               $this->assertWarnings(
+                       new InCategoryFeature( new \HashConfig( [
+                               'CirrusSearchMaxIncategoryOptions' => 2,
+                       ] ) ),
+                       [ [ 
'cirrussearch-incategory-feature-no-valid-categories', 'incategory' ] ],
+                       'incategory:id:74,id:18'
+               );
+       }
+}
diff --git a/tests/unit/Query/MoreLikeFeatureTest.php 
b/tests/unit/Query/MoreLikeFeatureTest.php
index da9333c..d7484d3 100644
--- a/tests/unit/Query/MoreLikeFeatureTest.php
+++ b/tests/unit/Query/MoreLikeFeatureTest.php
@@ -3,6 +3,7 @@
 namespace CirrusSearch\Query;
 
 use CirrusSearch\CirrusTestCase;
+use CirrusSearch\SearchConfig;
 use CirrusSearch\Search\SearchContext;
 use MediaWiki\MediaWikiServices;
 use Title;
@@ -27,7 +28,7 @@
  *
  * @group CirrusSearch
  */
-class MoreLikeFeatureTest extends CirrusTestCase {
+class MoreLikeFeatureTest extends BaseSimpleKeywordFeatureTest {
 
        public function applyProvider() {
                return [
@@ -144,4 +145,24 @@
                        }
                }
        }
+
+       public function testWarningsForUnknownPages() {
+               MediaWikiServices::getInstance()->getLinkCache()
+                       ->addGoodLinkObj( 12345, Title::newFromText( 'Some 
page' ) );
+               $this->assertWarnings(
+                       new MoreLikeFeature( new SearchConfig() ),
+                       [],
+                       'morelike:Some page'
+               );
+               $this->assertWarnings(
+                       new MoreLikeFeature( new SearchConfig() ),
+                       [],
+                       'morelike:Some page|Title that doesnt exist'
+               );
+               $this->assertWarnings(
+                       new MoreLikeFeature( new SearchConfig() ),
+                       [ [ 'cirrussearch-mlt-feature-no-valid-titles', 
'morelike' ] ],
+                       'morelike:Title that doesnt exist'
+               );
+       }
 }
diff --git a/tests/unit/Query/RegexInSourceFeatureTest.php 
b/tests/unit/Query/RegexInSourceFeatureTest.php
new file mode 100644
index 0000000..68b2736
--- /dev/null
+++ b/tests/unit/Query/RegexInSourceFeatureTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace CirrusSearch\Query;
+
+use CirrusSearch\SearchConfig;
+use CirrusSearch\Test\HashSearchConfig;
+
+/**
+ * @group CirrusSearch
+ */
+class RegexInSourceFeatureText extends BaseSimpleKeywordFeatureTest {
+
+       public function testGivesWarningIfNotEnabled() {
+               $config = new HashSearchConfig( [
+                       'CirrusSearchEnableRegex' => false,
+               ], [ 'inherit' ] );
+               $this->assertWarnings(
+                       new RegexInSourceFeature( $config ),
+                       [ [ 'cirrussearch-feature-not-available', 'insource 
regex' ] ],
+                       'insource:/abc/'
+               );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/326057
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia574888bc5ee2669701ead8b2f0aec1d9d5c49ea
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CirrusSearch
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to