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