Commit | Line | Data |
---|---|---|
b4a332a9 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
7d61e75f | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
b4a332a9 | 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 | | |
b4a332a9 TO |
9 | +--------------------------------------------------------------------+ |
10 | */ | |
11 | ||
12 | /** | |
13 | * Test that content produced by CiviMail looks the way it's expected. | |
14 | * | |
15 | * @package CiviCRM_APIv3 | |
16 | * @subpackage API_Job | |
17 | * | |
ca5cec67 | 18 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
b4a332a9 TO |
19 | * @version $Id: Job.php 30879 2010-11-22 15:45:55Z shot $ |
20 | * | |
21 | */ | |
22 | ||
23 | /** | |
24 | * Class CRM_Mailing_MailingSystemTest | |
25 | * | |
26 | * MailingSystemTest checks that overall composition and delivery of | |
27 | * CiviMail blasts works. It extends CRM_Mailing_BaseMailingSystemTest | |
28 | * which provides the general test scenarios -- but this variation | |
29 | * checks that certain internal events/hooks fire. | |
30 | * | |
31 | * MailingSystemTest is the counterpart to FlexMailerSystemTest. | |
32 | * | |
33 | * @group headless | |
34 | * @group civimail | |
35 | * @see \Civi\FlexMailer\FlexMailerSystemTest | |
36 | */ | |
37 | class CRM_Mailing_MailingSystemTest extends CRM_Mailing_BaseMailingSystemTest { | |
38 | ||
39 | private $counts; | |
40 | ||
41 | public function setUp() { | |
42 | parent::setUp(); | |
9099cab3 | 43 | Civi::settings()->add(['experimentalFlexMailerEngine' => FALSE]); |
b4a332a9 TO |
44 | |
45 | $hooks = \CRM_Utils_Hook::singleton(); | |
46 | $hooks->setHook('civicrm_alterMailParams', | |
9099cab3 | 47 | [$this, 'hook_alterMailParams']); |
b4a332a9 TO |
48 | } |
49 | ||
50 | /** | |
51 | * @see CRM_Utils_Hook::alterMailParams | |
52 | */ | |
53 | public function hook_alterMailParams(&$params, $context = NULL) { | |
54 | $this->counts['hook_alterMailParams'] = 1; | |
55 | $this->assertEquals('civimail', $context); | |
56 | } | |
57 | ||
58 | public function tearDown() { | |
76d57ffc RLAR |
59 | global $dbLocale; |
60 | if ($dbLocale) { | |
61 | CRM_Core_I18n_Schema::makeSinglelingual('en_US'); | |
62 | } | |
b4a332a9 TO |
63 | parent::tearDown(); |
64 | $this->assertNotEmpty($this->counts['hook_alterMailParams']); | |
65 | } | |
66 | ||
67 | // ---- Boilerplate ---- | |
68 | ||
69 | // The remainder of this class contains dummy stubs which make it easier to | |
70 | // work with the tests in an IDE. | |
71 | ||
72 | /** | |
73 | * Generate a fully-formatted mailing (with body_html content). | |
74 | * | |
75 | * @dataProvider urlTrackingExamples | |
76 | */ | |
77 | public function testUrlTracking( | |
78 | $inputHtml, | |
79 | $htmlUrlRegex, | |
80 | $textUrlRegex, | |
81 | $params | |
82 | ) { | |
83 | parent::testUrlTracking($inputHtml, $htmlUrlRegex, $textUrlRegex, $params); | |
84 | } | |
85 | ||
86 | public function testBasicHeaders() { | |
87 | parent::testBasicHeaders(); | |
88 | } | |
89 | ||
90 | public function testText() { | |
91 | parent::testText(); | |
92 | } | |
93 | ||
94 | public function testHtmlWithOpenTracking() { | |
95 | parent::testHtmlWithOpenTracking(); | |
96 | } | |
97 | ||
98 | public function testHtmlWithOpenAndUrlTracking() { | |
99 | parent::testHtmlWithOpenAndUrlTracking(); | |
100 | } | |
101 | ||
28106149 PN |
102 | /** |
103 | * Test to check Activity being created on mailing Job. | |
104 | * | |
105 | */ | |
106 | public function testMailingActivityCreate() { | |
107 | $subject = uniqid('testMailingActivityCreate'); | |
108 | $this->runMailingSuccess([ | |
109 | 'subject' => $subject, | |
110 | 'body_html' => 'Test Mailing Activity Create', | |
111 | 'scheduled_id' => $this->individualCreate(), | |
112 | ]); | |
113 | ||
114 | $this->callAPISuccessGetCount('activity', [ | |
115 | 'activity_type_id' => 'Bulk Email', | |
116 | 'status_id' => 'Completed', | |
117 | 'subject' => $subject, | |
118 | ], 1); | |
119 | } | |
120 | ||
76d57ffc RLAR |
121 | /** |
122 | * Data provider for testGitLabIssue1108 | |
123 | * | |
124 | * First we run it without multiLingual mode, then with. | |
125 | * | |
126 | * This is because we test table names, which may have been translated in a | |
127 | * multiLingual context. | |
128 | * | |
129 | */ | |
130 | public function multiLingual() { | |
131 | return [[0], [1]]; | |
132 | } | |
133 | ||
134 | /** | |
135 | * - unsubscribe used dodgy SQL that only checked half of the polymorphic | |
136 | * relationship in mailing_group, meaning it could match 'mailing 123' | |
137 | * against _group_ 123. | |
138 | * | |
139 | * - also, an INNER JOIN on the group table hid the mailing-based | |
140 | * mailing_group records. | |
141 | * | |
142 | * - in turn this inner join meant the query returned nothing, which then | |
143 | * caused the code that is supposed to find the contact within those groups | |
144 | * to basically find all the groups that the contact in or were smart groups. | |
145 | * | |
146 | * - in certain situations (which I have not been able to replicate in this | |
147 | * test) it caused the unsubscribe to fail to find *any* groups to unsubscribe | |
148 | * people from, thereby breaking the unsubscribe. | |
149 | * | |
150 | * @dataProvider multiLingual | |
151 | * | |
152 | */ | |
153 | public function testGitLabIssue1108($isMultiLingual) { | |
154 | ||
155 | // We need to make sure the mailing IDs are higher than the groupIDs. | |
156 | // We do this by adding mailings until the mailing.id value is at least 10 | |
157 | // higher than the highest group.id | |
158 | // Note that creating a row in a transaction then rolling back the | |
159 | // transaction still increments the AUTO_INCREMENT counter for the table. | |
160 | // (If this behaviour ever changes we throw an exception.) | |
161 | if ($isMultiLingual) { | |
162 | $this->enableMultilingual(); | |
163 | } | |
164 | $max_group_id = CRM_Core_DAO::singleValueQuery("SELECT MAX(id) FROM civicrm_group"); | |
165 | $max_mailing_id = 0; | |
166 | while ($max_mailing_id < $max_group_id + 10) { | |
167 | CRM_Core_Transaction::create()->run(function($tx) use (&$max_mailing_id) { | |
168 | CRM_Core_DAO::executeQuery("INSERT INTO civicrm_mailing (name) VALUES ('dummy');"); | |
169 | $_ = (int) CRM_Core_DAO::singleValueQuery("SELECT MAX(id) FROM civicrm_mailing"); | |
170 | if ($_ === $max_mailing_id) { | |
171 | throw new RuntimeException("Expected that creating a new row would increment ID, but it did not. This could be a change in MySQL's implementation of rollback"); | |
172 | } | |
173 | $max_mailing_id = $_; | |
174 | $tx->rollback(); | |
175 | }); | |
176 | } | |
177 | ||
178 | // Because our parent class marks the _groupID as private, we can't use that :-( | |
179 | $group_1 = $this->groupCreate([ | |
180 | 'name' => 'Test Group 1108.1', | |
181 | 'title' => 'Test Group 1108.1', | |
182 | ]); | |
183 | $this->createContactsInGroup(2, $group_1); | |
184 | ||
185 | // Also _mut is private to the parent, so we have to make our own: | |
186 | $mut = new CiviMailUtils($this, TRUE); | |
187 | ||
188 | // Create initial mailing to the group. | |
189 | $mailingParams = [ | |
190 | 'name' => 'Issue 1108: mailing 1', | |
191 | 'subject' => 'Issue 1108: mailing 1', | |
192 | 'created_id' => 1, | |
193 | 'groups' => ['include' => [$group_1]], | |
194 | 'scheduled_date' => 'now', | |
195 | 'body_text' => 'Please just {action.unsubscribe}', | |
196 | ]; | |
197 | ||
198 | // The following code is exactly the same as runMailingSuccess() except that we store the ID of the mailing. | |
199 | $mailing_1 = $this->callAPISuccess('mailing', 'create', $mailingParams); | |
200 | $mut->assertRecipients(array()); | |
201 | $this->callAPISuccess('job', 'process_mailing', array('runInNonProductionEnvironment' => TRUE)); | |
202 | ||
203 | $allMessages = $mut->getAllMessages('ezc'); | |
204 | // There are exactly two contacts produced by setUp(). | |
205 | $this->assertEquals(2, count($allMessages)); | |
206 | ||
207 | // We need a new group | |
208 | $group_2 = $this->groupCreate([ | |
209 | 'name' => 'Test Group 1108.2', | |
210 | 'title' => 'Test Group 1108.2', | |
211 | ]); | |
212 | ||
213 | // Now create the 2nd mailing to the recipients of the first, | |
214 | // excluding our new albeit empty group. | |
215 | $mailingParams = [ | |
216 | 'name' => 'Issue 1108: mailing 2', | |
217 | 'subject' => 'Issue 1108: mailing 2', | |
218 | 'created_id' => 1, | |
219 | 'mailings' => ['include' => [$mailing_1['id']]], | |
220 | 'groups' => ['exclude' => [$group_2]], | |
221 | 'scheduled_date' => 'now', | |
222 | 'body_text' => 'Please just {action.unsubscribeUrl}', | |
223 | ]; | |
224 | $this->callAPISuccess('mailing', 'create', $mailingParams); | |
225 | $_ = $this->callAPISuccess('job', 'process_mailing', array('runInNonProductionEnvironment' => TRUE)); | |
226 | ||
227 | $allMessages = $mut->getAllMessages('ezc'); | |
228 | // We should have 2+2 messages sent by the mail system now. | |
229 | $this->assertEquals(4, count($allMessages)); | |
230 | ||
231 | // So far so good. | |
232 | // Now extract the unsubscribe details. | |
233 | $message = end($allMessages); | |
234 | $this->assertTrue($message->body instanceof ezcMailText); | |
235 | $this->assertEquals('plain', $message->body->subType); | |
236 | $this->assertEquals(1, preg_match( | |
237 | '@mailing/unsubscribe.*jid=(\d+)&qid=(\d+)&h=([0-9a-z]+)@', | |
238 | $message->body->text, | |
239 | $matches | |
240 | )); | |
241 | ||
242 | // Create a group that has nothing to do with this mailing. | |
243 | $group_3 = $this->groupCreate([ | |
244 | 'name' => 'Test Group 1108.3', | |
245 | 'title' => 'Test Group 1108.3', | |
246 | ]); | |
247 | // Add contacts from group 1 to group 3. | |
248 | $gcQuery = new CRM_Contact_BAO_GroupContact(); | |
249 | $gcQuery->group_id = $group_1; | |
250 | $gcQuery->status = 'Added'; | |
251 | $gcQuery->find(); | |
252 | while ($gcQuery->fetch()) { | |
253 | $this->callAPISuccess('group_contact', 'create', | |
254 | ['group_id' => $group_3, 'contact_id' => $gcQuery->contact_id, 'status' => 'Added']); | |
255 | } | |
256 | ||
257 | // Part of the issue is caused by the fact that (at time of writing) the | |
258 | // SQL joined the mailing_group table on just the entity_id, assuming it to | |
259 | // be a group, but actually it could be a mailing. | |
260 | // The difficulty in testing this is that because all our IDs are very low | |
261 | // and contiguous the SQL looking for a match for 'mailing 1' does match a | |
262 | // group ID of '1', which is created in this class's parent's setUp(). | |
263 | // Strictly speaking we don't know that it has ID 1, but as we can't access _groupID | |
264 | // we'll have to assume that. | |
265 | // | |
266 | // So by deleting that group the SQL then matches nothing which is what we | |
267 | // need for this case. | |
268 | $_ = new CRM_Contact_BAO_Group(); | |
269 | $_->id = 1; | |
270 | $_->delete(); | |
271 | ||
272 | $hooks = \CRM_Utils_Hook::singleton(); | |
273 | $found = []; | |
274 | $hooks->setHook('civicrm_unsubscribeGroups', | |
275 | function ($op, $mailingId, $contactId, &$groups, &$baseGroups) use (&$found) { | |
276 | $found['groups'] = $groups; | |
277 | $found['baseGroups'] = $baseGroups; | |
278 | }); | |
279 | ||
280 | // Now test unsubscribe groups. | |
281 | $groups = CRM_Mailing_Event_BAO_Unsubscribe::unsub_from_mailing( | |
282 | $matches[1], | |
283 | $matches[2], | |
284 | $matches[3], | |
285 | TRUE | |
286 | ); | |
287 | ||
288 | // We expect that our group_1 was found. | |
289 | $this->assertEquals(['groups' => [$group_1], 'baseGroups' => []], $found); | |
290 | ||
291 | // We *should* get an array with just our $group_1 since this is the only group | |
292 | // that we have included. | |
293 | // $group_2 was only used to exclude people. | |
294 | // $group_3 has nothing to do with this mailing and should not be there. | |
295 | $this->assertNotEmpty($groups, "We should have received an array."); | |
296 | $this->assertEquals([$group_1], array_keys($groups), | |
297 | "We should have received an array with our group 1 in it."); | |
298 | } | |
299 | ||
b4a332a9 | 300 | } |