1<?php 2/** 3 * Copyright 2011-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file COPYING for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 2011-2016 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 * @subpackage UnitTests 13 */ 14 15/** 16 * Tests for the Search Query object. 17 * 18 * @author Michael Slusarz <slusarz@horde.org> 19 * @category Horde 20 * @copyright 2011-2016 Horde LLC 21 * @ignore 22 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 23 * @package Imap_Client 24 * @subpackage UnitTests 25 */ 26class Horde_Imap_Client_SearchTest extends PHPUnit_Framework_TestCase 27{ 28 /** 29 * @dataProvider flagQueryProvider 30 */ 31 public function testFlagQuery($flags, $fuzzy, $expected) 32 { 33 $ob = new Horde_Imap_Client_Search_Query(); 34 35 foreach ($flags as $val) { 36 $ob->flag($val[0], $val[1], array('fuzzy' => $fuzzy)); 37 } 38 39 $this->assertTrue($ob->flagSearch()); 40 $this->assertEquals( 41 $expected, 42 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 43 ); 44 } 45 46 public function flagQueryProvider() 47 { 48 return array( 49 array( 50 array( 51 /* System flag - set. */ 52 array('\\answered', true), 53 /* System flag - not set. */ 54 array('\\draft', false), 55 /* System flag - set. */ 56 array('foo', true), 57 /* System flag - not set. */ 58 array('bar', false) 59 ), 60 false, 61 'ANSWERED UNDRAFT KEYWORD FOO UNKEYWORD BAR' 62 ), 63 array( 64 array( 65 array('foo', true) 66 ), 67 true, 68 'FUZZY KEYWORD FOO' 69 ) 70 ); 71 } 72 73 /** 74 * @dataProvider newMsgsQueryProvider 75 */ 76 public function testNewMsgsQuery($newmsgs, $fuzzy, $expected) 77 { 78 $ob = new Horde_Imap_Client_Search_Query(); 79 $ob->newMsgs($newmsgs, array('fuzzy' => $fuzzy)); 80 81 $this->assertEquals( 82 $expected, 83 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 84 ); 85 } 86 87 public function newMsgsQueryProvider() 88 { 89 return array( 90 array(true, false, 'NEW'), 91 array(false, false, 'OLD'), 92 array(true, true, 'FUZZY NEW'), 93 array(false, true, 'FUZZY OLD') 94 ); 95 } 96 97 /** 98 * @dataProvider headerTextQueryProvider 99 */ 100 public function testHeaderTextQuery($not, $fuzzy, $expected) 101 { 102 $ob = new Horde_Imap_Client_Search_Query(); 103 $ob->headerText('Foo', 'Bar', $not, array('fuzzy' => $fuzzy)); 104 105 $this->assertEquals( 106 $expected, 107 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 108 ); 109 } 110 111 public function headerTextQueryProvider() 112 { 113 return array( 114 array(false, false, 'HEADER FOO Bar'), 115 array(true, false, 'NOT HEADER FOO Bar'), 116 array(false, true, 'FUZZY HEADER FOO Bar'), 117 array(true, true, 'FUZZY NOT HEADER FOO Bar') 118 ); 119 } 120 121 public function testHeaderTextUtf8Query() 122 { 123 $ob = new Horde_Imap_Client_Search_Query(); 124 $ob->headerText('Foo', 'EëE'); 125 126 try { 127 $ob->build(); 128 $this->fail(); 129 } catch (Horde_Imap_Client_Data_Format_Exception $e) { 130 // Expected 131 } 132 133 $ob->charset('UTF-8', false); 134 135 $this->assertNotEmpty($ob->build()); 136 } 137 138 /** 139 * @dataProvider textQueryProvider 140 */ 141 public function testTextQuery($body, $not, $fuzzy, $expected) 142 { 143 $ob = new Horde_Imap_Client_Search_Query(); 144 $ob->text('foo', $body, $not, array('fuzzy' => $fuzzy)); 145 146 $this->assertEquals( 147 $expected, 148 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 149 ); 150 } 151 152 public function textQueryProvider() 153 { 154 return array( 155 array(true, false, false, 'BODY foo'), 156 array(false, false, false, 'TEXT foo'), 157 array(true, true, false, 'NOT BODY foo'), 158 array(false, true, false, 'NOT TEXT foo'), 159 array(true, false, true, 'FUZZY BODY foo'), 160 array(false, false, true, 'FUZZY TEXT foo'), 161 array(true, true, true, 'FUZZY NOT BODY foo'), 162 array(false, true, true, 'FUZZY NOT TEXT foo') 163 ); 164 } 165 166 /** 167 * @dataProvider sizeQueryProvider 168 */ 169 public function testSizeQuery($larger, $not, $fuzzy, $expected) 170 { 171 $ob = new Horde_Imap_Client_Search_Query(); 172 $ob->size(100, $larger, $not, array('fuzzy' => $fuzzy)); 173 174 $this->assertEquals( 175 $expected, 176 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 177 ); 178 } 179 180 public function sizeQueryProvider() 181 { 182 return array( 183 array(true, false, false, 'LARGER 100'), 184 array(false, false, false, 'SMALLER 100'), 185 array(true, true, false, 'NOT LARGER 100'), 186 array(false, true, false, 'NOT SMALLER 100'), 187 array(true, false, true, 'FUZZY LARGER 100'), 188 array(false, false, true, 'FUZZY SMALLER 100'), 189 array(true, true, true, 'FUZZY NOT LARGER 100'), 190 array(false, true, true, 'FUZZY NOT SMALLER 100') 191 ); 192 } 193 194 /** 195 * @dataProvider idsQueryProvider 196 */ 197 public function testIdsQuery($ids, $not, $fuzzy, $expected) 198 { 199 $ob = new Horde_Imap_Client_Search_Query(); 200 $ob->ids(new Horde_Imap_Client_Ids($ids), $not, array( 201 'fuzzy' => $fuzzy 202 )); 203 204 $this->assertEquals( 205 $expected, 206 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 207 ); 208 } 209 210 public function idsQueryProvider() 211 { 212 return array( 213 array('1,2,3', false, false, 'UID 1:3'), 214 array('1:3', true, false, 'NOT UID 1:3'), 215 array('1,2,3', false, true, 'FUZZY UID 1:3'), 216 array('1:3', true, true, 'FUZZY NOT UID 1:3') 217 ); 218 } 219 220 /** 221 * @dataProvider dateSearchQueryProvider 222 */ 223 public function testDateSearchQuery( 224 $range, $header, $not, $fuzzy, $expected 225 ) 226 { 227 $ob = new Horde_Imap_Client_Search_Query(); 228 $ob->dateSearch( 229 new DateTime('January 1, 2010'), 230 $range, 231 $header, 232 $not, 233 array('fuzzy' => $fuzzy) 234 ); 235 236 $this->assertEquals( 237 $expected, 238 $fuzzy ? $this->_fuzzy($ob) : strval($ob) 239 ); 240 } 241 242 public function dateSearchQueryProvider() 243 { 244 return array( 245 array( 246 Horde_Imap_Client_Search_Query::DATE_BEFORE, 247 true, 248 false, 249 false, 250 'SENTBEFORE 1-Jan-2010', 251 ), 252 array( 253 Horde_Imap_Client_Search_Query::DATE_BEFORE, 254 false, 255 false, 256 false, 257 'BEFORE 1-Jan-2010', 258 ), 259 array( 260 Horde_Imap_Client_Search_Query::DATE_BEFORE, 261 true, 262 true, 263 false, 264 'NOT SENTBEFORE 1-Jan-2010', 265 ), 266 array( 267 Horde_Imap_Client_Search_Query::DATE_BEFORE, 268 false, 269 true, 270 false, 271 'NOT BEFORE 1-Jan-2010', 272 ), 273 array( 274 Horde_Imap_Client_Search_Query::DATE_BEFORE, 275 true, 276 true, 277 true, 278 'FUZZY NOT SENTBEFORE 1-Jan-2010', 279 ), 280 array( 281 Horde_Imap_Client_Search_Query::DATE_BEFORE, 282 false, 283 true, 284 true, 285 'FUZZY NOT BEFORE 1-Jan-2010', 286 ), 287 array( 288 Horde_Imap_Client_Search_Query::DATE_ON, 289 true, 290 false, 291 false, 292 'SENTON 1-Jan-2010', 293 ), 294 array( 295 Horde_Imap_Client_Search_Query::DATE_ON, 296 false, 297 false, 298 false, 299 'ON 1-Jan-2010', 300 ), 301 array( 302 Horde_Imap_Client_Search_Query::DATE_ON, 303 true, 304 true, 305 false, 306 'NOT SENTON 1-Jan-2010', 307 ), 308 array( 309 Horde_Imap_Client_Search_Query::DATE_ON, 310 false, 311 true, 312 false, 313 'NOT ON 1-Jan-2010', 314 ), 315 array( 316 Horde_Imap_Client_Search_Query::DATE_ON, 317 true, 318 true, 319 true, 320 'FUZZY NOT SENTON 1-Jan-2010', 321 ), 322 array( 323 Horde_Imap_Client_Search_Query::DATE_ON, 324 false, 325 true, 326 true, 327 'FUZZY NOT ON 1-Jan-2010', 328 ), 329 array( 330 Horde_Imap_Client_Search_Query::DATE_SINCE, 331 true, 332 false, 333 false, 334 'SENTSINCE 1-Jan-2010', 335 ), 336 array( 337 Horde_Imap_Client_Search_Query::DATE_SINCE, 338 false, 339 false, 340 false, 341 'SINCE 1-Jan-2010', 342 ), 343 array( 344 Horde_Imap_Client_Search_Query::DATE_SINCE, 345 true, 346 true, 347 false, 348 'NOT SENTSINCE 1-Jan-2010', 349 ), 350 array( 351 Horde_Imap_Client_Search_Query::DATE_SINCE, 352 false, 353 true, 354 false, 355 'NOT SINCE 1-Jan-2010', 356 ), 357 array( 358 Horde_Imap_Client_Search_Query::DATE_SINCE, 359 true, 360 true, 361 true, 362 'FUZZY NOT SENTSINCE 1-Jan-2010', 363 ), 364 array( 365 Horde_Imap_Client_Search_Query::DATE_SINCE, 366 false, 367 true, 368 true, 369 'FUZZY NOT SINCE 1-Jan-2010', 370 ) 371 ); 372 } 373 374 /** 375 * @dataProvider intervalSearchQueryProvider 376 */ 377 public function testIntervalSearchQuery($range, $not, $fuzzy, $expected) 378 { 379 $ob = new Horde_Imap_Client_Search_Query(); 380 $ob->intervalSearch(30, $range, $not, array('fuzzy' => $fuzzy)); 381 382 $this->assertEquals( 383 $expected, 384 $fuzzy ? $this->_fuzzy($ob, array('WITHIN')) : strval($ob) 385 ); 386 } 387 388 public function intervalSearchQueryProvider() 389 { 390 return array( 391 array( 392 Horde_Imap_Client_Search_Query::INTERVAL_OLDER, 393 false, 394 false, 395 'OLDER 30' 396 ), 397 array( 398 Horde_Imap_Client_Search_Query::INTERVAL_OLDER, 399 true, 400 false, 401 'NOT OLDER 30' 402 ), 403 array( 404 Horde_Imap_Client_Search_Query::INTERVAL_OLDER, 405 false, 406 true, 407 'FUZZY OLDER 30' 408 ), 409 array( 410 Horde_Imap_Client_Search_Query::INTERVAL_OLDER, 411 true, 412 true, 413 'FUZZY NOT OLDER 30' 414 ), 415 array( 416 Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER, 417 false, 418 false, 419 'YOUNGER 30' 420 ), 421 array( 422 Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER, 423 true, 424 false, 425 'NOT YOUNGER 30' 426 ), 427 array( 428 Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER, 429 false, 430 true, 431 'FUZZY YOUNGER 30' 432 ), 433 array( 434 Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER, 435 true, 436 true, 437 'FUZZY NOT YOUNGER 30' 438 ) 439 ); 440 } 441 442 public function testOrQueries() 443 { 444 $ob2 = new Horde_Imap_Client_Search_Query(); 445 $ob2->flag('\\deleted', false); 446 $ob2->headerText('from', 'ABC'); 447 448 $ob3 = new Horde_Imap_Client_Search_Query(); 449 $ob3->flag('\\deleted', true); 450 $ob3->headerText('from', 'DEF'); 451 452 $ob = new Horde_Imap_Client_Search_Query(); 453 $ob->orSearch(array($ob2, $ob3)); 454 455 $this->assertEquals( 456 'OR (DELETED FROM DEF) (UNDELETED FROM ABC)', 457 strval($ob) 458 ); 459 460 $ob4 = new Horde_Imap_Client_Search_Query(); 461 $ob4->flag('\\flagged', true); 462 $ob4->headerText('from', 'GHI'); 463 464 $ob5 = new Horde_Imap_Client_Search_Query(); 465 $ob5->orSearch(array($ob2, $ob3, $ob4)); 466 467 $this->assertEquals( 468 'OR (FLAGGED FROM GHI) (OR (DELETED FROM DEF) (UNDELETED FROM ABC))', 469 strval($ob5) 470 ); 471 } 472 473 public function testOrQueriesWithABaseQuery() 474 { 475 $or_ob = new Horde_Imap_Client_Search_Query(); 476 477 $ob = new Horde_Imap_Client_Search_Query(); 478 $ob->flag('\\deleted', false); 479 $ob->headerText('from', 'ABC'); 480 $or_ob->orSearch($ob); 481 482 $ob = new Horde_Imap_Client_Search_Query(); 483 $ob->flag('\\deleted', true); 484 $ob->headerText('from', 'DEF'); 485 $or_ob->orSearch($ob); 486 487 $base_ob = new Horde_Imap_Client_Search_Query(); 488 $base_ob->flag('\\seen', false); 489 $base_ob->andSearch($or_ob); 490 491 $this->assertEquals( 492 'UNSEEN OR (DELETED FROM DEF) (UNDELETED FROM ABC)', 493 strval($base_ob) 494 ); 495 } 496 497 /** 498 * @dataProvider modseqSearchQueryProvider 499 */ 500 public function testModseq($name, $type, $not, $fuzzy, $expected) 501 { 502 $ob = new Horde_Imap_Client_Search_Query(); 503 $ob->modseq(123, $name, $type, $not, array('fuzzy' => $fuzzy)); 504 505 $this->assertEquals( 506 $expected, 507 $fuzzy ? $this->_fuzzy($ob, array('CONDSTORE')) : strval($ob) 508 ); 509 } 510 511 public function modseqSearchQueryProvider() 512 { 513 return array( 514 array(null, null, false, false, 'MODSEQ 123'), 515 array(null, null, true, false, 'NOT MODSEQ 123'), 516 array(null, null, false, true, 'FUZZY MODSEQ 123'), 517 array(null, null, true, true, 'FUZZY NOT MODSEQ 123'), 518 array('foo', null, false, false, 'MODSEQ "foo" all 123'), 519 array('foo', null, true, false, 'NOT MODSEQ "foo" all 123'), 520 array('foo', null, false, true, 'FUZZY MODSEQ "foo" all 123'), 521 array('foo', null, true, true, 'FUZZY NOT MODSEQ "foo" all 123'), 522 array('foo', 'all', false, false, 'MODSEQ "foo" all 123'), 523 array('foo', 'all', true, false, 'NOT MODSEQ "foo" all 123'), 524 array('foo', 'all', false, true, 'FUZZY MODSEQ "foo" all 123'), 525 array('foo', 'all', true, true, 'FUZZY NOT MODSEQ "foo" all 123'), 526 array('foo', 'shared', false, false, 'MODSEQ "foo" shared 123'), 527 array('foo', 'shared', true, false, 'NOT MODSEQ "foo" shared 123'), 528 array('foo', 'shared', false, true, 'FUZZY MODSEQ "foo" shared 123'), 529 array('foo', 'shared', true, true, 'FUZZY NOT MODSEQ "foo" shared 123'), 530 array('foo', 'priv', false, false, 'MODSEQ "foo" priv 123'), 531 array('foo', 'priv', true, false, 'NOT MODSEQ "foo" priv 123'), 532 array('foo', 'priv', false, true, 'FUZZY MODSEQ "foo" priv 123'), 533 array('foo', 'priv', true, true, 'FUZZY NOT MODSEQ "foo" priv 123') 534 ); 535 } 536 537 /** 538 * @dataProvider previousSearchQueryProvider 539 */ 540 public function testPreviousSearchQuery($not, $fuzzy, $expected) 541 { 542 $ob = new Horde_Imap_Client_Search_Query(); 543 $ob->previousSearch($not, array('fuzzy' => $fuzzy)); 544 545 $this->assertEquals( 546 $expected, 547 $fuzzy ? $this->_fuzzy($ob, array('ESEARCH', 'SEARCHRES')) : strval($ob) 548 ); 549 } 550 551 public function previousSearchQueryProvider() 552 { 553 return array( 554 array(false, false, 'NOT $'), 555 array(true, false, '$'), 556 array(false, true, 'FUZZY NOT $'), 557 array(true, true, 'FUZZY $') 558 ); 559 } 560 561 public function testClone() 562 { 563 $ob = new Horde_Imap_Client_Search_Query(); 564 $ob->text('foo'); 565 566 $ob2 = clone $ob; 567 $ob2->text('bar'); 568 569 $this->assertEquals( 570 'BODY foo', 571 strval($ob) 572 ); 573 $this->assertEquals( 574 'BODY foo BODY bar', 575 strval($ob2) 576 ); 577 } 578 579 public function testSerialize() 580 { 581 $ob = new Horde_Imap_Client_Search_Query(); 582 $ob->ids(new Horde_Imap_Client_Ids('1:3'), true); 583 $ob->text('foo'); 584 $ob->charset('US-ASCII', false); 585 586 $this->assertEquals( 587 'NOT UID 1:3 BODY foo', 588 strval(unserialize(serialize($ob))) 589 ); 590 } 591 592 public function testBug13971() 593 { 594 $ob = new Horde_Imap_Client_Search_Query(); 595 $ob->ids(new Horde_Imap_Client_Ids(array())); 596 $ob->text('foo'); 597 598 $this->assertEquals( 599 '', 600 strval($ob) 601 ); 602 603 $ob2 = new Horde_Imap_Client_Search_Query(); 604 $ob2->text('foo2'); 605 $ob2->andSearch($ob); 606 607 $this->assertEquals( 608 '', 609 strval($ob2) 610 ); 611 612 $ob3 = new Horde_Imap_Client_Search_Query(); 613 $ob3->text('foo3'); 614 $ob3->orSearch($ob); 615 616 $this->assertEquals( 617 'BODY foo3', 618 strval($ob3) 619 ); 620 621 $ob2->orSearch($ob3); 622 623 $this->assertEquals( 624 'BODY foo3', 625 strval($ob2) 626 ); 627 628 /* A NOT qualifier on an empty ID list should ignore the list. */ 629 $ob->ids(new Horde_Imap_Client_Ids(array()), true); 630 631 $this->assertEquals( 632 'BODY foo', 633 strval($ob) 634 ); 635 } 636 637 /** 638 * @dataProvider inconsistentCharsetsInAndOrSearchesProvider 639 * @expectedException InvalidArgumentException 640 */ 641 public function testInconsistentCharsetsInAndOrSearches($query) 642 { 643 $query->build(null); 644 } 645 646 public function inconsistentCharsetsInAndOrSearchesProvider() 647 { 648 $ob = new Horde_Imap_Client_Search_Query(); 649 $ob->text('foo'); 650 $ob->charset('UTF-8', false); 651 652 $ob2 = new Horde_Imap_Client_Search_Query(); 653 $ob2->text('foo2'); 654 $ob2->charset('ISO-8859-1', false); 655 $ob2->andSearch($ob); 656 657 $ob3 = new Horde_Imap_Client_Search_Query(); 658 $ob3->text('foo2'); 659 $ob3->charset('ISO-8859-1', false); 660 $ob3->orSearch($ob); 661 662 return array(array($ob2), array($ob3)); 663 } 664 665 private function _fuzzy($ob, array $exts = array()) 666 { 667 $capability = new Horde_Imap_Client_Data_Capability_Imap(); 668 $capability->add('SEARCH', 'FUZZY'); 669 foreach ($exts as $val) { 670 $capability->add($val); 671 } 672 673 $res = $ob->build($capability); 674 return $res['query']->escape(); 675 } 676 677} 678