4 +--------------------------------------------------------------------+
5 | CiviCRM version 4.6 |
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2014 |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
30 require_once 'CiviTest/CiviUnitTestCase.php';
33 * Class CRM_Core_CommunityMessagesTest
35 class CRM_Core_CommunityMessagesTest
extends CiviUnitTestCase
{
38 * The max difference between two times such that they should be
39 * treated as equals (expressed in seconds).
41 const APPROX_TIME_EQUALITY
= 2;
44 * @var CRM_Utils_Cache_Interface
49 * @var array list of possible web responses
51 protected static $webResponses = NULL;
56 public static function initWebResponses() {
57 if (self
::$webResponses === NULL) {
58 self
::$webResponses = array(
59 'http-error' => array(
60 CRM_Utils_HttpClient
::STATUS_DL_ERROR
,
64 CRM_Utils_HttpClient
::STATUS_OK
,
65 '<html>this is not json!</html>'
67 'invalid-ttl-document' => array(
68 CRM_Utils_HttpClient
::STATUS_OK
,
70 'ttl' => 'z', // not an integer!
71 'retry' => 'z', // not an integer!
74 'markup' => '<h1>Invalid document</h1>',
79 'first-valid-response' => array(
80 CRM_Utils_HttpClient
::STATUS_OK
,
86 'markup' => '<h1>First valid response</h1>',
91 'second-valid-response' => array(
92 CRM_Utils_HttpClient
::STATUS_OK
,
98 'markup' => '<h1>Second valid response</h1>',
103 'two-messages' => array(
104 CRM_Utils_HttpClient
::STATUS_OK
,
110 'markup' => '<h1>One</h1>',
111 'components' => array('CiviMail'),
114 'markup' => '<h1>Two</h1>',
115 'components' => array('CiviMail'),
120 'two-messages-halfbadcomp' => array(
121 CRM_Utils_HttpClient
::STATUS_OK
,
127 'markup' => '<h1>One</h1>',
128 'components' => array('NotARealComponent'),
131 'markup' => '<h1>Two</h1>',
132 'components' => array('CiviMail'),
139 return self
::$webResponses;
142 public function setUp() {
144 $this->cache
= new CRM_Utils_Cache_Arraycache(array());
145 self
::initWebResponses();
148 public function tearDown() {
150 CRM_Utils_Time
::resetTime();
154 * A list of bad web-responses; in general, whenever the downloader
155 * encounters one of these bad responses, it should ignore the
156 * document, retain the old data, and retry again later.
160 public function badWebResponses() {
161 self
::initWebResponses();
163 array(self
::$webResponses['http-error']),
164 array(self
::$webResponses['bad-json']),
165 array(self
::$webResponses['invalid-ttl-document']),
170 public function testIsEnabled() {
171 $communityMessages = new CRM_Core_CommunityMessages(
173 $this->expectNoHttpRequest()
175 $this->assertTrue($communityMessages->isEnabled());
178 public function testIsEnabled_false() {
179 $communityMessages = new CRM_Core_CommunityMessages(
181 $this->expectNoHttpRequest(),
184 $this->assertFalse($communityMessages->isEnabled());
188 * Download a document; after the set expiration period, download again.
190 public function testGetDocument_NewOK_CacheOK_UpdateOK() {
191 // first try, good response
192 CRM_Utils_Time
::setTime('2013-03-01 10:00:00');
193 $communityMessages = new CRM_Core_CommunityMessages(
195 $this->expectOneHttpRequest(self
::$webResponses['first-valid-response'])
197 $doc1 = $communityMessages->getDocument();
198 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
199 $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires'], self
::APPROX_TIME_EQUALITY
);
201 // second try, $doc1 hasn't expired yet, so still use it
202 CRM_Utils_Time
::setTime('2013-03-01 10:09:00');
203 $communityMessages = new CRM_Core_CommunityMessages(
205 $this->expectNoHttpRequest()
207 $doc2 = $communityMessages->getDocument();
208 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
209 $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc2['expires'], self
::APPROX_TIME_EQUALITY
);
211 // third try, $doc1 expired, update it
212 CRM_Utils_Time
::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
213 $communityMessages = new CRM_Core_CommunityMessages(
215 $this->expectOneHttpRequest(self
::$webResponses['second-valid-response'])
217 $doc3 = $communityMessages->getDocument();
218 $this->assertEquals('<h1>Second valid response</h1>', $doc3['messages'][0]['markup']);
219 $this->assertApproxEquals(strtotime('2013-03-01 12:10:02'), $doc3['expires'], self
::APPROX_TIME_EQUALITY
);
223 * First download attempt fails (due to some bad web request).
224 * Store the NACK and retry after the default time period (DEFAULT_RETRY).
226 * @dataProvider badWebResponses
227 * @param array $badWebResponse Description of a web request that returns some kind of failure
229 public function testGetDocument_NewFailure_CacheOK_UpdateOK($badWebResponse) {
230 $this->assertNotEmpty($badWebResponse);
232 // first try, bad response
233 CRM_Utils_Time
::setTime('2013-03-01 10:00:00');
234 $communityMessages = new CRM_Core_CommunityMessages(
236 $this->expectOneHttpRequest($badWebResponse)
238 $doc1 = $communityMessages->getDocument();
239 $this->assertEquals(array(), $doc1['messages']);
240 $this->assertTrue($doc1['expires'] > CRM_Utils_Time
::getTimeRaw());
242 // second try, $doc1 hasn't expired yet, so still use it
243 CRM_Utils_Time
::setTime('2013-03-01 10:09:00');
244 $communityMessages = new CRM_Core_CommunityMessages(
246 $this->expectNoHttpRequest()
248 $doc2 = $communityMessages->getDocument();
249 $this->assertEquals(array(), $doc2['messages']);
250 $this->assertEquals($doc1['expires'], $doc2['expires']);
252 // third try, $doc1 expired, try again, get a good response
253 CRM_Utils_Time
::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
254 $communityMessages = new CRM_Core_CommunityMessages(
256 $this->expectOneHttpRequest(self
::$webResponses['first-valid-response'])
258 $doc3 = $communityMessages->getDocument();
259 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
260 $this->assertTrue($doc3['expires'] > CRM_Utils_Time
::getTimeRaw());
264 * First download of new doc is OK.
265 * The update fails (due to some bad web response).
266 * The old data is retained in the cache.
267 * The failure eventually expires.
268 * A new update succeeds.
270 * @dataProvider badWebResponses
271 * @param array $badWebResponse Description of a web request that returns some kind of failure
273 public function testGetDocument_NewOK_UpdateFailure_CacheOK_UpdateOK($badWebResponse) {
274 $this->assertNotEmpty($badWebResponse);
276 // first try, good response
277 CRM_Utils_Time
::setTime('2013-03-01 10:00:00');
278 $communityMessages = new CRM_Core_CommunityMessages(
280 $this->expectOneHttpRequest(self
::$webResponses['first-valid-response'])
282 $doc1 = $communityMessages->getDocument();
283 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
284 $this->assertApproxEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires'], self
::APPROX_TIME_EQUALITY
);
286 // second try, $doc1 has expired; bad response; keep old data
287 CRM_Utils_Time
::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
288 $communityMessages = new CRM_Core_CommunityMessages(
290 $this->expectOneHttpRequest($badWebResponse)
292 $doc2 = $communityMessages->getDocument();
293 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
294 $this->assertTrue($doc2['expires'] > CRM_Utils_Time
::getTimeRaw());
296 // third try, $doc2 hasn't expired yet; no request; keep old data
297 CRM_Utils_Time
::setTime('2013-03-01 12:09:00');
298 $communityMessages = new CRM_Core_CommunityMessages(
300 $this->expectNoHttpRequest()
302 $doc3 = $communityMessages->getDocument();
303 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
304 $this->assertEquals($doc2['expires'], $doc3['expires']);
306 // fourth try, $doc2 has expired yet; new request; replace data
307 CRM_Utils_Time
::setTime('2013-03-01 12:10:02');
308 $communityMessages = new CRM_Core_CommunityMessages(
310 $this->expectOneHttpRequest(self
::$webResponses['second-valid-response'])
312 $doc4 = $communityMessages->getDocument();
313 $this->assertEquals('<h1>Second valid response</h1>', $doc4['messages'][0]['markup']);
314 $this->assertApproxEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires'], self
::APPROX_TIME_EQUALITY
);
318 * Randomly pick among two options
320 public function testPick_rand() {
321 $communityMessages = new CRM_Core_CommunityMessages(
323 $this->expectOneHttpRequest(self
::$webResponses['two-messages'])
325 $doc1 = $communityMessages->getDocument();
326 $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
327 $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
329 // randomly pick many times
331 $freq = array(); // array($message => $count)
332 for ($i = 0; $i < $trials; $i++
) {
333 $message = $communityMessages->pick();
334 $freq[$message['markup']] = CRM_Utils_Array
::value($message['markup'], $freq, 0) +
1;
337 // assert the probabilities
338 $this->assertApproxEquals(0.5, $freq['<h1>One</h1>'] / $trials, 0.3);
339 $this->assertApproxEquals(0.5, $freq['<h1>Two</h1>'] / $trials, 0.3);
340 $this->assertEquals($trials, $freq['<h1>One</h1>'] +
$freq['<h1>Two</h1>']);
344 * When presented with two options using component filters, always
345 * choose the one which references an active component.
347 public function testPick_componentFilter() {
348 $communityMessages = new CRM_Core_CommunityMessages(
350 $this->expectOneHttpRequest(self
::$webResponses['two-messages-halfbadcomp'])
352 $doc1 = $communityMessages->getDocument();
353 $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
354 $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
356 // randomly pick many times
358 $freq = array(); // array($message => $count)
359 for ($i = 0; $i < $trials; $i++
) {
360 $message = $communityMessages->pick();
361 $freq[$message['markup']] = CRM_Utils_Array
::value($message['markup'], $freq, 0) +
1;
364 $this->assertEquals($trials, $freq['<h1>Two</h1>']);
367 public function testEvalMarkup() {
368 $communityMessages = new CRM_Core_CommunityMessages(
370 $this->expectNoHttpRequest()
372 $this->assertEquals('cms=UnitTests cms=UnitTests', $communityMessages->evalMarkup('cms=%%uf%% cms={{uf}}'));
376 * Generate a mock HTTP client with the expectation that it is never called.
378 * @return CRM_Utils_HttpClient|PHPUnit_Framework_MockObject_MockObject
380 protected function expectNoHttpRequest() {
381 $client = $this->getMock('CRM_Utils_HttpClient');
382 $client->expects($this->never())
388 * Generate a mock HTTP client with the expectation that it is called once.
392 * @return CRM_Utils_HttpClient|PHPUnit_Framework_MockObject_MockObject
394 protected function expectOneHttpRequest($response) {
395 $client = $this->getMock('CRM_Utils_HttpClient');
396 $client->expects($this->once())
398 ->will($this->returnValue($response));