1*79305873SAndreas Gohr<?php 2*79305873SAndreas Gohr 3*79305873SAndreas Gohrnamespace dokuwiki\test\Feed; 4*79305873SAndreas Gohr 5*79305873SAndreas Gohruse dokuwiki\Feed\FeedParser; 6*79305873SAndreas Gohruse dokuwiki\Feed\FeedParserFile; 7*79305873SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 8*79305873SAndreas Gohr 9*79305873SAndreas Gohr/** 10*79305873SAndreas Gohr * Tests for fetching and parsing remote feeds 11*79305873SAndreas Gohr */ 12*79305873SAndreas Gohrclass FeedParserTest extends \DokuWikiTest 13*79305873SAndreas Gohr{ 14*79305873SAndreas Gohr protected const FEED = <<<XML 15*79305873SAndreas Gohr<?xml version="1.0" encoding="UTF-8"?> 16*79305873SAndreas Gohr<rss version="2.0"><channel> 17*79305873SAndreas Gohr<title>Example Feed</title> 18*79305873SAndreas Gohr<item><title>First Item</title><link>http://example.com/1</link><description>One</description></item> 19*79305873SAndreas Gohr<item><title>Second Item</title><link>http://example.com/2</link><description>Two</description></item> 20*79305873SAndreas Gohr</channel></rss> 21*79305873SAndreas GohrXML; 22*79305873SAndreas Gohr 23*79305873SAndreas Gohr /** 24*79305873SAndreas Gohr * Builds a FeedParserFile that fetches through a mocked DokuHTTPClient. 25*79305873SAndreas Gohr * 26*79305873SAndreas Gohr * FeedParserFile creates its client in initHTTPClient() and maps the 27*79305873SAndreas Gohr * DokuHTTPClient fields onto the SimplePie response in its constructor, so we stub 28*79305873SAndreas Gohr * the factory method and then run the real constructor against the canned response. 29*79305873SAndreas Gohr * 30*79305873SAndreas Gohr * @return FeedParserFile 31*79305873SAndreas Gohr */ 32*79305873SAndreas Gohr protected function fetchedFile($status, $error, $body, $headers = ['content-type' => 'text/xml']) 33*79305873SAndreas Gohr { 34*79305873SAndreas Gohr $http = $this->createMock(DokuHTTPClient::class); 35*79305873SAndreas Gohr $http->method('sendRequest')->willReturnCallback( 36*79305873SAndreas Gohr function () use ($http, $status, $error, $body, $headers) { 37*79305873SAndreas Gohr $http->status = $status; 38*79305873SAndreas Gohr $http->error = $error; 39*79305873SAndreas Gohr $http->resp_body = $body; 40*79305873SAndreas Gohr $http->resp_headers = $headers; 41*79305873SAndreas Gohr return $status >= 200 && $status < 300; 42*79305873SAndreas Gohr } 43*79305873SAndreas Gohr ); 44*79305873SAndreas Gohr 45*79305873SAndreas Gohr $file = $this->getMockBuilder(FeedParserFile::class) 46*79305873SAndreas Gohr ->onlyMethods(['initHTTPClient']) 47*79305873SAndreas Gohr ->disableOriginalConstructor() 48*79305873SAndreas Gohr ->getMock(); 49*79305873SAndreas Gohr $file->method('initHTTPClient')->willReturn($http); 50*79305873SAndreas Gohr $file->__construct('http://example.com/feed.xml'); 51*79305873SAndreas Gohr 52*79305873SAndreas Gohr return $file; 53*79305873SAndreas Gohr } 54*79305873SAndreas Gohr 55*79305873SAndreas Gohr /** 56*79305873SAndreas Gohr * On success DokuHTTPClient reports an empty string error and exposes the status 57*79305873SAndreas Gohr * separately. SimplePie's FileClient rejects any response whose error is non-null 58*79305873SAndreas Gohr * while the status code is zero, so the fetched file has to carry a real status code 59*79305873SAndreas Gohr * and a null error. Regression test for feed aggregation breaking after the 60*79305873SAndreas Gohr * SimplePie 1.9 upgrade. 61*79305873SAndreas Gohr */ 62*79305873SAndreas Gohr public function testSuccessfulResponseIsAcceptedBySimplePie() 63*79305873SAndreas Gohr { 64*79305873SAndreas Gohr $file = $this->fetchedFile(200, '', self::FEED); 65*79305873SAndreas Gohr 66*79305873SAndreas Gohr $this->assertSame(200, $file->get_status_code()); 67*79305873SAndreas Gohr $this->assertNull($file->error); 68*79305873SAndreas Gohr $this->assertNotEmpty($file->get_body_content()); 69*79305873SAndreas Gohr } 70*79305873SAndreas Gohr 71*79305873SAndreas Gohr /** 72*79305873SAndreas Gohr * A genuine connection failure must stay distinguishable from a success 73*79305873SAndreas Gohr */ 74*79305873SAndreas Gohr public function testFailedResponseKeepsStatusAndError() 75*79305873SAndreas Gohr { 76*79305873SAndreas Gohr $file = $this->fetchedFile(-100, 'Could not connect to server', ''); 77*79305873SAndreas Gohr 78*79305873SAndreas Gohr $this->assertSame(-100, $file->get_status_code()); 79*79305873SAndreas Gohr $this->assertSame('Could not connect to server', $file->error); 80*79305873SAndreas Gohr } 81*79305873SAndreas Gohr 82*79305873SAndreas Gohr /** 83*79305873SAndreas Gohr * The fetched body is parsed into feed items 84*79305873SAndreas Gohr */ 85*79305873SAndreas Gohr public function testFeedItemsAreParsed() 86*79305873SAndreas Gohr { 87*79305873SAndreas Gohr $feed = new FeedParser(); 88*79305873SAndreas Gohr $feed->enable_order_by_date(false); 89*79305873SAndreas Gohr $feed->set_feed_url('http://example.com/feed.xml'); 90*79305873SAndreas Gohr $feed->set_file($this->fetchedFile(200, '', self::FEED)); 91*79305873SAndreas Gohr $rc = $feed->init(); 92*79305873SAndreas Gohr 93*79305873SAndreas Gohr $this->assertTrue($rc); 94*79305873SAndreas Gohr $this->assertSame(2, $feed->get_item_quantity()); 95*79305873SAndreas Gohr $this->assertSame('First Item', $feed->get_item(0)->get_title()); 96*79305873SAndreas Gohr $this->assertSame('http://example.com/1', $feed->get_item(0)->get_permalink()); 97*79305873SAndreas Gohr $this->assertSame('Second Item', $feed->get_item(1)->get_title()); 98*79305873SAndreas Gohr } 99*79305873SAndreas Gohr} 100