[ CRM_Utils_HttpClient::STATUS_DL_ERROR, NULL, ], 'bad-json' => [ CRM_Utils_HttpClient::STATUS_OK, 'this is not json!', ], 'invalid-ttl-document' => [ CRM_Utils_HttpClient::STATUS_OK, json_encode([ // not an integer! 'ttl' => 'z', // not an integer! 'retry' => 'z', 'messages' => [ [ 'markup' => '

Invalid document

', ], ], ]), ], 'first-valid-response' => [ CRM_Utils_HttpClient::STATUS_OK, json_encode([ 'ttl' => 600, 'retry' => 600, 'messages' => [ [ 'markup' => '

First valid response

', ], ], ]), ], 'second-valid-response' => [ CRM_Utils_HttpClient::STATUS_OK, json_encode([ 'ttl' => 600, 'retry' => 600, 'messages' => [ [ 'markup' => '

Second valid response

', ], ], ]), ], 'two-messages' => [ CRM_Utils_HttpClient::STATUS_OK, json_encode([ 'ttl' => 600, 'retry' => 600, 'messages' => [ [ 'markup' => '

One

', 'components' => ['CiviMail'], ], [ 'markup' => '

Two

', 'components' => ['CiviMail'], ], ], ]), ], 'two-messages-halfbadcomp' => [ CRM_Utils_HttpClient::STATUS_OK, json_encode([ 'ttl' => 600, 'retry' => 600, 'messages' => [ [ 'markup' => '

One

', 'components' => ['NotARealComponent'], ], [ 'markup' => '

Two

', 'components' => ['CiviMail'], ], ], ]), ], ]; } return self::$webResponses; } public function setUp() { parent::setUp(); $this->cache = new CRM_Utils_Cache_Arraycache([]); self::initWebResponses(); } public function tearDown() { parent::tearDown(); CRM_Utils_Time::resetTime(); } /** * A list of bad web-responses; in general, whenever the downloader * encounters one of these bad responses, it should ignore the * document, retain the old data, and retry again later. * * @return array */ public function badWebResponses() { self::initWebResponses(); $result = [ [self::$webResponses['http-error']], [self::$webResponses['bad-json']], [self::$webResponses['invalid-ttl-document']], ]; return $result; } public function testIsEnabled() { $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest() ); $this->assertTrue($communityMessages->isEnabled()); } public function testIsEnabled_false() { $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest(), FALSE ); $this->assertFalse($communityMessages->isEnabled()); } /** * Download a document; after the set expiration period, download again. */ public function testGetDocument_NewOK_CacheOK_UpdateOK() { // first try, good response CRM_Utils_Time::setTime('2013-03-01 10:00:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['first-valid-response']) ); $doc1 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc1['messages'][0]['markup']); $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires'], self::APPROX_TIME_EQUALITY); // second try, $doc1 hasn't expired yet, so still use it CRM_Utils_Time::setTime('2013-03-01 10:09:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest() ); $doc2 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc2['messages'][0]['markup']); $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc2['expires'], self::APPROX_TIME_EQUALITY); // third try, $doc1 expired, update it // more than 2 hours later (DEFAULT_RETRY) CRM_Utils_Time::setTime('2013-03-01 12:00:02'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['second-valid-response']) ); $doc3 = $communityMessages->getDocument(); $this->assertEquals('

Second valid response

', $doc3['messages'][0]['markup']); $this->assertApproxEquals(strtotime('2013-03-01 12:10:02'), $doc3['expires'], self::APPROX_TIME_EQUALITY); } /** * First download attempt fails (due to some bad web request). * Store the NACK and retry after the default time period (DEFAULT_RETRY). * * @dataProvider badWebResponses * @param array $badWebResponse * Description of a web request that returns some kind of failure. */ public function testGetDocument_NewFailure_CacheOK_UpdateOK($badWebResponse) { $this->assertNotEmpty($badWebResponse); // first try, bad response CRM_Utils_Time::setTime('2013-03-01 10:00:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest($badWebResponse) ); $doc1 = $communityMessages->getDocument(); $this->assertEquals([], $doc1['messages']); $this->assertTrue($doc1['expires'] > CRM_Utils_Time::getTimeRaw()); // second try, $doc1 hasn't expired yet, so still use it CRM_Utils_Time::setTime('2013-03-01 10:09:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest() ); $doc2 = $communityMessages->getDocument(); $this->assertEquals([], $doc2['messages']); $this->assertEquals($doc1['expires'], $doc2['expires']); // third try, $doc1 expired, try again, get a good response // more than 2 hours later (DEFAULT_RETRY) CRM_Utils_Time::setTime('2013-03-01 12:00:02'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['first-valid-response']) ); $doc3 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc3['messages'][0]['markup']); $this->assertTrue($doc3['expires'] > CRM_Utils_Time::getTimeRaw()); } /** * First download of new doc is OK. * The update fails (due to some bad web response). * The old data is retained in the cache. * The failure eventually expires. * A new update succeeds. * * @dataProvider badWebResponses * @param array $badWebResponse * Description of a web request that returns some kind of failure. */ public function testGetDocument_NewOK_UpdateFailure_CacheOK_UpdateOK($badWebResponse) { $this->assertNotEmpty($badWebResponse); // first try, good response CRM_Utils_Time::setTime('2013-03-01 10:00:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['first-valid-response']) ); $doc1 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc1['messages'][0]['markup']); $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires'], self::APPROX_TIME_EQUALITY); // second try, $doc1 has expired; bad response; keep old data // more than 2 hours later (DEFAULT_RETRY) CRM_Utils_Time::setTime('2013-03-01 12:00:02'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest($badWebResponse) ); $doc2 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc2['messages'][0]['markup']); $this->assertTrue($doc2['expires'] > CRM_Utils_Time::getTimeRaw()); // third try, $doc2 hasn't expired yet; no request; keep old data CRM_Utils_Time::setTime('2013-03-01 12:09:00'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest() ); $doc3 = $communityMessages->getDocument(); $this->assertEquals('

