targetDate`) * and should relate to `$this->contact['alice']` (or 'bob 'or 'carol'). * - Implement at least one schedule-helper which configures `$this->schedule` * to use the preferred action mapping. It may define various * filters, such as value-filters, status-filters, or recipient-filters. * - Implement `createTestCases()` which defines various * permutations of tests to run. Each test provides a list of emails * which should be fired (datetime/recipient/subject). * * For examples: * @see CRM_Contribute_ActionMapping_ByTypeTest * @see CRM_Activity_ActionMappingTest */ abstract class AbstractMappingTest extends \CiviUnitTestCase { /** * @var \CRM_Core_DAO_ActionSchedule */ public $schedule; /** * The date which should be stored on the matching record in the DB. * * @var string */ public $targetDate; /** * Example contact records. * * @var array */ public $contacts; /** * The schedule for invoking cron. * * @var array * - start: string * - end: string * - interval: int, seconds */ public $cronSchedule; /** * When comparing timestamps, treat them as the same if they * occur within a certain distance of each other. * * @var int seconds */ public $dateTolerance = 120; /** * @var \CiviMailUtils */ public $mut; /** * Generate a list of test cases, where each is a distinct combination of * data, schedule-rules, and schedule results. * * @return array * - targetDate: string; eg "2015-02-01 00:00:01" * - setupFuncs: string, space-separated list of setup functions * - messages: array; each item is a message that's expected to be sent * each message may include keys: * - time: approximate time (give or take a few seconds) * - subject: regex * - message: regex */ abstract public function createTestCases(); // ---------------------------------------- Setup Helpers ---------------------------------------- /** * Send first message on the designated date. */ public function startOnTime() { $this->schedule->start_action_condition = 'before'; $this->schedule->start_action_offset = '0'; $this->schedule->start_action_unit = 'day'; } /** * Send first message one week before designated date. */ public function startWeekBefore() { $this->schedule->start_action_condition = 'before'; $this->schedule->start_action_offset = '7'; $this->schedule->start_action_unit = 'day'; } /** * Send first message one week after designated date. */ public function startWeekAfter() { $this->schedule->start_action_condition = 'after'; $this->schedule->start_action_offset = '7'; $this->schedule->start_action_unit = 'day'; } /** * Send repeated messages until two weeks after designated date. */ public function repeatTwoWeeksAfter() { $this->schedule->is_repeat = 1; $this->schedule->repetition_frequency_interval = '7'; $this->schedule->repetition_frequency_unit = 'day'; $this->schedule->end_action = 'after'; $this->schedule->end_date = $this->schedule->start_action_date; $this->schedule->end_frequency_interval = '14'; $this->schedule->end_frequency_unit = 'day'; } /** * Compose a "Hello" email which includes the recipient's first name. */ public function useHelloFirstName() { $this->schedule->subject = 'Hello, {contact.first_name}. (via subject)'; $this->schedule->body_html = '

Hello, {contact.first_name}. (via body_html)

'; $this->schedule->body_text = 'Hello, {contact.first_name}. (via body_text)'; } /** * Limit possible recipients to Alice. */ public function limitToRecipientAlice() { $this->schedule->limit_to = 1; $this->schedule->recipient = NULL; $this->schedule->recipient_listing = NULL; $this->schedule->recipient_manual = $this->contacts['alice']['id']; } /** * Limit possible recipients to Bob. */ public function limitToRecipientBob() { $this->schedule->limit_to = 1; $this->schedule->recipient = NULL; $this->schedule->recipient_listing = NULL; $this->schedule->recipient_manual = $this->contacts['bob']['id']; } /** * Also include recipient Bob. */ public function alsoRecipientBob() { $this->schedule->limit_to = 0; $this->schedule->recipient = NULL; $this->schedule->recipient_listing = NULL; $this->schedule->recipient_manual = $this->contacts['bob']['id']; } // ---------------------------------------- Core test definitions ---------------------------------------- /** * Setup an empty schedule and some contacts. */ protected function setUp() { parent::setUp(); $this->useTransaction(); $this->mut = new \CiviMailUtils($this, TRUE); $this->cronSchedule = [ 'start' => '2015-01-20 00:00:00', 'end' => '2015-03-01 00:00:00', // seconds 'interval' => 24 * 60 * 60, ]; $this->schedule = new \CRM_Core_DAO_ActionSchedule(); $this->schedule->title = $this->getName(TRUE); $this->schedule->name = \CRM_Utils_String::munge($this->schedule->title); $this->schedule->is_active = 1; $this->schedule->group_id = NULL; $this->schedule->recipient = NULL; $this->schedule->recipient_listing = NULL; $this->schedule->recipient_manual = NULL; $this->schedule->absolute_date = NULL; $this->schedule->msg_template_id = NULL; $this->schedule->record_activity = NULL; $this->contacts['alice'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Alice', 'last_name' => 'Exemplar', 'email' => 'alice@example.org', ]); $this->contacts['bob'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Bob', 'last_name' => 'Exemplar', 'email' => 'bob@example.org', ]); $this->contacts['carol'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Carol', 'last_name' => 'Exemplar', 'email' => 'carol@example.org', ]); $this->contacts['dave'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Dave', 'last_name' => 'Exemplar', 'email' => 'dave@example.org', 'do_not_email' => 1, ]); $this->contacts['edith'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Edith', 'last_name' => 'Exemplar', 'email' => 'edith@example.org', 'is_deceased' => 1, ]); $this->contacts['francis'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Francis', 'last_name' => 'Exemplar', 'api.Email.create' => [ 'email' => 'frances@example.org', 'on_hold' => 1, ], ]); $this->contacts['gretchen'] = $this->callAPISuccess('Contact', 'create', [ 'contact_type' => 'Individual', 'first_name' => 'Gretchen', 'last_name' => 'Exemplar', 'email' => 'gretchen@example.org', 'api.Email.create' => [ 'email' => 'gratchen@example.org', 'on_hold' => 1, ], ]); } /** * Execute the default schedule, without any special recipient selections. * * @dataProvider createTestCases * * @param string $targetDate * @param string $setupFuncs * @param array $expectMessages * * @throws \Exception */ public function testDefault($targetDate, $setupFuncs, $expectMessages) { $this->targetDate = $targetDate; foreach (explode(' ', $setupFuncs) as $setupFunc) { $this->{$setupFunc}(); } $this->schedule->save(); $actualMessages = []; foreach ($this->cronTimes() as $time) { \CRM_Utils_Time::setTime($time); $this->callAPISuccess('job', 'send_reminder', []); foreach ($this->mut->getAllMessages('ezc') as $message) { /** @var \ezcMail $message */ $simpleMessage = [ 'time' => $time, 'to' => \CRM_Utils_Array::collect('email', $message->to), 'subject' => $message->subject, ]; sort($simpleMessage['to']); $actualMessages[] = $simpleMessage; $this->mut->clearMessages(); } } $errorText = "Incorrect messages: " . print_r([ 'actualMessages' => $actualMessages, 'expectMessages' => $expectMessages, ], 1); $this->assertEquals(count($expectMessages), count($actualMessages), $errorText); usort($expectMessages, [__CLASS__, 'compareSimpleMsgs']); usort($actualMessages, [__CLASS__, 'compareSimpleMsgs']); foreach ($expectMessages as $offset => $expectMessage) { $actualMessage = $actualMessages[$offset]; $this->assertApproxEquals(strtotime($expectMessage['time']), strtotime($actualMessage['time']), $this->dateTolerance, $errorText); if (isset($expectMessage['to'])) { sort($expectMessage['to']); $this->assertEquals($expectMessage['to'], $actualMessage['to'], $errorText); } if (isset($expectMessage['subject'])) { $this->assertRegExp($expectMessage['subject'], $actualMessage['subject'], $errorText); } } } protected function cronTimes() { $skew = 0; $times = []; $end = strtotime($this->cronSchedule['end']); for ($time = strtotime($this->cronSchedule['start']); $time < $end; $time += $this->cronSchedule['interval']) { $times[] = date('Y-m-d H:i:s', $time + $skew); //$skew++; } return $times; } protected function compareSimpleMsgs($a, $b) { if ($a['time'] != $b['time']) { return ($a['time'] < $b['time']) ? 1 : -1; } if ($a['to'] != $b['to']) { return ($a['to'] < $b['to']) ? 1 : -1; } if ($a['subject'] != $b['subject']) { return ($a['subject'] < $b['subject']) ? 1 : -1; } } }