simplenews_scheduler.test in Simplenews Scheduler 6.2
Same filename and directory in other branches
Tests for Simplenews Scheduler.
File
tests/simplenews_scheduler.testView source
<?php
/**
* @file
* Tests for Simplenews Scheduler.
*/
/**
* Class with common setup.
*
* Declares the module dependencies for the test.
*
* We need to use DrupalWebTestCase as our base class even for functional
* testing, as the functions that we test rely on variable_get() which requires
* a bootstrapped database.
*/
class SimpleNewsSchedulerWebTestCase extends DrupalWebTestCase {
function setUp() {
// setUp() expects a list rather than an array, and we can't use
// call_user_func_array() with parent:: without mucking about with checking
// whether we're on PHP 5.2 or 5.3.
// Hence for now the module list is fixed for all our tests.
// @todo: figure out why we need content module to not crash tests!?!
parent::setUp('content', 'simplenews', 'token', 'date_api', 'date', 'simplenews_scheduler');
// Use a timezone for testing that is visibly different from UTC, and which
// has daylight saving changes.
$this->site_timezone_name = 'Europe/Kiev';
$this->site_timezone_object = timezone_open($this->site_timezone_name);
// Set the site timezone.
variable_set('date_default_timezone_name', $this->site_timezone_name);
// Note that there's no point in setting 'date_default_timezone' here
// because it's stored as a fixed offset in seconds from UTC, and so doesn't
// take daylight saving time changes in to account!
// Workaround for D6 not setting the PHP local timezone.
date_default_timezone_set($this->site_timezone_name);
}
/**
* Workaround helper for format_date() to fix timezone issues.
*
* This takes care of calculating the right offset to pass to the $timezone
* parameter.
*
* @see http://drupal.org/node/1528598.
*
* @param $date
* A PHP DateTime object.
* @param ...
* Other params same as format_date(). $timezone is ignored.
*
* @return
* As format_date().
*/
function format_date_helper($date, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
// Build the right offset to give to format_date(), because it's stupid.
$format_date_offset = $this->site_timezone_object
->getOffset($date);
return format_date($date
->getTimestamp(), $type, $format, $format_date_offset);
}
}
/**
* Test scheduled edition creation.
*/
class SimpleNewsSchedulerNodeCreationTest extends SimpleNewsSchedulerWebTestCase {
protected $privileged_user;
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Newsletter generation test',
'description' => 'Testing generation of newsletters',
'group' => 'Simplenews Scheduler',
);
}
/**
* Declares the module dependencies for the test.
*/
function setUp() {
parent::setUp();
$this->privileged_user = $this
->drupalCreateUser(array(
'access content',
'administer nodes',
'create simplenews content',
'edit own simplenews content',
'send newsletter',
'send scheduled newsletters',
'overview scheduled newsletters',
));
$this
->drupalLogin($this->privileged_user);
}
/**
* Basic simplenews newsletter generation test
* create a simplenews node,
*/
function testNewsletterGeneration() {
$edit = array();
$title = "newsletter " . $this
->randomName(8);
// Create a template newsletter node.
$edit = array();
$edit['title'] = $title;
$edit['body'] = $this
->randomName(16);
// Tests run on a clean enviroment; we can assume the newsletter taxonomy
// term ID is 1.
$edit['taxonomy[1]'] = 1;
$this
->drupalPost('node/add/simplenews', $edit, t('Save'));
$this
->assertText($title);
preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches);
$node = node_load($matches[1]);
// The 'Editions' node tab should not yet be accessible.
$this
->drupalGet("node/{$node->nid}/editions");
$this
->assertResponse(403, 'Access is denied on the editions tab for a non-scheduled node.');
// Now create the simplenews schedule configuration.
$this
->drupalGet("node/{$node->nid}/simplenews");
$this
->assertText(t("Send newsletter according to schedule"));
$edit = array();
$edit["simplenews[send]"] = '3';
$edit["simplenews[scheduler][send_interval]"] = "hour";
// Specify a start time 30 minutes in the past to be able to have a known
// edition creation time that can be checked.
$date = new DateTime('now', $this->site_timezone_object);
$date
->modify('-30 minutes');
$edit["simplenews[scheduler][start_date][year]"] = $date
->format('Y');
$edit["simplenews[scheduler][start_date][month]"] = $date
->format('n');
$edit["simplenews[scheduler][start_date][day]"] = $date
->format('j');
$edit["simplenews[scheduler][start_date][hour]"] = $date
->format('G');
$edit["simplenews[scheduler][start_date][minute]"] = $date
->format('i');
$this
->drupalPost("node/{$node->nid}/simplenews", $edit, t('Submit'));
// Make sure it knows no editions created yet.
$this
->drupalGet("node/{$node->nid}/editions");
$this
->assertText(t("No scheduled newsletters have been sent."));
// Force a clear of node_load()'s static cache. This ensures that the
// call to _simplenews_scheduler_new_edition() during the simulated cron
// gets scheduler data loaded into the node when it calls node_load().
node_load(NULL, NULL, TRUE);
// Call our hook to generate it.
simplenews_scheduler_cron();
// See if it was created.
$this
->drupalGet("node/{$node->nid}/editions");
$this
->assertText($title);
$this
->assertNoText(t("No scheduled newsletter editions have been sent."));
// Go to node and verify creation time. Due to the breadcrumb, this is the
// second link with the label on the page.
$this
->clickLink($title, 1);
$this
->assertText(t('@date', array(
'@date' => $this
->format_date_helper($date),
)));
$this
->drupalGet("node/2/editions");
$this
->assertText(t('This node is part of a scheduled newsletter configuration. View the original newsletter here.'));
}
}
/**
* Unit testing for monthly newsletter next run times.
*/
class SimpleNewsSchedulerNextRunTimeTest extends SimpleNewsSchedulerWebTestCase {
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Next run time: monthly',
'description' => 'Testing edition times for newsletters due every month.',
'group' => 'Simplenews Scheduler',
);
}
/**
* Test a frequency of 1 month.
*/
function testNextRunTimeOneMonth() {
// The start date of the edition.
$this->edition_day = '05';
$start_date = new DateTime("2012-01-{$this->edition_day} 12:00:00");
// Fake newsletter parent data: sets the interval, start date, and frequency.
$newsletter_parent_data = (object) array(
'nid' => 1,
'last_run' => 0,
'activated' => '1',
'send_interval' => 'month',
'interval_frequency' => '1',
'start_date' => $start_date
->getTimestamp(),
'next_run' => $start_date
->getTimestamp(),
// Needs to be set manually when creating new records programmatically.
'stop_type' => '0',
'stop_date' => '0',
'stop_edition' => '0',
'php_eval' => '',
'title' => '[node:title] for [current-date:long]',
);
// Number of days to run for.
$days = 370;
// Index of the days we've done so far.
$added_days = 0;
// Iterate over days.
$last_run_time = $start_date
->getTimestamp();
while ($added_days <= $days) {
// Create today's date at noon and get the timestamp.
$date = clone $start_date;
$date
->modify("+ {$added_days} days");
$timestamp_noon = $date
->getTimestamp();
// Get the next run time from the API function we're testing.
$next_run_time = simplenews_scheduler_calculate_next_run_time($newsletter_parent_data, $timestamp_noon);
//debug($edition_time);
if ($next_run_time != $last_run_time) {
$offset = _simplenews_scheduler_make_time_offset($newsletter_parent_data->send_interval, $newsletter_parent_data->interval_frequency);
$next_run_date = date_add(date_create(date('Y-m-d H:i:s', $last_run_time)), date_interval_create_from_date_string($offset));
$this
->assertEqual($next_run_date
->getTimestamp(), $next_run_time, t('New next run timestamp has advanced by the expected offset of !offset.', array(
'!offset' => $offset,
)));
$last_run_time = $next_run_time;
}
$this
->assertTrue($timestamp_noon < $next_run_time, t('Next run time of !next-run is in the future relative to current time of !now.', array(
'!next-run' => date("Y-n-d H:i:s", $next_run_time),
'!now' => date("Y-n-d H:i:s", $timestamp_noon),
)));
$interval = $newsletter_parent_data->interval_frequency * 31 * 24 * 60 * 60;
//$this->assertTrue($next_run_time - $timestamp_noon <= $interval, t('Next run timestamp is less than or exactly one month in the future.'));
// Create a date object from the timestamp. The '@' makes the constructor
// consider the string as a timestamp.
$next_run_date = new DateTime(date('Y-m-d H:i:s', $last_run_time));
$d = date_format($next_run_date, 'd');
$this
->assertEqual($next_run_date
->format('d'), $this->edition_day, t('Next run timestamp is on same day of the month as the start date.'));
$this
->assertEqual($next_run_date
->format('H:i:s'), '12:00:00', t('Next run timestamp is at the same time.'));
$added_days++;
}
// while days
}
}
/**
* Unit testing for monthly newsletter edition times.
*/
class SimpleNewsSchedulerEditionTimeTest extends SimpleNewsSchedulerWebTestCase {
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Edition time: monthly',
'description' => 'Testing edition times for newsletters due every month.',
'group' => 'Simplenews Scheduler',
);
}
/**
* Test a frequency of 1 month.
*/
function testEditionTimeOneMonth() {
// The start date of the edition.
$this->edition_day = '01';
$start_date = new DateTime("2012-01-{$this->edition_day} 12:00:00");
// Fake newsletter parent data: sets the interval, start date, and frequency.
$newsletter_parent_data = (object) array(
'nid' => 1,
'last_run' => 0,
'activated' => '1',
'send_interval' => 'month',
'interval_frequency' => '1',
'start_date' => $start_date
->getTimestamp(),
'stop_type' => '0',
'stop_date' => '0',
'stop_edition' => '0',
'php_eval' => '',
'title' => '[node:title] for [current-date:long]',
);
// Number of days to run for. Go just over one year.
$days = 370;
// Index of the days we've done so far.
$added_days = 0;
// Iterate over days.
while ($added_days <= $days) {
// Create today's date at noon and get the timestamp.
$date = clone $start_date;
$date
->modify("+ {$added_days} days");
$timestamp_noon = $date
->getTimestamp();
$edition_time = simplenews_scheduler_calculate_edition_time($newsletter_parent_data, $timestamp_noon);
//debug($edition_time);
// Expected edition time is always the {$this->edition_day}th of the month
// at noon.
$edition_time_formatted = date("Y-m-d H:i:s", $edition_time);
$this_month = $date
->format('Y-m');
$expected_time_formatted = "{$this_month}-{$this->edition_day} 12:00:00";
$this
->assertEqual($edition_time_formatted, $expected_time_formatted, t("Edition time of !edition-time matches expected time of !edition-time-expected at time !now.", array(
'!edition-time' => $edition_time_formatted,
'!edition-time-expected' => $expected_time_formatted,
'!now' => $date
->format("Y-m-d H:i:s"),
)));
$added_days++;
}
// while days
}
}
/**
* Unit testing for simplenews_scheduler_get_newsletters_due().
*/
class SimpleNewsSchedulerEditionDueTest extends SimpleNewsSchedulerWebTestCase {
protected $privileged_user;
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Edition due test',
'description' => 'Functional tests for simplenews_scheduler_get_newsletters_due().',
'group' => 'Simplenews Scheduler',
);
}
/**
* Declares the module dependencies and create data for the test.
*/
function setUp() {
parent::setUp();
$this->privileged_user = $this
->drupalCreateUser(array(
'access content',
'administer nodes',
'create simplenews content',
'edit own simplenews content',
'send newsletter',
'send scheduled newsletters',
'overview scheduled newsletters',
));
$this
->drupalLogin($this->privileged_user);
// The start date of the edition. This is on 5 January so that we get some
// days in either month, and at at noon to keep things simple.
$this->edition_day = '05';
$start_date = new DateTime("2012-01-{$this->edition_day} 12:00:00");
// Create a parent newsletter node.
$node = (object) NULL;
$node->type = 'simplenews';
$node->title = 'Parent';
$node->uid = 1;
$node->status = 1;
$node->language = 'und';
// Safe to assume there is only one taxonomy term and it's the newsletter.
$node->field_simplenews_term['und'][0]['tid'] = 1;
// Workaround for http://drupal.org/node/1480258
$node->nid = NULL;
node_save($node);
// Grumble grumble there's no node saving API in our module!
// @see http://drupal.org/node/1480328 to clean this up.
$node->simplenews_scheduler = (object) array(
'nid' => $node->nid,
'last_run' => 0,
'activated' => '1',
'send_interval' => 'month',
'interval_frequency' => '1',
'start_date' => $start_date
->getTimestamp(),
'next_run' => $start_date
->getTimestamp(),
// Needs to be set manually when creating new records programmatically.
'stop_type' => '0',
'stop_date' => '0',
'stop_edition' => '0',
'php_eval' => '',
'title' => '[node:title] for [current-date:long]',
);
$record = (array) $node->simplenews_scheduler;
drupal_write_record('simplenews_scheduler', $record);
// Store the node ID for the test to use.
$this->parent_nid = $node->nid;
}
/**
* Test simplenews_scheduler_get_newsletters_due().
*/
function testEditionsDue() {
// Get the node id of the parent newsletter node.
$parent_nid = $this->parent_nid;
// But just check it exists for sanity.
$this
->drupalGet("node/{$parent_nid}");
// Simulate cron running daily at half past 12 so that an edition due at
// 12 noon should be picked up.
$start_date = new DateTime("2012-01-01 12:00:00");
$time_offsets = array(
'before' => "-1 hour",
'after' => "+1 hour",
);
// Number of days to run cron for.
$days = 150;
// Index of the days we've done so far.
$added_days = 0;
// Iterate over days.
while ($added_days <= $days) {
// Create today's date at noon and get the timestamp.
$date = clone $start_date;
$date
->modify("+ {$added_days} days");
$timestamp_noon = $date
->getTimestamp();
// We simulate running cron one hour before and one hour after noon.
foreach ($time_offsets as $offset_key => $offset) {
// Create a timestamp based on noon + the offset.
// This gives us either 11:00 or 13:00 on the current day.
$timestamp = strtotime($offset, $timestamp_noon);
// debug("base: $timestamp_noon, off: $offset, result: $timestamp");
// Get the list of newsletters due.
$due = simplenews_scheduler_get_newsletters_due($timestamp);
// An edition is due if it's 13:00 on the edition day.
$formatted = date(DATE_RFC850, $timestamp);
if ($offset_key == 'after' && date('d', $timestamp) == $this->edition_day) {
$this
->assertTrue(isset($due[$parent_nid]), "Edition due at day {$added_days}, {$formatted}, {$timestamp}");
}
else {
$this
->assertFalse(isset($due[$parent_nid]), "Edition not due at day {$added_days}, {$formatted}, {$timestamp}");
}
// Get some debug output to figure out what is going on in
// simplenews_scheduler_get_newsletters_due().
$intervals['hour'] = 3600;
$intervals['day'] = 86400;
$intervals['week'] = $intervals['day'] * 7;
$intervals['month'] = $intervals['day'] * date_days_in_month(date('Y', $timestamp), date('m', $timestamp));
if (isset($due[$parent_nid])) {
// Output what we got back from the function.
// debug($due);
$newsletter_parent_data = $due[$parent_nid];
$edition_time = simplenews_scheduler_calculate_edition_time($newsletter_parent_data, $timestamp);
$eid = _simplenews_scheduler_new_edition($newsletter_parent_data->nid, $timestamp);
// Output the last_run as a sanity check.
$result = db_query("SELECT last_run FROM {simplenews_scheduler} WHERE nid = %d", array(
$parent_nid,
));
$last_run = db_result($result);
$formatted = date(DATE_RFC850, $last_run);
// debug("Last run: $formatted, $last_run");
// Output the calculated edition time.
$formatted = date(DATE_RFC850, $edition_time);
// debug("Edition time: $formatted, $edition_time");
// Check the edition time is 12:00.
$this
->assertEqual(date('H:i', $edition_time), '12:00', t('Edition time is at 12:00.'));
// Fake sending it: update the 'last_run' for subsequent iterations.
db_query("UPDATE {simplenews_scheduler} SET last_run = %d WHERE nid = %d", $timestamp, $parent_nid);
// Update the edition record.
simplenews_scheduler_scheduler_update($newsletter_parent_data, $timestamp);
// Check the node exists.
$this
->drupalGet("node/{$eid}");
}
// handling the request for a new edition
}
// foreach offset timestamp
// Increment our counter.
$added_days++;
}
// foreach day
}
}
/**
* Test edition time around DST changes.
*
* Test that simplenews_scheduler_calculate_edition_time() returns an edition
* timestamp whose time in the local timezone remains the same after the
* timezone changes over to Daylight Saving Time.
*/
class SimpleNewsSchedulerDaylightSavingSwitchTest extends SimpleNewsSchedulerWebTestCase {
protected $privileged_user;
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Daylight Saving Time',
'description' => 'Functional tests for DST changes.',
'group' => 'Simplenews Scheduler',
);
}
/**
* Test edition time after DST changes for a monthly newsletter.
*
* @todo: generalize this for other intervals.
*/
function testDSTMonthly() {
$timezone_object = date_default_timezone();
// Create a last run time before DST begins, and a now time after.
// Use date_create() rather than strtotime so that we create a date at the
// given time *in the current timezone* rather than UTC.
$last_run_date = new DateTime("01-Mar-12 12:00:00", $timezone_object);
$now_date = new DateTime("05-Apr-12 12:00:00", $timezone_object);
//debug('last run date TZ: ' . $last_run_date->getTimezone()->getName());
//debug('now date TZ: ' . $now_date->getTimezone()->getName());
// Fake up newsletter data.
$newsletter_parent_data = (object) array(
'last_run' => $last_run_date
->getTimestamp(),
'send_interval' => 'month',
'interval_frequency' => 1,
);
// Get the edition time.
$edition_time = simplenews_scheduler_calculate_edition_time($newsletter_parent_data, $now_date
->getTimestamp());
$edition_date = date_create('@' . $edition_time);
// Because we create from a timestamp we have to set the timezone separately.
$edition_date
->setTimezone($timezone_object);
//debug($edition_date->format(DATE_ATOM));
// Format the edition time.
$edition_time_formatted = $this
->format_date_helper($edition_date, 'custom', DATE_ATOM);
$edition_hour_formatted = $this
->format_date_helper($edition_date, 'custom', 'H:i');
$this
->assertEqual($edition_hour_formatted, '12:00', t('Edition time is at 12:00 in the local timezone; full edition time is %time.', array(
'%time' => $edition_time_formatted,
)));
}
}
Classes
Name | Description |
---|---|
SimpleNewsSchedulerDaylightSavingSwitchTest | Test edition time around DST changes. |
SimpleNewsSchedulerEditionDueTest | Unit testing for simplenews_scheduler_get_newsletters_due(). |
SimpleNewsSchedulerEditionTimeTest | Unit testing for monthly newsletter edition times. |
SimpleNewsSchedulerNextRunTimeTest | Unit testing for monthly newsletter next run times. |
SimpleNewsSchedulerNodeCreationTest | Test scheduled edition creation. |
SimpleNewsSchedulerWebTestCase | Class with common setup. |