Merge pull request #3689 from atif-shaikh/CRM-14999
[civicrm-core.git] / tests / phpunit / CRM / Core / CommunityMessagesTest.php
CommitLineData
ecbe1139
TO
1<?php
2
3/*
4 +--------------------------------------------------------------------+
06a1bc01 5 | CiviCRM version 4.5 |
ecbe1139 6 +--------------------------------------------------------------------+
06a1bc01 7 | Copyright CiviCRM LLC (c) 2004-2014 |
ecbe1139
TO
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
10 | |
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. |
14 | |
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. |
19 | |
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 +--------------------------------------------------------------------+
27*/
28
29
30require_once 'CiviTest/CiviUnitTestCase.php';
e9479dcf
EM
31
32/**
33 * Class CRM_Core_CommunityMessagesTest
34 */
ecbe1139 35class CRM_Core_CommunityMessagesTest extends CiviUnitTestCase {
ecbe1139
TO
36 /**
37 * @var CRM_Utils_Cache_Interface
38 */
39 protected $cache;
40
41 /**
42 * @var array list of possible web responses
43 */
376d8353 44 protected static $webResponses = NULL;
ecbe1139 45
4cbe18b8
EM
46 /**
47 * @return array
48 */
376d8353
TO
49 public static function initWebResponses() {
50 if (self::$webResponses === NULL) {
51 self::$webResponses = array(
52 'http-error' => array(
53 CRM_Utils_HttpClient::STATUS_DL_ERROR,
54 NULL
55 ),
56 'bad-json' => array(
57 CRM_Utils_HttpClient::STATUS_OK,
58 '<html>this is not json!</html>'
59 ),
60 'invalid-ttl-document' => array(
61 CRM_Utils_HttpClient::STATUS_OK,
62 json_encode(array(
63 'ttl' => 'z', // not an integer!
64 'retry' => 'z', // not an integer!
65 'messages' => array(
66 array(
67 'markup' => '<h1>Invalid document</h1>',
68 ),
8bfc3cb1 69 ),
376d8353
TO
70 ))
71 ),
3dcd50d8 72 'first-valid-response' => array(
376d8353
TO
73 CRM_Utils_HttpClient::STATUS_OK,
74 json_encode(array(
75 'ttl' => 600,
76 'retry' => 600,
77 'messages' => array(
78 array(
3dcd50d8 79 'markup' => '<h1>First valid response</h1>',
376d8353 80 ),
ecbe1139 81 ),
376d8353
TO
82 ))
83 ),
3dcd50d8 84 'second-valid-response' => array(
376d8353
TO
85 CRM_Utils_HttpClient::STATUS_OK,
86 json_encode(array(
87 'ttl' => 600,
88 'retry' => 600,
89 'messages' => array(
90 array(
3dcd50d8 91 'markup' => '<h1>Second valid response</h1>',
376d8353 92 ),
ecbe1139 93 ),
376d8353
TO
94 ))
95 ),
dc92f2f8
TO
96 'two-messages' => array(
97 CRM_Utils_HttpClient::STATUS_OK,
98 json_encode(array(
99 'ttl' => 600,
100 'retry' => 600,
101 'messages' => array(
102 array(
103 'markup' => '<h1>One</h1>',
104 'components' => array('CiviMail'),
105 ),
106 array(
107 'markup' => '<h1>Two</h1>',
108 'components' => array('CiviMail'),
109 ),
110 ),
111 ))
112 ),
113 'two-messages-halfbadcomp' => array(
114 CRM_Utils_HttpClient::STATUS_OK,
115 json_encode(array(
116 'ttl' => 600,
117 'retry' => 600,
118 'messages' => array(
119 array(
120 'markup' => '<h1>One</h1>',
121 'components' => array('NotARealComponent'),
122 ),
123 array(
124 'markup' => '<h1>Two</h1>',
125 'components' => array('CiviMail'),
126 ),
127 ),
128 ))
129 ),
376d8353
TO
130 );
131 }
132 return self::$webResponses;
133 }
134
135 public function setUp() {
136 parent::setUp();
137 $this->cache = new CRM_Utils_Cache_Arraycache(array());
138 self::initWebResponses();
ecbe1139
TO
139 }
140
141 public function tearDown() {
142 parent::tearDown();
143 CRM_Utils_Time::resetTime();
144 }
145
acdf9ae2
TO
146 /**
147 * A list of bad web-responses; in general, whenever the downloader
148 * encounters one of these bad responses, it should ignore the
149 * document, retain the old data, and retry again later.
150 *
151 * @return array
152 */
153 public function badWebResponses() {
376d8353
TO
154 self::initWebResponses();
155 $result = array(
156 array(self::$webResponses['http-error']),
157 array(self::$webResponses['bad-json']),
158 array(self::$webResponses['invalid-ttl-document']),
159 );
160 return $result;
161 }
162
847c93ac
TO
163 public function testIsEnabled() {
164 $communityMessages = new CRM_Core_CommunityMessages(
165 $this->cache,
166 $this->expectNoHttpRequest()
167 );
168 $this->assertTrue($communityMessages->isEnabled());
169 }
170
171 public function testIsEnabled_false() {
e8977170
TO
172 $communityMessages = new CRM_Core_CommunityMessages(
173 $this->cache,
174 $this->expectNoHttpRequest(),
175 FALSE
176 );
847c93ac 177 $this->assertFalse($communityMessages->isEnabled());
e8977170
TO
178 }
179
ecbe1139
TO
180 /**
181 * Download a document; after the set expiration period, download again.
182 */
e8977170 183 public function testGetDocument_NewOK_CacheOK_UpdateOK() {
ecbe1139
TO
184 // first try, good response
185 CRM_Utils_Time::setTime('2013-03-01 10:00:00');
186 $communityMessages = new CRM_Core_CommunityMessages(
187 $this->cache,
3dcd50d8 188 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
ecbe1139
TO
189 );
190 $doc1 = $communityMessages->getDocument();
3dcd50d8 191 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
ecbe1139
TO
192 $this->assertEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires']);
193
194 // second try, $doc1 hasn't expired yet, so still use it
195 CRM_Utils_Time::setTime('2013-03-01 10:09:00');
196 $communityMessages = new CRM_Core_CommunityMessages(
197 $this->cache,
198 $this->expectNoHttpRequest()
199 );
200 $doc2 = $communityMessages->getDocument();
3dcd50d8 201 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
ecbe1139
TO
202 $this->assertEquals(strtotime('2013-03-01 10:10:00'), $doc2['expires']);
203
204 // third try, $doc1 expired, update it
205 CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
206 $communityMessages = new CRM_Core_CommunityMessages(
207 $this->cache,
3dcd50d8 208 $this->expectOneHttpRequest(self::$webResponses['second-valid-response'])
ecbe1139
TO
209 );
210 $doc3 = $communityMessages->getDocument();
3dcd50d8 211 $this->assertEquals('<h1>Second valid response</h1>', $doc3['messages'][0]['markup']);
ecbe1139
TO
212 $this->assertEquals(strtotime('2013-03-01 12:10:02'), $doc3['expires']);
213 }
214
215 /**
376d8353
TO
216 * First download attempt fails (due to some bad web request).
217 * Store the NACK and retry after the default time period (DEFAULT_RETRY).
218 *
acdf9ae2
TO
219 * @dataProvider badWebResponses
220 * @param array $badWebResponse Description of a web request that returns some kind of failure
ecbe1139 221 */
acdf9ae2
TO
222 public function testGetDocument_NewFailure_CacheOK_UpdateOK($badWebResponse) {
223 $this->assertNotEmpty($badWebResponse);
376d8353 224
ecbe1139
TO
225 // first try, bad response
226 CRM_Utils_Time::setTime('2013-03-01 10:00:00');
227 $communityMessages = new CRM_Core_CommunityMessages(
228 $this->cache,
acdf9ae2 229 $this->expectOneHttpRequest($badWebResponse)
ecbe1139
TO
230 );
231 $doc1 = $communityMessages->getDocument();
232 $this->assertEquals(array(), $doc1['messages']);
233 $this->assertTrue($doc1['expires'] > CRM_Utils_Time::getTimeRaw());
234
235 // second try, $doc1 hasn't expired yet, so still use it
236 CRM_Utils_Time::setTime('2013-03-01 10:09:00');
237 $communityMessages = new CRM_Core_CommunityMessages(
238 $this->cache,
239 $this->expectNoHttpRequest()
240 );
241 $doc2 = $communityMessages->getDocument();
242 $this->assertEquals(array(), $doc2['messages']);
243 $this->assertEquals($doc1['expires'], $doc2['expires']);
244
245 // third try, $doc1 expired, try again, get a good response
246 CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
247 $communityMessages = new CRM_Core_CommunityMessages(
248 $this->cache,
3dcd50d8 249 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
ecbe1139
TO
250 );
251 $doc3 = $communityMessages->getDocument();
3dcd50d8 252 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
ecbe1139
TO
253 $this->assertTrue($doc3['expires'] > CRM_Utils_Time::getTimeRaw());
254 }
255
256 /**
257 * First download of new doc is OK.
acdf9ae2
TO
258 * The update fails (due to some bad web response).
259 * The old data is retained in the cache.
260 * The failure eventually expires.
261 * A new update succeeds.
376d8353 262 *
acdf9ae2
TO
263 * @dataProvider badWebResponses
264 * @param array $badWebResponse Description of a web request that returns some kind of failure
ecbe1139 265 */
acdf9ae2
TO
266 public function testGetDocument_NewOK_UpdateFailure_CacheOK_UpdateOK($badWebResponse) {
267 $this->assertNotEmpty($badWebResponse);
376d8353 268
ecbe1139
TO
269 // first try, good response
270 CRM_Utils_Time::setTime('2013-03-01 10:00:00');
271 $communityMessages = new CRM_Core_CommunityMessages(
272 $this->cache,
3dcd50d8 273 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
ecbe1139
TO
274 );
275 $doc1 = $communityMessages->getDocument();
3dcd50d8 276 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
ecbe1139
TO
277 $this->assertEquals(strtotime('2013-03-01 10:10:00'), $doc1['expires']);
278
279 // second try, $doc1 has expired; bad response; keep old data
280 CRM_Utils_Time::setTime('2013-03-01 12:00:02'); // more than 2 hours later (DEFAULT_RETRY)
281 $communityMessages = new CRM_Core_CommunityMessages(
282 $this->cache,
acdf9ae2 283 $this->expectOneHttpRequest($badWebResponse)
ecbe1139
TO
284 );
285 $doc2 = $communityMessages->getDocument();
3dcd50d8 286 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
ecbe1139
TO
287 $this->assertTrue($doc2['expires'] > CRM_Utils_Time::getTimeRaw());
288
289 // third try, $doc2 hasn't expired yet; no request; keep old data
290 CRM_Utils_Time::setTime('2013-03-01 12:09:00');
291 $communityMessages = new CRM_Core_CommunityMessages(
292 $this->cache,
293 $this->expectNoHttpRequest()
294 );
295 $doc3 = $communityMessages->getDocument();
3dcd50d8 296 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
ecbe1139
TO
297 $this->assertEquals($doc2['expires'], $doc3['expires']);
298
299 // fourth try, $doc2 has expired yet; new request; replace data
300 CRM_Utils_Time::setTime('2013-03-01 12:10:02');
301 $communityMessages = new CRM_Core_CommunityMessages(
302 $this->cache,
3dcd50d8 303 $this->expectOneHttpRequest(self::$webResponses['second-valid-response'])
ecbe1139
TO
304 );
305 $doc4 = $communityMessages->getDocument();
3dcd50d8 306 $this->assertEquals('<h1>Second valid response</h1>', $doc4['messages'][0]['markup']);
ecbe1139
TO
307 $this->assertEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires']);
308 }
309
dc92f2f8
TO
310 /**
311 * Randomly pick among two options
312 */
313 public function testPick_rand() {
314 $communityMessages = new CRM_Core_CommunityMessages(
315 $this->cache,
316 $this->expectOneHttpRequest(self::$webResponses['two-messages'])
317 );
318 $doc1 = $communityMessages->getDocument();
319 $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
320 $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
321
322 // randomly pick many times
2e794830 323 $trials = 80;
dc92f2f8
TO
324 $freq = array(); // array($message => $count)
325 for ($i = 0; $i < $trials; $i++) {
326 $message = $communityMessages->pick();
af2dfa5c 327 $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1;
dc92f2f8
TO
328 }
329
330 // assert the probabilities
2e794830
TO
331 $this->assertApproxEquals(0.5, $freq['<h1>One</h1>'] / $trials, 0.3);
332 $this->assertApproxEquals(0.5, $freq['<h1>Two</h1>'] / $trials, 0.3);
dc92f2f8
TO
333 $this->assertEquals($trials, $freq['<h1>One</h1>'] + $freq['<h1>Two</h1>']);
334 }
335
336 /**
337 * When presented with two options using component filters, always
338 * choose the one which references an active component.
339 */
340 public function testPick_componentFilter() {
341 $communityMessages = new CRM_Core_CommunityMessages(
342 $this->cache,
343 $this->expectOneHttpRequest(self::$webResponses['two-messages-halfbadcomp'])
344 );
345 $doc1 = $communityMessages->getDocument();
346 $this->assertEquals('<h1>One</h1>', $doc1['messages'][0]['markup']);
347 $this->assertEquals('<h1>Two</h1>', $doc1['messages'][1]['markup']);
348
349 // randomly pick many times
350 $trials = 10;
351 $freq = array(); // array($message => $count)
352 for ($i = 0; $i < $trials; $i++) {
353 $message = $communityMessages->pick();
af2dfa5c 354 $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1;
dc92f2f8
TO
355 }
356
357 $this->assertEquals($trials, $freq['<h1>Two</h1>']);
358 }
359
d1b65097
TO
360 function testEvalMarkup() {
361 $communityMessages = new CRM_Core_CommunityMessages(
362 $this->cache,
363 $this->expectNoHttpRequest()
364 );
365 $this->assertEquals('cms=UnitTests cms=UnitTests', $communityMessages->evalMarkup('cms=%%uf%% cms={{uf}}'));
366 }
367
ecbe1139
TO
368 /**
369 * Generate a mock HTTP client with the expectation that it is never called.
370 *
371 * @return CRM_Utils_HttpClient|PHPUnit_Framework_MockObject_MockObject
372 */
373 protected function expectNoHttpRequest() {
374 $client = $this->getMock('CRM_Utils_HttpClient');
375 $client->expects($this->never())
376 ->method('get');
377 return $client;
378 }
379
380 /**
381 * Generate a mock HTTP client with the expectation that it is called once.
382 *
2a6da8d7
EM
383 * @param $response
384 *
ecbe1139
TO
385 * @return CRM_Utils_HttpClient|PHPUnit_Framework_MockObject_MockObject
386 */
387 protected function expectOneHttpRequest($response) {
388 $client = $this->getMock('CRM_Utils_HttpClient');
389 $client->expects($this->once())
390 ->method('get')
391 ->will($this->returnValue($response));
392 return $client;
393 }
394}