add autogenerated comments
[civicrm-core.git] / tests / phpunit / CRM / Core / CommunityMessagesTest.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | CiviCRM version 4.5 |
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2014 |
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
30 require_once 'CiviTest/CiviUnitTestCase.php';
31
32 /**
33 * Class CRM_Core_CommunityMessagesTest
34 */
35 class CRM_Core_CommunityMessagesTest extends CiviUnitTestCase {
36 /**
37 * @var CRM_Utils_Cache_Interface
38 */
39 protected $cache;
40
41 /**
42 * @var array list of possible web responses
43 */
44 protected static $webResponses = NULL;
45
46 /**
47 * @return array
48 */
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 ),
69 ),
70 ))
71 ),
72 'first-valid-response' => array(
73 CRM_Utils_HttpClient::STATUS_OK,
74 json_encode(array(
75 'ttl' => 600,
76 'retry' => 600,
77 'messages' => array(
78 array(
79 'markup' => '<h1>First valid response</h1>',
80 ),
81 ),
82 ))
83 ),
84 'second-valid-response' => array(
85 CRM_Utils_HttpClient::STATUS_OK,
86 json_encode(array(
87 'ttl' => 600,
88 'retry' => 600,
89 'messages' => array(
90 array(
91 'markup' => '<h1>Second valid response</h1>',
92 ),
93 ),
94 ))
95 ),
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 ),
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();
139 }
140
141 public function tearDown() {
142 parent::tearDown();
143 CRM_Utils_Time::resetTime();
144 }
145
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() {
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
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() {
172 $communityMessages = new CRM_Core_CommunityMessages(
173 $this->cache,
174 $this->expectNoHttpRequest(),
175 FALSE
176 );
177 $this->assertFalse($communityMessages->isEnabled());
178 }
179
180 /**
181 * Download a document; after the set expiration period, download again.
182 */
183 public function testGetDocument_NewOK_CacheOK_UpdateOK() {
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,
188 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
189 );
190 $doc1 = $communityMessages->getDocument();
191 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
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();
201 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
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,
208 $this->expectOneHttpRequest(self::$webResponses['second-valid-response'])
209 );
210 $doc3 = $communityMessages->getDocument();
211 $this->assertEquals('<h1>Second valid response</h1>', $doc3['messages'][0]['markup']);
212 $this->assertEquals(strtotime('2013-03-01 12:10:02'), $doc3['expires']);
213 }
214
215 /**
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 *
219 * @dataProvider badWebResponses
220 * @param array $badWebResponse Description of a web request that returns some kind of failure
221 */
222 public function testGetDocument_NewFailure_CacheOK_UpdateOK($badWebResponse) {
223 $this->assertNotEmpty($badWebResponse);
224
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,
229 $this->expectOneHttpRequest($badWebResponse)
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,
249 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
250 );
251 $doc3 = $communityMessages->getDocument();
252 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
253 $this->assertTrue($doc3['expires'] > CRM_Utils_Time::getTimeRaw());
254 }
255
256 /**
257 * First download of new doc is OK.
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.
262 *
263 * @dataProvider badWebResponses
264 * @param array $badWebResponse Description of a web request that returns some kind of failure
265 */
266 public function testGetDocument_NewOK_UpdateFailure_CacheOK_UpdateOK($badWebResponse) {
267 $this->assertNotEmpty($badWebResponse);
268
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,
273 $this->expectOneHttpRequest(self::$webResponses['first-valid-response'])
274 );
275 $doc1 = $communityMessages->getDocument();
276 $this->assertEquals('<h1>First valid response</h1>', $doc1['messages'][0]['markup']);
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,
283 $this->expectOneHttpRequest($badWebResponse)
284 );
285 $doc2 = $communityMessages->getDocument();
286 $this->assertEquals('<h1>First valid response</h1>', $doc2['messages'][0]['markup']);
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();
296 $this->assertEquals('<h1>First valid response</h1>', $doc3['messages'][0]['markup']);
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,
303 $this->expectOneHttpRequest(self::$webResponses['second-valid-response'])
304 );
305 $doc4 = $communityMessages->getDocument();
306 $this->assertEquals('<h1>Second valid response</h1>', $doc4['messages'][0]['markup']);
307 $this->assertEquals(strtotime('2013-03-01 12:20:02'), $doc4['expires']);
308 }
309
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
323 $trials = 80;
324 $freq = array(); // array($message => $count)
325 for ($i = 0; $i < $trials; $i++) {
326 $message = $communityMessages->pick();
327 $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1;
328 }
329
330 // assert the probabilities
331 $this->assertApproxEquals(0.5, $freq['<h1>One</h1>'] / $trials, 0.3);
332 $this->assertApproxEquals(0.5, $freq['<h1>Two</h1>'] / $trials, 0.3);
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();
354 $freq[$message['markup']] = CRM_Utils_Array::value($message['markup'], $freq, 0) + 1;
355 }
356
357 $this->assertEquals($trials, $freq['<h1>Two</h1>']);
358 }
359
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
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 *
383 * @param $response
384 *
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 }