First valid response

', $doc3['messages'][0]['markup']); $this->assertEquals($doc2['expires'], $doc3['expires']); // fourth try, $doc2 has expired yet; new request; replace data CRM_Utils_Time::setTime('2013-03-01 12:10:02'); $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['second-valid-response']) ); $doc4 = $communityMessages->getDocument(); $this->assertEquals('

Second valid response

', $doc4['messages'][0]['markup']); $this->assertApproxEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires'], self::APPROX_TIME_EQUALITY); } /** * Randomly pick among two options. */ public function testPick_rand() { $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['two-messages']) ); $doc1 = $communityMessages->getDocument(); $this->assertEquals('

One

', $doc1['messages'][0]['markup']); $this->assertEquals('

Two

', $doc1['messages'][1]['markup']); // randomly pick many times $trials = 80; // array($message => $count) $freq = []; for ($i = 0; $i < $trials; $i++) { $message = $communityMessages->pick(); $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1; } // assert the probabilities $this->assertApproxEquals(0.5, $freq['

One

'] / $trials, 0.3); $this->assertApproxEquals(0.5, $freq['

Two

'] / $trials, 0.3); $this->assertEquals($trials, $freq['

One

'] + $freq['

Two

']); } /** * When presented with two options using component filters, always * choose the one which references an active component. */ public function testPick_componentFilter() { $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectOneHttpRequest(self::$webResponses['two-messages-halfbadcomp']) ); $doc1 = $communityMessages->getDocument(); $this->assertEquals('

One

', $doc1['messages'][0]['markup']); $this->assertEquals('

Two

', $doc1['messages'][1]['markup']); // randomly pick many times $trials = 10; // array($message => $count) $freq = []; for ($i = 0; $i < $trials; $i++) { $message = $communityMessages->pick(); $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1; } $this->assertEquals($trials, $freq['

Two

']); } public function testEvalMarkup() { $communityMessages = new CRM_Core_CommunityMessages( $this->cache, $this->expectNoHttpRequest() ); $this->assertEquals('cms=UnitTests cms=UnitTests', $communityMessages->evalMarkup('cms=%%uf%% cms={{uf}}')); } /** * Generate a mock HTTP client with the expectation that it is never called. * * @return CRM_Utils_HttpClient|PHPUnit\Framework\MockObject\MockObject */ protected function expectNoHttpRequest() { $mockFunction = $this->mockMethod; $client = $this->$mockFunction('CRM_Utils_HttpClient'); $client->expects($this->never()) ->method('get'); return $client; } /** * Generate a mock HTTP client with the expectation that it is called once. * * @param $response * * @return CRM_Utils_HttpClient|PHPUnit\Framework\MockObject\MockObject */ protected function expectOneHttpRequest($response) { $mockFunction = $this->mockMethod; $client = $this->$mockFunction('CRM_Utils_HttpClient'); $client->expects($this->once()) ->method('get') ->will($this->returnValue($response)); return $client; } }