array(
CRM_Utils_HttpClient::STATUS_DL_ERROR,
NULL
),
'bad-json' => array(
CRM_Utils_HttpClient::STATUS_OK,
'this is not json!'
),
'invalid-ttl-document' => array(
CRM_Utils_HttpClient::STATUS_OK,
json_encode(array(
'ttl' => 'z', // not an integer!
'retry' => 'z', // not an integer!
'messages' => array(
array(
'markup' => '
Invalid document
',
),
),
))
),
'first-valid-response' => array(
CRM_Utils_HttpClient::STATUS_OK,
json_encode(array(
'ttl' => 600,
'retry' => 600,
'messages' => array(
array(
'markup' => 'First valid response
',
),
),
))
),
'second-valid-response' => array(
CRM_Utils_HttpClient::STATUS_OK,
json_encode(array(
'ttl' => 600,
'retry' => 600,
'messages' => array(
array(
'markup' => 'Second valid response
',
),
),
))
),
'two-messages' => array(
CRM_Utils_HttpClient::STATUS_OK,
json_encode(array(
'ttl' => 600,
'retry' => 600,
'messages' => array(
array(
'markup' => 'One
',
'components' => array('CiviMail'),
),
array(
'markup' => 'Two
',
'components' => array('CiviMail'),
),
),
))
),
'two-messages-halfbadcomp' => array(
CRM_Utils_HttpClient::STATUS_OK,
json_encode(array(
'ttl' => 600,
'retry' => 600,
'messages' => array(
array(
'markup' => 'One
',
'components' => array('NotARealComponent'),
),
array(
'markup' => 'Two
',
'components' => array('CiviMail'),
),
),
))
),
);
}
return self::$webResponses;
}
public function setUp() {
parent::setUp();
$this->cache = new CRM_Utils_Cache_Arraycache(array());
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 = array(
array(self::$webResponses['http-error']),
array(self::$webResponses['bad-json']),
array(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->assertEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires']);
// 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->assertEquals(strtotime('2013-03-01 10:10:00'), $doc2['expires']);
// third try, $doc1 expired, update it
CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
$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->assertEquals(strtotime('2013-03-01 12:10:02'), $doc3['expires']);
}
/**
* 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(array(), $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(array(), $doc2['messages']);
$this->assertEquals($doc1['expires'], $doc2['expires']);
// third try, $doc1 expired, try again, get a good response
CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
$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->assertEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires']);
// second try, $doc1 has expired; bad response; keep old data
CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
$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->assertEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires']);
}
/**
* 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;
$freq = array(); // array($message => $count)
for ($i = 0; $i < $trials; $i++) {
$message = $communityMessages->pick();
$freq[$message['markup']]++;
}
// 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;
$freq = array(); // array($message => $count)
for ($i = 0; $i < $trials; $i++) {
$message = $communityMessages->pick();
$freq[$message['markup']]++;
}
$this->assertEquals($trials, $freq['Two
']);
}
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() {
$client = $this->getMock('CRM_Utils_HttpClient');
$client->expects($this->never())
->method('get');
return $client;
}
/**
* Generate a mock HTTP client with the expectation that it is called once.
*
* @return CRM_Utils_HttpClient|PHPUnit_Framework_MockObject_MockObject
*/
protected function expectOneHttpRequest($response) {
$client = $this->getMock('CRM_Utils_HttpClient');
$client->expects($this->once())
->method('get')
->will($this->returnValue($response));
return $client;
}
}