deploy.test in Deploy - Content Staging 7.2
Same filename and directory in other branches
Deployment tests.
File
deploy.testView source
<?php
/**
* @file
* Deployment tests.
*/
/**
* Helper class.
*/
class DeployWebTestCase extends DrupalWebTestCase {
// Array to keep presaved variables.
private $servers = array();
protected $manage_add_to_plan = TRUE;
protected $origin_modules = array(
'entity',
'ctools',
'uuid',
'deploy',
'deploy_ui',
'deploy_example',
);
protected $endpoint_modules = array(
'entity',
'ctools',
'uuid',
'services',
'rest_server',
'uuid_services',
'uuid_services_example',
);
/**
* Set up all sites.
*
* For some tests we don't need the multisite environment, but still want
* to use common methods in this test case.
*/
protected function setUp($simple = FALSE) {
$this->profile = 'standard';
if ($simple) {
parent::setUp('entity', 'deploy');
return;
}
// Set up our origin site.
$this
->setUpSite('deploy_origin', $this->origin_modules);
// Switch back to original site to be able to set up a new site.
$this
->switchSite('deploy_origin', 'simpletest_original_default');
// Set up one endpoint site.
$this
->setUpSite('deploy_endpoint', $this->endpoint_modules);
// This is the user that will be used to authenticate the deployment between
// the site. We add it to $GLOBALS so we can access the user info on the
// origin site and configure the endpoint object with its username and
// password.
$GLOBALS['endpoint_user'] = $this
->drupalCreateUser(array(
'access content',
'create article content',
'edit any article content',
'administer users',
'administer taxonomy',
));
// Switch back to origin site where we want to start.
$this
->switchSite('deploy_endpoint', 'deploy_origin');
// Edit an endpoint object to point to our endpoint site.
$this
->editEndpoint('deploy_example_endpoint', 'deploy_endpoint');
}
/**
* Tear down all sites.
*
* @todo Make this transparent of how many sites we've set up.
*/
protected function tearDown() {
// Tear down current site.
parent::tearDown();
// We are making it easy for us (but a bit hacky) by using this method to
// clean out other environments that we've created.
simpletest_clean_database();
simpletest_clean_temporary_directories();
registry_rebuild();
cache_clear_all('simpletest', 'cache');
}
/**
* Set up a new site.
*/
protected function setUpSite($key, $modules) {
static $original = array();
// How we can call parent::foo() changed in PHP 5.3.
if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
call_user_func_array('parent::setUp', $modules);
}
else {
call_user_func_array(array(
$this,
'parent::setUp',
), $modules);
}
$this
->saveState($key);
variable_set('deploy_site_hash', md5(time()));
// Save original settings after first setUp(). We need to be able to restore
// them after subsequent calls to setUp().
if (empty($original)) {
$original = array(
$this->originalLanguage,
$this->originalLanguageDefault,
$this->originalFileDirectory,
$this->originalProfile,
$this->originalShutdownCallbacks,
);
}
// Restore the original settings.
list($this->originalLanguage, $this->originalLanguageDefault, $this->originalFileDirectory, $this->originalProfile, $this->originalShutdownCallbacks) = $original;
$this
->setUpSiteSpecifics($key);
}
/**
* Method for site specific setup.
*/
protected function setUpSiteSpecifics($key) {
// Empty but available for classes to extend.
}
/**
* Switch to a specific site.
*/
protected function switchSite($from, $to) {
// This is used to test the switch.
$old_site_hash = variable_get('deploy_site_hash', '');
// Switch database connection. This is where the magic happens.
Database::renameConnection('default', $from);
Database::renameConnection($to, 'default');
// Reset static caches, so sites doesn't share them.
drupal_static_reset();
// Since variables ($conf) lives in the global namespace, we need to
// reinitalize them to not make sites share variables.
cache_clear_all('*', 'cache_bootstrap');
$GLOBALS['conf'] = variable_initialize();
// No need to restore anything if we are switching to the original site.
if ($to != 'simpletest_original_default') {
$this
->restoreState($to);
// Test the switch.
$new_site_hash = variable_get('deploy_site_hash', '');
$this
->assertNotEqual($old_site_hash, $new_site_hash, t('Switch to site %key was successful.', array(
'%key' => $to,
)));
}
}
/**
* Save state.
*/
protected function saveState($key) {
if (!isset($this->sites[$key])) {
$this->sites[$key] = new stdClass();
}
$this->sites[$key]->cookieFile = $this->cookieFile;
$this->sites[$key]->databasePrefix = $this->databasePrefix;
$this->sites[$key]->curlHandle = $this->curlHandle;
$this->sites[$key]->cookieFile = $this->cookieFile;
}
/**
* Restore state.
*/
protected function restoreState($key) {
$this->cookieFile = $this->sites[$key]->cookieFile;
$this->databasePrefix = $this->sites[$key]->databasePrefix;
$this->curlHandle = $this->sites[$key]->curlHandle;
$this->cookieFile = $this->sites[$key]->cookieFile;
}
/**
* Overridden method adjusted to work with this testing framework.
*/
protected function cronRun() {
// The "regular" way of running cron in SimpleTest doesn't work for us,
// since we have a very complex setup with a several "virtual" SimpleTest
// sites. This is an easier way of running cron. It doesn't cover as many
// test cases, but it's good enough.
drupal_cron_run();
}
/**
* Edit a plan.
*/
protected function editPlan($plan_name, $params = array()) {
$plan = deploy_plan_load($plan_name);
foreach ($params as $key => $value) {
$plan->{$key} = $value;
}
ctools_export_crud_save('deploy_plans', $plan);
}
/**
* Edit an endpoint to make it point to a specific site in this test
* environment.
*
* This is needed in order for deployments to be able to reach sites in this
* test environment.
*/
protected function editEndpoint($endpoint_name, $site_key) {
$endpoint = deploy_endpoint_load($endpoint_name);
$endpoint->authenticator_config = array(
'username' => $GLOBALS['endpoint_user']->name,
'password' => $GLOBALS['endpoint_user']->pass_raw,
);
$endpoint->service_config['url'] = url('api', array(
'absolute' => TRUE,
));
$user_agent = drupal_generate_test_ua($this->sites[$site_key]->databasePrefix);
$endpoint->service_config['headers'] = array(
'User-Agent' => $user_agent,
);
ctools_export_crud_save('deploy_endpoints', $endpoint);
}
/**
* Deploy the plan.
*
* @param string $name
* Name of the deployment plan.
* @return type
*/
protected function deployPlan($name) {
if (empty($name)) {
return;
}
$plan = deploy_plan_load($name);
$plan
->deploy();
// Some processors depends on cron.
$this
->cronRun();
}
// @ignore comment_comment_see:comment
/**
* Code taken from TaxonomyWebTestCase::createTerm() since we can't extend
* that test case. Some simplifications are made though.
*
* @todo
* This will probably not work when the Testing profile is used. Then we
* need to create the vocabulary manually.
*
* @see TaxonomyWebTestCase::createTerm()
*/
protected function createTerm() {
$term = new stdClass();
$term->name = $this
->randomName();
$term->description = $this
->randomName();
// Use the first available text format.
$term->format = db_query_range('SELECT format FROM {filter_format}', 0, 1)
->fetchField();
// For our test cases it's enough to rely on the standard 'tags' vocabulary.
$term->vid = 1;
taxonomy_term_save($term);
return $term;
}
/**
* This method runs a deployment scenario where we have one production site
* (the endpoint) and a staging site (the origin).
*
* Both sites are "out of sync" content wise (as production/stage always are)
* but deployments of new and updated content are still possible.
*
* @todo
* Conditionally test references modules too, since they are very likely too
* be used on most sites.
*
* @todo
* Test with translations too.
*/
protected function runScenario($plan_name) {
// Switch to our production site.
$this
->switchSite('deploy_origin', 'deploy_endpoint');
// Intentionally force the sites out of sync by creating some content that
// only exists in production.
$user = $this
->drupalCreateUser();
$term = $this
->createTerm();
$this
->drupalCreateNode(array(
'type' => 'article',
'uid' => $user->uid,
'field_tags' => array(
LANGUAGE_NONE => array(
array(
'tid' => $term->tid,
),
),
),
));
// Switch to our staging site and push some new content.
$this
->switchSite('deploy_endpoint', 'deploy_origin');
$user_stage = $this
->drupalCreateUser();
$term_stage = $this
->createTerm();
$node_title_orig = $this
->randomString();
$node_stage = $this
->drupalCreateNode(array(
'type' => 'article',
'title' => $node_title_orig,
'uid' => $user_stage->uid,
'field_tags' => array(
LANGUAGE_NONE => array(
array(
'tid' => $term_stage->tid,
),
),
),
));
list($nid_stage, $vid_stage) = entity_extract_ids('node', $node_stage);
// If this base class should manage adding to the plan is configurable to
// allow for modules like deploy_auto_plan.module to use other ways and
// still be able to extend this class.
if ($this->manage_add_to_plan) {
// Now add the node to the plan.
deploy_manager_add_to_plan($plan_name, 'node', $node_stage);
}
// Test that the node was added to the plan with the right identifiers.
$count = db_query('SELECT COUNT(revision_id) FROM {deploy_manager_entities} WHERE plan_name = :plan_name AND entity_id = :nid AND revision_id = :vid', array(
':plan_name' => $plan_name,
':nid' => $nid_stage,
':vid' => $vid_stage,
))
->fetchField();
$this
->assertEqual($count, 1, 'The entity was added to the deployment plan');
// This will deploy the node only. But with dependencies (like the author
// and the term).
$this
->deployPlan($plan_name);
// Switch to our production site and make sure the content was pushed.
$this
->switchSite('deploy_origin', 'deploy_endpoint');
// Load the deployed entities to test. Since we don't know their primary IDs
// here on the production site we look them up using their UUIDs.
$users = entity_uuid_load('user', array(
$user_stage->uuid,
), array(), TRUE);
$terms = entity_uuid_load('taxonomy_term', array(
$term_stage->uuid,
), array(), TRUE);
$nodes = entity_uuid_load('node', array(
$node_stage->uuid,
), array(), TRUE);
$user_prod = reset($users);
$term_prod = reset($terms);
$node_prod = reset($nodes);
// Test to see if all entities are locally different, but universally the
// same. They should be, since we forced the sites out of sync earlier.
//
// Test the node author.
$test = $user_stage->uuid == $user_prod->uuid && $user_stage->uid != $user_prod->uid;
$this
->assertTrue($test, 'New node author was deployed successfully.');
// Test the term.
$test = $term_stage->uuid == $term_prod->uuid && $term_stage->tid != $term_prod->tid;
$this
->assertTrue($test, 'New term was deployed successfully.');
// Test the node itself.
$test = $node_stage->uuid == $node_prod->uuid && $node_stage->nid != $node_prod->nid;
$this
->assertTrue($test, 'New node was deployed successfully.');
// Test if the dependencies got attached to the node.
$this
->assertEqual($node_prod->uid, $user_stage->uuid, 'Node author was successfully attached to node.');
$this
->assertEqual($node_prod->field_tags[LANGUAGE_NONE][0]['tid'], $term_stage->uuid, 'Term was successfully attached to node.');
// Now switch back to staging site and make updates to all entities to see
// if updates is comming through, when a new deployment is done.
$this
->switchSite('deploy_endpoint', 'deploy_origin');
// Update the node.
$node_stage->title = $this
->randomString();
node_save($node_stage);
// TODO: Update more entities in the dependency chain of the node.
// Since we have a new revision ID we have to add the node again.
if ($this->manage_add_to_plan) {
deploy_manager_add_to_plan($plan_name, 'node', $node_stage);
}
// Now deploy the node again.
$this
->deployPlan($plan_name);
// Switch back to production to assert the changes.
$this
->switchSite('deploy_origin', 'deploy_endpoint');
$nodes = entity_uuid_load('node', array(
$node_stage->uuid,
), array(), TRUE);
$node_prod = reset($nodes);
$test = $node_prod->title == $node_stage->title && $node_prod->title != $node_title_orig;
$this
->assertTrue($test, 'Node was successfully updated after new deployment.');
}
}
/**
* Test a full deployment between two sites, based on the provided example
* feature.
*/
class DeploySimpleDeploymentTestCase extends DeployWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Simple deployment setup',
'description' => 'Test a full deployment between two sites, based on the provided example feature.',
'group' => 'Deployment',
);
}
// @ignore comment_comment_see:comment
/**
* Simple deployment scenario.
*
* @see DeployWebTestCase::runScenario()
*/
function testDeployment() {
$this
->runScenario('deploy_example_plan');
}
}
/**
* Test a full deployment between two sites with the Views aggregator and the
* Queue processor.
*/
class DeployQueuedDeploymentTestCase extends DeployWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Queued deployment setup',
'description' => 'Test a full deployment between two sites, based on the provided example feature with the Queue API processor.',
'group' => 'Deployment',
);
}
/**
* {@inheritdoc}
*/
function testDeployment() {
$this
->editPlan('deploy_example_plan', array(
'processor_plugin' => 'DeployProcessorQueue',
));
$this
->runScenario('deploy_example_plan');
}
}
Classes
Name | Description |
---|---|
DeployQueuedDeploymentTestCase | Test a full deployment between two sites with the Views aggregator and the Queue processor. |
DeploySimpleDeploymentTestCase | Test a full deployment between two sites, based on the provided example feature. |
DeployWebTestCase | Helper class. |