<?php

/**
 * @file
 * Functionality tests for UUID module.
 */

/**
 * Helper test class with some added functions for testing.
 */
class UUIDTestHelper extends DrupalWebTestCase {
  function setUp() {
    $args = func_get_args();

    // Call parent::setUp() allowing test cases to pass further modules.
    $modules = array('uuid');
    if (isset($args[0]) && is_array($args[0])) {
      $modules = array_merge($modules, $args[0]);
    }

    $parent_callback = 'parent::setUp';
    if (version_compare(PHP_VERSION, '5.3.0', '<')) {
      $parent_callback = array($this, 'parent::setUp');
    }
    call_user_func_array($parent_callback, $modules);

  }

 /**
  * Custom UUID assertions and helpers.
  */

 /**
  * Log in as administrator and change the settings using the admin settings
  * form. After finishing the verification, return to previously logged in user.
  *
  * @param array $options
  *  - associative array of options to be set in the settings form. Available
  *    options will match the settings form field names.
  */
  public function uuidSettingsSet($options) {
    $loggedin = $this->loggedInUser;

    // Login as an administration user with permissions to configure the module.
    $admin = $this->drupalCreateUser(array('administer content types'));
    $this->drupalLogin($admin);

    // Submit the settings form with new settings.
    $this->drupalPost('admin/settings/uuid', $options, t('Save configuration'));
    foreach ($options as $name => $value) {
      $this->assertFieldByName(
        $name,
        $value,
        t("Field %name successfully verified.", array('%name' => $name)),
        t("Settings form")
      );
    }

    // Logout and set back previously logged in user.
    if ($loggedin) {
      $this->drupalLogin($loggedin);
    }
    else {
      $this->drupalLogout();
    }
  }

 /**
  * Verify the argument is a valid uuid and performs validation.
  *
  * @param string $uuid
  *   string to be validated as uuid.
  * @param string $message
  *   Message to show in this assertion.
  * @param string $group
  *   Name of the group for this assertion.
  */
  function assertIsUUID($uuid, $message = NULL, $group = NULL) {
    $this->assertNotNull($uuid, t("Value is not null"), $group);
    $this->assertTrue(uuid_is_valid($uuid), $message, $group);
  }

  /**
   * Sets a fail message in the test for further reviewing.
   * @param text $message
   *   text message to show.
   */
  function markIncomplete($message) {
    $this->pass($message, 'Debug');
  }

}

/**
 * Unit tests for UUID resolver API functions.
 *
 * @todo I don't even have clear what API functions to test, as heyrocker is
 * about to submit a patch with updated functions, so I'm just leaving this
 * testcase as a placeholder for now.
 */
class UUIDUnitTestCase extends UUIDTestHelper {
  public static function getInfo() {
    return array(
      'name' => 'Unitary testcase',
      'description' => 'Unitary tests for uuid api functions.',
      'group' => 'UUID',
    );
  }

  /**
   * Mark testcase as incomplete.
   */
  function testMarkIncomplete() {
    $this->markIncomplete(t("This test is incomplete."));
  }


}

/**
 * Test basic uuid resolver functionality.
 *
 * @todo include taxonomy and comment uuid tests.
 */
class UUIDFunctionalityTestCase extends UUIDTestHelper {
  public static function getInfo() {
    // Include dependencies to be handled by the testbot.
    return array(
      'name' => 'Functionality testcase',
      'description' => 'Test basic UUID functionality.',
      'group' => 'UUID',
    );
  }

  /**
   * Verify uuid behavior with default settings. I'm assuming default settings
   * should be restrictive, but actually, the radio type settings are not being
   * defaulted to any value.
   */

  /**
   * Verify uuid behavior for nodes.
   */
  function testDefaultNodeUUID() {
    // Create a user.
    $account = $this->drupalCreateUser(array('create page content'));

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $object = $this->drupalCreateNode($options);

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("Node has no uuid field attached with default settings."),
      t("Node UUID")
    );
    $this->assertTrue(
      empty($object->revision_uuid),
      t("Node has no revision_uuid field attached with default settings."),
      t("Node revision UUID")
    );

