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