    // Create a new revision for this node.
    $object->revision = 1;
    node_save($object);

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("Node has no uuid field attached with default settings."),
      t("Node UUID")
    );
    $this->assertTrue(
      empty($object->revision_uuid),
      t("Node has no revision_uuid field attached with default settings."),
      t("Node revision UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($object, 1));
  }

  /**
   * Verify uuid behavior for users.
   */
  function testDefaultUserUUID() {
    // Create a user.
    $object = $this->drupalCreateUser();

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("User has no uuid field attached with default settings."),
      t("User UUID")
    );
  }

  /**
   * Verify uuid behavior for taxonomy.
   */
  function testDefaultTaxonomyUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior for comment.
   */
  function testDefaultCommentUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior with disabled settings. All settings are forced to
   * disabled, so new 'entities' should not get any uuid assigned.
   */

  /**
   * Verify uuid behavior for nodes.
   */
  function testDisabledNodeUUID() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]'  => FALSE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $account = $this->drupalCreateUser(array('create page content'));

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $object = $this->drupalCreateNode($options);

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("Node has no uuid field attached with disabled settings."),
      t("Node UUID")
    );
    $this->assertTrue(
      empty($object->revision_uuid),
      t("Node has no revision_uuid field attached with disabled settings."),
      t("Node revision UUID")
    );

    // Create a new revision for this node.
    $object->revision = 1;
    node_save($object);

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("Node has no uuid field attached with default settings."),
      t("Node UUID")
    );
    $this->assertTrue(
      empty($object->revision_uuid),
      t("Node has no revision_uuid field attached with default settings."),
      t("Node revision UUID")
    );

    // Test Programmatically setting UUIDs.
    $new_uuid = uuid_uuid();
    $revision_uuid = uuid_uuid();
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
      'uuid' => $new_uuid,
      'revision_uuid' => $revision_uuid,
    );
    $object = $this->drupalCreateNode($options);
    $loaded_node = node_load($object->nid, NULL, TRUE);

    $this->assertEqual(
      $object->uuid,
      $new_uuid,
      t("Node uuid can be set programmatically with disabled settings."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->uuid,
      $loaded_node->uuid,
      t("Node uuid is being returned correctly."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $revision_uuid,
      t("Node revision uuid can be set programmatically with disabled settings."),
      t("Node Revision UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $loaded_node->revision_uuid,
      t("Node revision uuid is being returned correctly."),
      t("Node Revision UUID")
    );
  }

  /**
   * Verify uuid behavior for users.
   */
  function testDisabledUserUUID() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_users' => FALSE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $object = $this->drupalCreateUser();

    // Verify fields are not attached to the node.
    $this->assertTrue(
      empty($object->uuid),
      t("User has no uuid field attached with disabled settings."),
      t("User UUID")
    );
  }

  /**
   * Verify uuid behavior for taxonomy.
   */
  function testDisabledTaxonomyUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior for comment.
   */
  function testDisabledCommentUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior with enabled settings. All settings are forced to
   * enabled, so new 'entities' should get a uuid assigned.
   *
   * Note that there have been issues with the uuid values the node object
   * after saving not necessarily reflecting what's in the database, so check
   * with a node_load().
   */

  /**
   * Verify uuid behavior for nodes.
   */
  function testEnabledNodeUUID() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]'  => TRUE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $account = $this->drupalCreateUser(array('create page content'));

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $object = $this->drupalCreateNode($options);

    // Verify fields are attached to the node.
    $this->assertIsUUID(
      $object->uuid,
      t("Node has uuid field attached with enabled settings."),
      t("Node UUID")
    );
    $this->assertIsUUID(
      $object->revision_uuid,
      t("Node has revision uuid field attached with enabled settings."),
      t("Node revision UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($object, 1));

    // Test saving without a new revision
    $uuid = $object->uuid;
    $revision_uuid = $object->revision_uuid;
    $object->revision = FALSE;
    node_save($object);
    $loaded_object = node_load($object->nid, NULL, TRUE);

    $this->assertEqual(
      $object->uuid,
      $uuid,
      t("Node uuid is preserved after update with revisioning off."),
      t("Node UUID")
    );
    $this->assertEqual(
      $loaded_object->uuid,
      $uuid,
      t("Node uuid loads correctly after update with revisioning off."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $revision_uuid,
      t("Node revision uuid is preserved after update with revisioning off."),
      t("Node Revision UUID")
    );
    $this->assertEqual(
      $loaded_object->revision_uuid,
      $revision_uuid,
      t("Node revision uuid loads correctly after update with revisioning off."),
      t("Node Revision UUID")
    );

    // make a copy of this node.
    $node = clone($object);

    // Create a new revision for this node.
    $object->revision = 1;
    node_save($object);
    $loaded_node = node_load($object->nid, NULL, TRUE);

    // Verify fields are attached to the node.
    $this->assertIsUUID(
      $object->uuid,
      t("Node has uuid field attached with enabled settings."),
      t("Node UUID")
    );
    $this->assertIsUUID(
      $object->revision_uuid,
      t("Node has revision uuid field attached with enabled settings."),
      t("Node revision UUID")
    );

    // A new revision uuid must be created.
    $this->assertEqual(
      $object->uuid,
      $node->uuid,
      t("Node uuid is being updated successfully."),
      t("Node UUID")
    );
    $this->assertNotEqual(
      $object->revision_uuid,
      $node->revision_uuid,
      t("Node revision uuid is being updated successfully."),
      t("Node revision UUID")
    );

    // Verify altered object reflects what's really in the database
    $this->assertEqual(
      $object->uuid,
      $loaded_node->uuid,
      t("Node uuid is being returned correctly."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $loaded_node->revision_uuid,
      t("Node revision uuid is being returned correctly."),
      t("Node revision UUID")
    );

    // Replace the UUID
    $new_uuid = uuid_uuid();
    $revision_uuid = uuid_uuid();
    $object->uuid = $new_uuid;
    $object->revision_uuid = $revision_uuid;
    node_save($object);
    $loaded_node = node_load($object->nid, NULL, TRUE);
    $this->assertEqual(
      $object->uuid,
      $new_uuid,
      t("Node uuid is being changed correctly."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->uuid,
      $loaded_node->uuid,
      t("Node uuid is being returned correctly."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $revision_uuid,
      t("Node revision uuid is being changed correctly."),
      t("Node Revision UUID")
    );
    $this->assertEqual(
      $object->revision_uuid,
      $loaded_node->revision_uuid,
      t("Node revision uuid is being returned correctly."),
      t("Node Revision UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($node, 1));
  }

  /**
   * Verify uuid behavior when edititng via node form.
   * http://drupal.org/node/1065364
   */
  function testEnabledNodeUUIDFormEdit() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]'  => TRUE,
    );
    $this->uuidSettingsSet($settings);

    // Create and log in our privileged user.
    $account = $this->drupalCreateUser(array('create page content', 'edit own page content'));
    $this->drupalLogin($account);

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $node = $this->drupalCreateNode($options);

    // Verify fields are attached to the node.
    $this->assertIsUUID(
      $node->uuid,
      t("Node has uuid field attached with enabled settings."),
      t("Node UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($node, 1));

    // Edit the node via edit form
    $edit = array();
    $edit['title'] = $this->randomName(8);
    $this->drupalPost("node/{$node->nid}/edit", $edit, t('Save'));
    $this->assertText(t('@title has been updated.', array('@title' => $edit['title'])));

    // Reload the node
    $updated_node = node_load($node->nid, NULL, TRUE);
    $this->assertEqual($node->uuid, $updated_node->uuid, t("Node updated with edit form retained same UUID."));

    // Show current node only in verbose mode.
    $this->verbose(print_r($updated_node, 1));
  }

  /**
   * Verify uuid behavior for users.
   */
  function testEnabledUserUUID() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_users' => TRUE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $object = $this->drupalCreateUser();

    // Verify fields are attached to the user.
    $this->assertIsUUID(
      $object->uuid,
      t("User has uuid field attached with enabled settings."),
      t("User UUID")
    );
  }

  /**
   * Verify uuid behavior for taxonomy.
   */
  function testEnabledTaxonomyUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior for comment.
   */
  function testEnabledCommentUUID() {
    $this->markIncomplete(t("This test is incomplete."));
  }

  /**
   * Verify uuid behavior with changed settings. All settings are forced to
   * disabled, then a node is created (with no UUID). Then settings are enabled,
   * and the node is resaved, which should trigger UUIDs being created.
   */
  function testChangedSettingNodeUUID() {
    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]'  => FALSE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $account = $this->drupalCreateUser(array('create page content'));

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $object1 = $this->drupalCreateNode($options);
    $object2 = $this->drupalCreateNode($options);

    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]' => TRUE,
    );
    $this->uuidSettingsSet($settings);

    node_save($object1);

    // Verify fields are attached to the node.
    $this->assertIsUUID(
      $object1->uuid,
      t("Node has uuid field attached with previously disabled settings."),
      t("Node UUID")
    );
    $this->assertIsUUID(
      $object1->revision_uuid,
      t("Node has revision uuid field attached with previously disabled settings."),
      t("Node revision UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($object1, 1));

    // Now programmatically set UUIDs and test with object2
    $new_uuid = uuid_uuid();
    $revision_uuid = uuid_uuid();
    $object2->uuid = $new_uuid;
    $object2->revision_uuid = $revision_uuid;
    node_save($object2);
    $loaded_node = node_load($object2->nid, NULL, TRUE);
    $this->assertEqual(
      $object2->uuid,
      $new_uuid,
      t("Node uuid can be set programmatically with changed settings."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object2->uuid,
      $loaded_node->uuid,
      t("Node uuid with changed settings is being returned correctly."),
      t("Node UUID")
    );
    $this->assertEqual(
      $object2->revision_uuid,
      $revision_uuid,
      t("Node revision uuid can be set programmatically with changed settings."),
      t("Node Revision UUID")
    );
    $this->assertEqual(
      $object2->revision_uuid,
      $loaded_node->revision_uuid,
      t("Node revision uuid with changed settings is being returned correctly."),
      t("Node Revision UUID")
    );

    // Show current node only in verbose mode.
    $this->verbose(print_r($object2, 1));
  }

}


/**
 * Test uuid and token integration.
 *
 * @todo taxonomy and comment tokends are not yet defined.
 * @link http://drupal.org/node/324712 @endlink
 */
class UUIDTokenFunctionalityTestCase extends UUIDTestHelper {
  public static function getInfo() {
    // Include dependencies to be handled by the testbot.
    return array(
      'name' => 'Token integration testcase',
      'description' => 'Test UUID token integration.',
      'group' => 'UUID',
      'dependencies' => array('token'),
    );
  }

  function setUp() {
    // By default, the helper class will include the uuid module, leaving the
    // option to include additional modules by the test class
    parent::setUp(array('token'));
  }

  /**
   * Verify global type token integration.
   *
   * Expect the token:
   *  - uuid: an arbitraty generated uuid.
   */
  function testDefaultTokens() {
    // Do not continue if token module is not enabled.
    if (!function_exists('token_replace')) {
      $this->pass(t('Token module is not installed. Skipping this test.'));
      return;
    }

    // Perform a global token replacement for [uuid] token.
    $uuid = token_replace("[uuid]", 'global', NULL);
    $this->assertIsUUID(
      $uuid,
      t("UUID Token successfully generated."),
      t("Global tokens")
    );
  }

  /**
   * Verify node type token integration (by content-type).
   *
   * Configure the module settings to support uuid for a content type. Create a
   * node of that content type and expect the tokens:
   *  - uuid
   *  - revision-uuid
   *  - author-uuid
   */
  function testDefaultNodeTokens() {
    // Do not continue if token module is not enabled.
    if (!function_exists('token_replace')) {
      $this->markImcomplete(t('Token module is not installed. Skipping this test.'));
      return;
    }

    /*
     * Verify the behaviour when the node UUID and revision UUID fields.
     */

    // Set module settings.
    $settings = array(
      'uuid_automatic_for_nodes[page]'  => TRUE,
      'uuid_automatic_for_users'        => TRUE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $account = $this->drupalCreateUser(array('create page content'));

    // Create a node with that user.
    $options = array(
      'uid'  => $account->uid,
      'type' => 'page',
    );
    $object = $this->drupalCreateNode($options);

    // This is the list of tokens and the expected value.
    $checks = array(
      'uuid'          => $object->uuid,
      'revision-uuid' => $object->revision_uuid,
      'author-uuid'   => $account->uuid,
    );
    foreach ($checks as $token => $value) {
      $this->assertEqual(
        token_replace("[" . $token . "]", 'node', $object),
        (string) $value,
        t("Token %token successfully verified.", array('%token' => $token)),
        t("Node tokens")
      );
    }

  }

  /**
   * Verify user type token integration.
   *
   * Create a user and expect the token:
   *  - uuid
   */
  function testDefaultUserTokens() {
    // Do not continue if token module is not enabled.
    if (!function_exists('token_replace')) {
      $this->pass(t('Token module is not installed. Skipping this test.'));
      return;
    }

    /*
     * Verify behavior when uuid is enabled for users.
     */

    // Set module settings.
    $settings = array(
      'uuid_automatic_for_users' => TRUE,
    );
    $this->uuidSettingsSet($settings);

    // Create a user.
    $object = $this->drupalCreateUser();

    // This is the list of tokens and the expected value.
    $checks = array(
      'uuid'  => $object->uuid,
    );

    foreach ($checks as $token => $value) {
      $this->assertEqual(
        token_replace("[" . $token . "]", 'user', $object),
        (string) $value,
        t("Token %token successfully verified.", array('%token' => $token)),
        t("User tokens")
      );
    }
  }

}
