You are here

workbench_access.test in Workbench Access 7

Test file for Workbench Access.

File

tests/workbench_access.test
View source
<?php

/**
 * @file
 * Test file for Workbench Access.
 */
abstract class WorkbenchAccessTestCase extends DrupalWebTestCase {
  protected $user_list;
  protected $editor_role;
  protected $permissions;
  function setUp() {
    parent::setUp(array(
      'workbench_access',
      'workbench_access_test',
    ));

    // Add an editor role.
    $this->permissions = array(
      'access content',
      'create page content',
      'edit any page content',
      'delete any page content',
      'access workbench access by role',
    );
    $this->editor_role = $this
      ->drupalCreateRole($this->permissions);
  }

  /**
   * Helper function to create nodes.
   */
  function createWorkbenchAccessNodes($count = 10) {

    // Create some nodes.
    for ($i = 0; $i < $count; $i++) {
      $settings = array(
        'type' => 'page',
        'title' => $this
          ->randomName(32),
        'body' => array(
          LANGUAGE_NONE => array(
            array(
              $this
                ->randomName(64),
            ),
          ),
        ),
        'promote' => 1,
      );
      $this
        ->drupalCreateNode($settings);
    }
  }

  /**
   * Helper function to create users.
   */
  function createWorkbenchAccessUsers($count = 10) {
    for ($i = 0; $i < $count; $i++) {

      // Using drupalCreateUser runs tests we don't want.
      $edit = array(
        'name' => $this
          ->randomName(32),
        'mail' => $this
          ->randomName(32) . '@example.com',
        'roles' => drupal_map_assoc(array(
          DRUPAL_AUTHENTICATED_RID,
          $this->editor_role,
        )),
        'status' => 1,
        'pass' => 'fubar',
      );
      $this->user_list[] = user_save(NULL, $edit);
    }
  }

  /**
   * Helper function for form introspection.
   *
   * Forms are sensitive to the global user account, so we must reset it when
   * using this technique.
   *
   * @param $uid
   *   A user id representing the account to test.
   * @param $type
   *   The node type of the form.
   */
  public function workbenchAccessNodeForm($uid, $type) {
    $temp = $GLOBALS['user'];
    $user = user_load($uid, TRUE);
    $GLOBALS['user'] = $user;
    $node = (object) array(
      'uid' => $user->uid,
      'name' => isset($user->name) ? $user->name : '',
      'type' => $type,
      'language' => LANGUAGE_NONE,
    );
    module_load_include('inc', 'node', 'node.pages');
    $form = drupal_get_form($type . '_node_form', $node);
    $GLOBALS['user'] = $temp;
    return $form;
  }

  /**
   * Simple method for running the same node access checks repeatedly.
   *
   * @param $nodes
   *  The nodes to check. If one fails, the test fails.
   * @param $account
   *  The user account being tested.
   * @param $message
   *  The string to prefix in front of the test result message.
   * @param $true
   *  Boolean indicator that we want to test TRUE or FALSE on this test.
   */
  function assertWorkbenchAccessCheck($nodes, $account, $message, $true = TRUE) {

    // Since we change conditions, reset node access.
    drupal_static_reset('node_access');

    // Check the node operations.
    $actions = array(
      'create',
      'update',
      'delete',
    );
    foreach ($actions as $action) {
      foreach ($nodes as $node) {
        $status[$action] = FALSE;
        if ($action == 'create') {
          $result = node_access($action, $node->type, $account);
        }
        else {
          $result = node_access($action, $node, $account);
        }
        if ($result) {
          $status[$action] = TRUE;
          break;
        }
      }
      if ($true) {
        $this
          ->assertTrue(!empty($status[$action]), t('@message. Test user can @action content.', array(
          '@message' => $message,
          '@action' => $action,
        )));
      }
      else {
        $this
          ->assertTrue(empty($status[$action]), t('@message. Test user cannot @action content.', array(
          '@message' => $message,
          '@action' => $action,
        )));
      }
    }
  }

  /**
   * Any tests that can be abstracted should go here in a new method.
   */
  function assertWorkbenchScheme($scheme, $root = 'workbench_access') {
    workbench_access_reset_tree();
    $active = workbench_access_get_active_tree();
    $this
      ->assertTrue(!empty($active['access_scheme']), t('Active access scheme set.'));
    $this
      ->assertTrue(!empty($active['tree']), t('Active access tree set.'));
    $this
      ->assertTrue(!empty($active['active']), t('Active access sections set.'));
    $this
      ->assertTrue($active['access_scheme']['access_scheme'] == $scheme, t('Using %scheme access scheme.', array(
      '%scheme' => $scheme,
    )));
    $this
      ->assertTrue(in_array($root, $active['access_scheme']['access_type_id']), t('Using Workbench Access test scheme.'));
    $this
      ->assertTrue(isset($active['tree'][$root]), t('Tree returned correctly.'));
  }

  /**
   * Helper method for grabbing a new user from the list.
   *
   * @param $id
   *   The array key of the current user being tested.
   *
   * @return
   *   A user account from the user list.
   */
  function getWorkbenchAccessUser($id = NULL) {
    if (is_null($id)) {
      $id = array_rand($this->user_list);
    }
    elseif ($id < count($this->user_list) - 1) {
      $id++;
    }
    else {
      $id--;
    }
    $account = $this->user_list[$id];

    // Store the id key for later lookups.
    $account->testId = $id;
    return $account;
  }

}
class WorkbenchAccessBaseTestCase extends WorkbenchAccessTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Workbench access installation',
      'description' => 'Test installation for Workbench Access.',
      'group' => 'Workbench Access',
    );
  }
  function testWorkbenchAccessInstall() {

    // Create some nodes and users.
    $this
      ->createWorkbenchAccessNodes();
    $this
      ->createWorkbenchAccessUsers();

    // Check for user creation.
    $this
      ->assertTrue(count($this->user_list) == 10, t('Ten new users were created.'));
    $this
      ->assertTrue(!empty($this->editor_role), t('Created editor role.'));

    // Check for node creation.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Ten initial nodes created.'));
    $nid = db_query_range("SELECT n.nid FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid", 0, 1)
      ->fetchField();
    $this
      ->assertTrue(empty($nid), t('Initial nodes have no access data.'));
  }

}
class WorkbenchAccessTaxonomyTestCase extends WorkbenchAccessTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Workbench access taxonomy',
      'description' => 'Test Taxonomy access control rules for Workbench Access.',
      'group' => 'Workbench Access',
    );
  }

  // Base test for installation.
  function testWorkbenchAccessTaxonomy() {

    // Create some nodes and users.
    $this
      ->createWorkbenchAccessNodes();
    $this
      ->createWorkbenchAccessUsers();

    // Create the taxonomy test scheme.
    module_load_include('inc', 'workbench_access', 'workbench_access.admin');
    workbench_access_example_taxonomy();
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Initial nodes assigned access data.'));

    // Check that the vocabulary is setup correctly.
    $this
      ->assertWorkbenchScheme('taxonomy');

    // Check that nodes have been assigned to the top-level item.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid WHERE wan.access_id = 'workbench_access' AND wan.access_scheme = 'taxonomy'")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Initial nodes assigned to top-level hierarchy.'));

    // Test access settings of user 1.
    $account = user_load(1, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access['workbench_access']), t('User 1 assigned to top-level hierarchy.'));

    // Change the machine_name of the vocabulary and check that we track
    // the change. See https://drupal.org/node/1779502
    $vocabulary = taxonomy_vocabulary_machine_name_load('workbench_access');
    $vocabulary->machine_name = 'workbench_access_foo';
    taxonomy_vocabulary_save($vocabulary);

    // Check that the vocabulary is setup correctly.
    workbench_access_reset_tree();
    $active = workbench_access_get_active_tree();

    // Check that we reset properly.
    $this
      ->assertTrue(isset($active['tree']['workbench_access_foo']) && $active['active'][1]['access_type_id'] == 'workbench_access_foo', t('Updated vocabulary machine_name successfully.'));

    // Check that nodes have been assigned to the top-level item.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid WHERE wan.access_id = 'workbench_access_foo' AND wan.access_scheme = 'taxonomy'")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Initial nodes re-assigned to top-level hierarchy.'));

    // Test access settings of user 1.
    $account = user_load(1, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access['workbench_access_foo']), t('User 1 re-assigned to top-level hierarchy.'));

    // Set things back.
    $vocabulary->machine_name = 'workbench_access';
    taxonomy_vocabulary_save($vocabulary);
    workbench_access_reset_tree();

    // Assign a user to a section and check permissions.
    // This is a multi-step check.
    // First, the user should not be able to do anything (Create, Update or Delete).
    $account = $this
      ->getWorkbenchAccessUser();
    $id = $account->testId;
    $this
      ->assertTrue(empty($account->workbench_access['workbench_access']), t('Test user not assigned to a section.'));
    $nids = db_query("SELECT nid FROM {node}")
      ->fetchAllAssoc('nid');
    $nodes = node_load_multiple(array_keys($nids));
    $assigned = TRUE;
    foreach ($nodes as $node) {
      if (!isset($node->workbench_access['workbench_access'])) {
        $assigned = FALSE;
      }
    }
    $this
      ->assertTrue(!empty($assigned), t('All nodes properly assigned.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('No sections'), FALSE);

    // Test that the role lookup function works correctly.
    $roles = workbench_access_get_roles('access workbench access by role');

    // The 'administrator' role always has permission.
    $this
      ->assertTrue(count($roles) == 2, t('One user role assigned.'));

    // Now, we assign the user to a section and check again.
    workbench_access_user_section_save($account->uid, 'workbench_access', $active['access_scheme']['access_scheme']);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access['workbench_access']), t('Test user assigned to top-level hierarchy.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Assigned sections'), TRUE);

    // Remove the permission to 'access workbench access by role' and check again.
    user_role_revoke_permissions($this->editor_role, array(
      'access workbench access by role',
    ));
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(empty($account->workbench_access['workbench_access']), t('Permission revoked and test user not assigned to a section.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Role disallowed'), FALSE);

    // Test that the role lookup function works correctly.
    drupal_static_reset('workbench_access_get_roles');
    $roles = workbench_access_get_roles('access workbench access by role');

    // The 'administrator' role always has permission. Ignore that.
    $this
      ->assertTrue(count($roles) == 1, t('No user roles assigned.'));

    // Now give them permissions again.
    user_role_grant_permissions($this->editor_role, array(
      'access workbench access by role',
    ));
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access['workbench_access']), t('Permission reinstated and test user assigned to a section.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Role allowed'), TRUE);

    // Test the autocomplete query for adding new editors.
    drupal_static_reset('workbench_access_get_roles');
    module_load_include('inc', 'workbench_access', 'workbench_access.pages');

    // Search for the existing user via autocomplete. Should return empty.
    $test = workbench_access_autocomplete('taxonomy', 'workbench_access', substr($account->name, 0), TRUE);
    $this
      ->assertTrue(empty($test), t('Autocomplete did not match assigned user.'));
    $test_account = $this
      ->getWorkbenchAccessUser($id);
    $test = workbench_access_autocomplete('taxonomy', 'workbench_access', substr($test_account->name, 0, 1), TRUE);
    $this
      ->assertTrue(!empty($test), t('Autocomplete matched unassigned user.'));

    // Now take away the core permissions to page content and test.
    $perms = array(
      'create page content',
      'edit any page content',
      'delete any page content',
    );
    user_role_revoke_permissions($this->editor_role, $perms);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Page access disallowed'), FALSE);

    // Now give back the core permissions.
    user_role_grant_permissions($this->editor_role, $perms);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Page access allowed'), TRUE);

    // Form testing in Drupal is horribly broken.
    // We can confirm that a form page is loaded, but cannot perform
    // any introspection on the $form array.
    $account->pass_raw = 'fubar';
    $this
      ->drupalLogin($account);

    // Set the form label.
    // Attempt to access edit page.
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertResponse(200);
    $this
      ->assertRaw('Section', t('Workbench Access field was found.'));

    // Note that the field is nested as
    // $form['workbench_access']['workbench_access'], which forces FormAPI to
    // add the --2 suffix to the id.
    $this
      ->assertRaw('<select id="edit-workbench-access--2" name="workbench_access" class="form-select required">', t('Form presents a select list with no multiple select.'));

    // Change some values and try again.
    variable_set('workbench_access_allow_multiple', 1);
    variable_set('workbench_access_label', 'TestWA');
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertRaw('TestWA', t('Workbench Access field was renamed.'));
    $this
      ->assertRaw('<select multiple="multiple" name="workbench_access[]" id="edit-workbench-access--2" class="form-select required">', t('Form presents a select list with multiple select.'));

    // Try some form introspection.
    $form = $this
      ->workbenchAccessNodeForm($account->uid, $node->type);
    $this
      ->assertTrue(isset($form['workbench_access']['workbench_access']['#options']), t('Form element returns properly.'));
    $this
      ->assertTrue(count($form['workbench_access']['workbench_access']['#options']) == 10, t('Form element returned ten options to user with all sections.'));

    // Delete global permission.
    workbench_access_user_section_delete($account->uid, 'workbench_access', 'taxonomy');

    // Add sub permission.
    $term = taxonomy_term_load(1);
    workbench_access_user_section_save($account->uid, $term->tid, 'taxonomy');
    $form = $this
      ->workbenchAccessNodeForm($account->uid, $node->type);
    $this
      ->assertTrue(count($form['workbench_access']['workbench_access']['#options']) == 3, t('Form element returned three options to user with limited options.'));

    // Check that access control by node type settings work.
    $this
      ->assertTrue(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access enforced for %type content.', array(
      '%type' => $node->type,
    )));

    // Force a fail by removing this user's access rules. Else it will just
    // return NODE_ACCESS_IGNORE, which cannot be tested.
    $account->workbench_access = array(
      'foo',
    );
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // Since the user is not in a section, this should DENY, unless the node
    // type is ignored.
    // Test for ignore.
    variable_set('workbench_access_node_type_' . $node->type, 0);
    $this
      ->assertFalse(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access not enforced for %type content.', array(
      '%type' => $node->type,
    )));
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_IGNORE, t('Workbench Access rules ignored on test node.'));

    // Test for deny.
    variable_set('workbench_access_node_type_' . $node->type, 1);
    $this
      ->assertTrue(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access enforced for %type content.', array(
      '%type' => $node->type,
    )));
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // If the node is not assigned, we should ignore.
    $temp = $node->workbench_access;
    $node->workbench_access = array();
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_IGNORE, t('Workbench Access rules ignored for unassigned node.'));

    // Make sure the above was not a false positive.
    $node->workbench_access = $temp;
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // Delete the user account.
    user_delete($account->uid);
    $records = db_query("SELECT 1 FROM {workbench_access_user} WHERE uid = :uid", array(
      ':uid' => $account->uid,
    ))
      ->fetchField();
    $this
      ->assertFalse($records, 'User section assignments removed.');

    // Test module hooks.
    // Ensure that our node type uses the form element.
    $GLOBALS['conf']['workbench_access_node_type_' . $node->type] = TRUE;
    $form = drupal_get_form($node->type . '_node_form', $node);
    $this
      ->assertTrue(!empty($form['workbench_access']['workbench_access']['#workbench_access_test']), t('hook_workbench_access_node_element_alter() fired correctly.'));
  }

}
class WorkbenchAccessMenuTestCase extends WorkbenchAccessTestCase {
  protected $node_titles = array(
    1 => 'A',
    3 => 'A1',
    7 => 'A1a',
    15 => 'A1a1',
    16 => 'A1a2',
    8 => 'A1b',
    17 => 'A1b1',
    18 => 'A1b2',
    4 => 'A2',
    9 => 'A2a',
    19 => 'A2a1',
    20 => 'A2a2',
    10 => 'A2b',
    21 => 'A2b1',
    22 => 'A2b2',
    2 => 'B',
    5 => 'B1',
    11 => 'B1a',
    23 => 'B1a1',
    24 => 'B1a2',
    12 => 'B1b',
    25 => 'B1b1',
    26 => 'B1b2',
    6 => 'B2',
    13 => 'B2a',
    27 => 'B2a1',
    28 => 'B2a2',
    14 => 'B2b',
    29 => 'B2b1',
    30 => 'B2b2',
  );
  public static function getInfo() {
    return array(
      'name' => 'Workbench access menu',
      'description' => 'Test Menu access control rules for Workbench Access.',
      'group' => 'Workbench Access',
    );
  }
  function testWorkbenchAccessMenu() {

    // Load all includes.
    workbench_access_load_include();

    // Create some node and users.
    $this
      ->createWorkbenchAccessUsers();
    $this
      ->createWorkbenchAccessNodes(30);

    // Set node titles for easier debugging.
    for ($i = 1; $i <= 30; $i++) {
      $node = node_load($i);
      $node->title = $this->node_titles[$i];
      node_save($node);
    }

    // Create a menu.
    $menu = array(
      'menu_name' => 'menu-workbench_access',
      'description' => '',
      'title' => $this
        ->randomName(16),
    );
    menu_save($menu);
    $menu_name = $menu['menu_name'];

    // Attach menu to content type.
    variable_set('menu_options_page', array(
      'menu_options[' . $menu_name . ']' => $menu_name,
    ));

    // Make all menu items access sections.
    variable_set('workbench_access_auto_assign', 1);

    // Set this menu as our active section.
    variable_set('workbench_access', 'menu');
    variable_set('workbench_access_menu', array(
      $menu_name,
    ));

    // Use a custom form element.
    variable_set('workbench_access_custom_form', 1);

    // Set up the top-level menu.
    $section = array(
      'access_id' => $menu_name,
      'access_scheme' => 'menu',
      'access_type' => 'menu',
      'access_type_id' => $menu_name,
    );
    workbench_access_section_save($section);

    /**
     * Assign menu links for nodes.
     * The hierarchy should four levels deep with two options per level
     * A helpful diagram
     *
     * A
     *   A1
     *     A1a
     *        A1a1
     *        A1a2
     *     A1b
     *        A1b1
     *        A1b2
     *   A2
     *     A2a
     *        A2a1
     *        A2a2
     *     A2b
     *        A2b1
     *        A2b2
     * B
     *   B1
     *     B1a
     *        B1a1
     *        B1a2
     *     B1b
     *        B1b1
     *        B1b2
     *   B2
     *     B2a
     *        B2a1
     *        B2a2
     *     B2b
     *        B2b1
     *        B2b2
     */
    $mlids = array();
    $nodes = db_query("SELECT nid, title FROM {node} ORDER BY nid")
      ->fetchAll();
    foreach ($nodes as $nid => $node) {

      // Create a menu item
      $settings = array(
        'link_path' => 'node/' . $node->nid,
        'link_title' => $node->title,
        'menu_name' => $menu_name,
        'weight' => $node->nid,
        'plid' => 0,
      );
      if ($nid > 1) {
        $settings['plid'] = $mlids[floor(($nid - 2) / 2)];
      }
      $mlids[] = menu_link_save($settings);
    }

    // Check that the menu scheme is setup correctly.
    $this
      ->assertWorkbenchScheme('menu', $menu_name);

    // Check for node creation.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n")
      ->fetchField();
    $this
      ->assertTrue($count == 30, t('Thirty initial nodes created.'));

    // Check for node assignment.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid")
      ->fetchField();
    $this
      ->assertTrue($count == 0, t('Initial nodes have no access data.'));

    // Save data for nodes.
    foreach ($nodes as $node) {
      $mlid = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :link_path", array(
        ':link_path' => 'node/' . $node->nid,
      ))
        ->fetchField();
      if (!isset($first_mlid)) {
        $first_mlid = $mlid;
      }
      $edit = array(
        'nid' => $node->nid,
        'access_id' => $mlid,
        'access_scheme' => 'menu',
      );
      db_insert('workbench_access_node')
        ->fields($edit)
        ->execute();
    }

    // Assign user 1 to the top-level.
    workbench_access_user_section_save(1, $menu_name, 'menu');

    // Check node data.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid")
      ->fetchField();
    $this
      ->assertTrue($count == 30, t('Initial nodes assigned access data.'));
    $active = workbench_access_get_active_tree();

    // Check that a node has been assigned to a first-level menu item.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid WHERE wan.access_id = :access_id AND wan.access_scheme = 'menu'", array(
      ':access_id' => $first_mlid,
    ))
      ->fetchField();
    $this
      ->assertTrue($count == 1, t('One node assigned to first-level menu item.'));

    // Test access settings of user 1.
    $account = user_load(1, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access[$menu_name]), t('User 1 assigned to top-level hierarchy.'));

    // End setup tests.
    // Assign a user to a section and check permissions.
    // This is a multi-step check.
    // First, the user should not be able to do anything (Create, Update or Delete).
    $account = $this
      ->getWorkbenchAccessUser();
    $id = $account->testId;
    $this
      ->assertTrue(empty($account->workbench_access[$menu_name]), t('Test user not assigned to a section.'));
    $nids = db_query("SELECT nid FROM {node}")
      ->fetchCol('nid');
    $nodes = node_load_multiple($nids);
    $assigned = TRUE;
    foreach ($nodes as $node) {

      // Just check if its set or not
      if (!isset($node->workbench_access) && !empty($node->workbench_access)) {
        $assigned = FALSE;
      }
    }
    $this
      ->assertTrue(!empty($assigned), t('All nodes properly assigned.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('No sections'), FALSE);

    // Test that the role lookup function works correctly.
    $roles = workbench_access_get_roles('access workbench access by role');

    // The 'administrator' role always has permission.
    $this
      ->assertTrue(count($roles) == 2, t('One user role assigned.'));

    // Now, we assign the user to a section and check again.
    workbench_access_user_section_save($account->uid, $menu_name, 'menu');
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access[$menu_name]), t('Test user assigned to top-level hierarchy.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Assigned sections'), TRUE);

    // Remove the permission to 'access workbench access by role' and check again.
    user_role_revoke_permissions($this->editor_role, array(
      'access workbench access by role',
    ));
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(empty($account->workbench_access[$menu_name]), t('Permission revoked and test user not assigned to a section.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Role disallowed'), FALSE);

    // Test that the role lookup function works correctly.
    drupal_static_reset('workbench_access_get_roles');
    $roles = workbench_access_get_roles('access workbench access by role');

    // The 'administrator' role always has permission. Ignore that.
    $this
      ->assertTrue(count($roles) == 1, t('One user roles assigned.'));

    // Now give them permissions again.
    user_role_grant_permissions($this->editor_role, array(
      'access workbench access by role',
    ));
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access[$menu_name]), t('Permission reinstated and test user assigned to a section.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Role allowed'), TRUE);

    // Test the autocomplete query for adding new editors.
    drupal_static_reset('workbench_access_get_roles');
    module_load_include('inc', 'workbench_access', 'workbench_access.pages');

    // Search for the existing user via autocomplete. Should return empty.
    $test = workbench_access_autocomplete('menu', $menu_name, substr($account->name, 0), TRUE);
    $this
      ->assertTrue(empty($test), t('Autocomplete did not match assigned user.'));

    // Test another user who is currently not assigned.
    $test_account = $this
      ->getWorkbenchAccessUser($id);
    $test = workbench_access_autocomplete('menu', $menu_name, substr($test_account->name, 0, 1), TRUE);
    $this
      ->assertTrue(!empty($test), t('Autocomplete matched unassigned user.'));

    // Now take away the core permissions to page content and test.
    $perms = array(
      'create page content',
      'edit any page content',
      'delete any page content',
    );
    user_role_revoke_permissions($this->editor_role, $perms);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Page access disallowed'), FALSE);

    // Now give back the core permissions.
    user_role_grant_permissions($this->editor_role, $perms);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Page access allowed'), TRUE);

    // Form testing in Drupal is horribly broken.
    // We can confirm that a form page is loaded, but cannot perform
    // any introspection on the $form array.
    $account->pass_raw = 'fubar';
    $this
      ->drupalLogin($account);

    // Set the form label.
    // Attempt to access edit page.
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertResponse(200);
    $this
      ->assertRaw('Section', t('Workbench Access field was found.'));

    // Note that the field is nested as
    // $form['workbench_access']['workbench_access'], which forces FormAPI to
    // add the --2 suffix to the id.
    $this
      ->assertRaw('<select id="edit-workbench-access--2" name="workbench_access" class="form-select required">', t('Form presents a select list with no multiple select.'));

    // Change some values and try again.
    variable_set('workbench_access_allow_multiple', 1);
    variable_set('workbench_access_label', 'TestWA');
    $this
      ->drupalGet("node/{$node->nid}/edit");
    $this
      ->assertRaw('TestWA', t('Workbench Access field was renamed.'));
    $this
      ->assertRaw('<select multiple="multiple" name="workbench_access[]" id="edit-workbench-access--2" class="form-select required">', t('Form presents a select list with multiple select.'));

    // Test module hooks.
    // Ensure that our node type uses the form element.
    $GLOBALS['conf']['workbench_access_node_type_' . $node->type] = TRUE;
    module_load_include('inc', 'node', 'node.pages');
    $form = drupal_get_form($node->type . '_node_form', $node);
    $this
      ->assertTrue(!empty($form['workbench_access']['workbench_access']['#workbench_access_test']), t('hook_workbench_access_node_element_alter() fired correctly.'));

    // Try some form introspection.
    $form = $this
      ->workbenchAccessNodeForm($account->uid, $node->type);
    $this
      ->assertTrue(isset($form['workbench_access']['workbench_access']['#options']), t('Form element returns properly.'));
    $this
      ->assertTrue(count($form['workbench_access']['workbench_access']['#options']) == 31, t('Form element returned thirty-one options (top menu and thirty nodes) to user with all sections.'));

    // Delete global permission.
    workbench_access_user_section_delete($account->uid, $menu_name, 'menu');

    // Add sub permission.
    $array = array_slice($active['tree'][$menu_name]['children'], 0, 1);
    $mlid = array_shift($array);
    workbench_access_user_section_save($account->uid, $mlid, 'menu');
    $form = $this
      ->workbenchAccessNodeForm($account->uid, $node->type);
    $this
      ->assertTrue(count($form['workbench_access']['workbench_access']['#options']) == 15, t('Form element returned fifteen options to user with limited options.'));

    // Test the form again using native support.
    // Do not set a custom form element.
    variable_set('workbench_access_custom_form', 0);
    $custom = variable_get('workbench_access_custom_form', 1);
    $this
      ->assertTrue(empty($custom), t('Switched to using native menu form.'));

    // Try some form introspection.
    $form = $this
      ->workbenchAccessNodeForm($account->uid, $node->type);
    $this
      ->assertTrue(count($form['menu']['link']['parent']['#options']) == 15, t('Native menu form element returned fifteen options to user with limited options.'));

    // Check that access control by node type settings work.
    $this
      ->assertTrue(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access enforced for %type content.', array(
      '%type' => $node->type,
    )));

    // Force a fail by removing this user's access rules. Else it will just
    // return NODE_ACCESS_IGNORE, which cannot be tested.
    $account->workbench_access = array(
      'foo',
    );
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // Since the user is not in a section, this should DENY, unless the node
    // type is ignored.
    // Test for ignore.
    variable_set('workbench_access_node_type_' . $node->type, 0);
    $this
      ->assertFalse(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access not enforced for %type content.', array(
      '%type' => $node->type,
    )));
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_IGNORE, t('Workbench Access rules ignored on test node.'));

    // Test for deny.
    variable_set('workbench_access_node_type_' . $node->type, 1);
    $this
      ->assertTrue(variable_get('workbench_access_node_type_' . $node->type, 1), t('Workbench Access enforced for %type content.', array(
      '%type' => $node->type,
    )));
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // If the node is not assigned, we should ignore.
    $temp = $node->workbench_access;
    $node->workbench_access = array();
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_IGNORE, t('Workbench Access rules ignored for unassigned node.'));

    // Make sure the above was not a false positive.
    $node->workbench_access = $temp;
    $response = workbench_access_node_access($node, 'update', $account);
    $this
      ->assertTrue($response == NODE_ACCESS_DENY, t('Workbench Access rules enforced on test node.'));

    // Delete the user account.
    user_delete($account->uid);
    $records = db_query("SELECT 1 FROM {workbench_access_user} WHERE uid = :uid", array(
      ':uid' => $account->uid,
    ))
      ->fetchField();
    $this
      ->assertFalse($records, 'User section assignments removed.');

    // Tests access per menu levels.  We have structured our menu so that we
    // know what these results should return.
    // Test another user who is currently not assigned.
    $account = $this
      ->getWorkbenchAccessUser($id);

    // @todo: This is pretty obtuse. Can we improve it?
    // @params are $account, $nid, $depth, $expected_count.
    // See the map in the large docblock at the top of this test.
    $this
      ->assertMenuUpdateNodes($account, 1, 1, 15);
    $this
      ->assertMenuUpdateNodes($account, 3, 2, 7);
    $this
      ->assertMenuUpdateNodes($account, 7, 3, 3);
    $this
      ->assertMenuUpdateNodes($account, 15, 4, 1);

    // Test for issue #1203260. Some menu links cause fatal error.
    // Make all menu items access sections.
    variable_set('workbench_access_auto_assign', 1);

    // Create a menu item that triggers the failure, which is based on access
    // checks for menu links while we are fetching our raw tree.
    $link = array(
      'link_path' => 'node/add',
      'link_title' => 'Add content',
      'menu_name' => $menu_name,
      'weight' => 0,
      'plid' => 0,
    );
    menu_link_save($link);

    // Bug is triggered by anonymous users building the cache.
    $this
      ->drupalLogout();
    workbench_access_reset_tree();

    // Check that the menu scheme is setup correctly.
    drupal_flush_all_caches();
    $this
      ->drupalGet('node');

    // If we hit an infinite loop, the page does not return listings.
    // Note the HTML fragent here, which assures success on various installs.
    $this
      ->assertRaw('node/30">B2b2</a>', t('Node listing page returns properly.'));
    $this
      ->assertWorkbenchScheme('menu', $menu_name);
  }

  /**
   * Helper function for testing node access over the menu hierarchy.
   *
   * @param $account
   *   A user account object.
   * @param $nid
   *   The node id to test for access control.
   * @param $depth
   *   The menu depth of the node being tested.
   * @param $expected_count
   *   A count of the expected returns based on the known children of this node.
   *   See the setup in $this->node_titles.
   */
  private function assertMenuUpdateNodes($account, $nid, $depth, $expected_count) {

    // Since we change conditions, reset node access.
    drupal_static_reset('node_access');

    // Confirm the account isn't assigned any sections.
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(empty($account->workbench_access), t('Test user not assigned any sections.'));

    // Assign section
    $mlid = db_query("SELECT link_title, mlid FROM {menu_links} WHERE link_path = :link_path", array(
      ':link_path' => 'node/' . $nid,
    ))
      ->fetchObject();
    if (empty($account->workbench_access[$mlid->mlid])) {
      workbench_access_user_section_save($account->uid, $mlid->mlid, 'menu');
    }
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(array_key_exists($mlid->mlid, $account->workbench_access), t('Test user assigned section :section.', array(
      ':section' => $mlid->link_title,
    )));

    // Get an array of editable nodes.
    // Use db_select because of the variable depth.
    $query = db_select('menu_links', 'ml');
    $query
      ->condition('p' . $depth, $mlid->mlid);
    $query
      ->addExpression("SUBSTRING(ml.link_path, 6, (LENGTH(ml.link_path) - 1))", 'nid');
    $count = $query
      ->countQuery()
      ->execute()
      ->fetchField();
    $this
      ->assertTrue($count == $expected_count, t('!mlids menu items found associated with a !depth-level deep node.', array(
      '!mlids' => $expected_count,
      '!depth' => $depth,
    )));
    $editable_nids = $query
      ->execute()
      ->fetchCol();
    sort($editable_nids);

    // Check update permission on editable nodes.
    foreach ($editable_nids as $nid) {
      $node = node_load($nid, NULL, TRUE);
      $result = node_access('update', $node, $account);
      $this
        ->assertTrue($result, t('Page update allowed on :title.', array(
        ':title' => $node->title,
      )));
    }

    // Get an array of non-editable nodes.
    $non_editable_nids = db_query("SELECT nid FROM {node} WHERE nid NOT IN (:nids)", array(
      ':nids' => $editable_nids,
    ))
      ->fetchCol();

    // Check update permission on non-editable nodes.
    foreach ($non_editable_nids as $nid) {
      $node = node_load($nid, null, TRUE);
      $result = node_access('update', $node, $account);
      $this
        ->assertTrue(!$result, t('Page update disallowed on :title.', array(
        ':title' => $node->title,
      )));
    }

    // Revoke assigned section
    workbench_access_user_section_delete($account->uid, $mlid->mlid, 'menu');
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(empty($account->workbench_access), t('Test user removed from section :section.', array(
      ':section' => $mlid->link_title,
    )));
  }

}
class WorkbenchAccessTokenTestCase extends DrupalWebTestCase {
  private $nodes = array();
  public static function getInfo() {
    return array(
      'name' => 'Workbench access tokens',
      'description' => 'Tests tokens provided by Workbench Access.',
      'group' => 'Workbench Access',
      'dependencies' => array(
        'token',
      ),
    );
  }
  function setUp() {
    parent::setUp('workbench_access', 'token');

    // Allow nodes to be assigned multiple sections.
    variable_set('workbench_access_allow_multiple', 1);

    // Disable access control for basic page nodes.
    variable_set('workbench_access_node_type_page', 0);
  }
  function testTaxonomyTokens() {
    $assigned_node = $this
      ->drupalCreateNode(array(
      'type' => 'article',
    ));
    module_load_include('inc', 'workbench_access', 'admin');
    workbench_access_example_taxonomy();
    $assigned_node = node_load($assigned_node->nid);

    // Fetch a list of additional sections to be used in the tests.
    $terms = taxonomy_get_term_by_name('Exhibits');
    $terms += taxonomy_get_term_by_name('Library');
    $terms += taxonomy_get_term_by_name('Gift Shop');

    // Test tokens for a node that has been assigned to the Museum section only.
    $tokens = array(
      'workbench-access-sections' => 'Museum',
      'workbench-access-sections:keys' => 'workbench_access',
      'workbench-access-sections:count' => 1,
    );
    $this
      ->assertTokens('node', array(
      'node' => $assigned_node,
    ), $tokens);
    $assigned_node->workbench_access = array_keys($terms);
    node_save($assigned_node);

    // Test tokens now that the node has multiple section assignments.
    $assigned_node = node_load($assigned_node->nid);
    $tokens = array(
      'workbench-access-sections' => 'Exhibits, Library, Gift Shop',
      'workbench-access-sections:keys' => implode(', ', array_keys($terms)),
      'workbench-access-sections:count' => 3,
      'workbench-access-sections:first' => 'Exhibits',
      'workbench-access-sections:last' => 'Gift Shop',
    );
    $this
      ->assertTokens('node', array(
      'node' => $assigned_node,
    ), $tokens);

    // Test tokens for a node that has not been assigned to any sections.
    $unassigned_node = $this
      ->drupalCreateNode(array(
      'type' => 'article',
    ));
    $tokens = array(
      'workbench-access-sections' => 'Unassigned',
      'workbench-access-sections:keys' => NULL,
      'workbench-access-sections:count' => NULL,
    );
    $this
      ->assertTokens('node', array(
      'node' => $unassigned_node,
    ), $tokens);

    // Test tokens for a node that is not under section access control.
    $unassigned_node = $this
      ->drupalCreateNode(array(
      'type' => 'page',
    ));
    $tokens = array(
      'workbench-access-sections' => NULL,
      'workbench-access-sections:keys' => NULL,
      'workbench-access-sections:count' => NULL,
    );
    $this
      ->assertTokens('node', array(
      'node' => $unassigned_node,
    ), $tokens);

    // Test tokens for a user that has been assigned to the Museum section only.
    $admin_user = user_load(1);
    $tokens = array(
      'workbench-access-sections' => 'Museum',
      'workbench-access-sections:keys' => 'workbench_access',
      'workbench-access-sections:count' => 1,
    );
    $this
      ->assertTokens('user', array(
      'user' => $admin_user,
    ), $tokens);

    // Change the sections for the user.
    foreach ($terms as $term) {
      workbench_access_user_section_save($admin_user->uid, $term->tid, 'taxonomy');
    }
    workbench_access_user_section_delete($admin_user->uid, 'workbench_access', 'taxonomy');

    // Test tokens now that the user has multiple section assignments.
    $admin_user = user_load(1);
    $tokens = array(
      'workbench-access-sections' => 'Exhibits, Library, Gift Shop',
      'workbench-access-sections:keys' => implode(', ', array_keys($terms)),
      'workbench-access-sections:count' => 3,
      'workbench-access-sections:first' => 'Exhibits',
      'workbench-access-sections:last' => 'Gift Shop',
    );
    $this
      ->assertTokens('user', array(
      'user' => $admin_user,
    ), $tokens);

    // Test tokens for a user that is not assigned to any sections.
    $unassigned_user = $this
      ->drupalCreateUser();
    $tokens = array(
      'workbench-access-sections' => NULL,
      'workbench-access-sections:keys' => NULL,
      'workbench-access-sections:count' => NULL,
    );
    $this
      ->assertTokens('user', array(
      'user' => $unassigned_user,
    ), $tokens);

    // Test the site-wide access scheme tokens.
    $tokens = array(
      'workbench-access-scheme' => t('Taxonomy'),
      'workbench-access-scheme:name' => t('Taxonomy'),
      'workbench-access-scheme:machine-name' => 'taxonomy',
      'workbench-access-scheme:description' => t('Uses taxonomy vocabularies for assigning hierarchical access control.'),
    );
    $this
      ->assertTokens('site', array(), $tokens);

    // Change the site-wide scheme to menu.module.
    variable_set('workbench_access', 'menu');
    $tokens = array(
      'workbench-access-scheme' => t('Menu'),
      'workbench-access-scheme:name' => t('Menu'),
      'workbench-access-scheme:machine-name' => 'menu',
      'workbench-access-scheme:description' => t('Uses the menu system for assigning hierarchical access control.'),
    );
    $this
      ->assertTokens('site', array(), $tokens);
  }

  /**
   * Function copied from TokenTestHelper::assertTokens().
   */
  function assertTokens($type, array $data, array $tokens, array $options = array()) {
    $token_input = drupal_map_assoc(array_keys($tokens));
    $values = token_generate($type, $token_input, $data, $options);
    foreach ($tokens as $token => $expected) {
      if (!isset($expected)) {
        $this
          ->assertTrue(!isset($values[$token]), t("Token value for [@type:@token] was not generated.", array(
          '@type' => $type,
          '@token' => $token,
        )));
      }
      elseif (!isset($values[$token])) {
        $this
          ->fail(t("Token value for [@type:@token] was not generated.", array(
          '@type' => $type,
          '@token' => $token,
        )));
      }
      elseif (!empty($options['regex'])) {
        $this
          ->assertTrue(preg_match('/^' . $expected . '$/', $values[$token]), t("Token value for [@type:@token] was '@actual', matching regular expression pattern '@expected'.", array(
          '@type' => $type,
          '@token' => $token,
          '@actual' => $values[$token],
          '@expected' => $expected,
        )));
      }
      else {
        $this
          ->assertIdentical($values[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array(
          '@type' => $type,
          '@token' => $token,
          '@actual' => $values[$token],
          '@expected' => $expected,
        )));
      }
    }
    return $values;
  }

}
class WorkbenchAccessRoleTestCase extends WorkbenchAccessTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Workbench access role',
      'description' => 'Test Taxonomy access control rules applied by role.',
      'group' => 'Workbench Access',
    );
  }

  // Test for assignment by role.
  function testWorkbenchAccessRoles() {

    // Create some nodes and users.
    $this
      ->createWorkbenchAccessNodes();
    $this
      ->createWorkbenchAccessUsers();

    // Create the taxonomy test scheme.
    module_load_include('inc', 'workbench_access', 'workbench_access.admin');
    workbench_access_example_taxonomy();
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Initial nodes assigned access data.'));

    // Check that the vocabulary is setup correctly.
    $this
      ->assertWorkbenchScheme('taxonomy');

    // Check that nodes have been assigned to the top-level item.
    $count = db_query("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {workbench_access_node} wan ON n.nid = wan.nid WHERE wan.access_id = 'workbench_access' AND wan.access_scheme = 'taxonomy'")
      ->fetchField();
    $this
      ->assertTrue($count == 10, t('Initial nodes assigned to top-level hierarchy.'));

    // First, the user should not be able to do anything (Create, Update or Delete).
    $account = $this
      ->getWorkbenchAccessUser();
    $id = $account->testId;
    $this
      ->assertTrue(empty($account->workbench_access['workbench_access']), t('Test user not assigned to a section.'));
    $nids = db_query("SELECT nid FROM {node}")
      ->fetchAllAssoc('nid');
    $nodes = node_load_multiple(array_keys($nids));
    $assigned = TRUE;
    foreach ($nodes as $node) {
      if (!isset($node->workbench_access['workbench_access'])) {
        $assigned = FALSE;
      }
    }
    $this
      ->assertTrue(!empty($assigned), t('All nodes properly assigned.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('No sections'), FALSE);

    // Test that the role lookup function works correctly.
    $roles = workbench_access_get_roles('access workbench access by role');

    // The 'administrator' role always has permission.
    $this
      ->assertTrue(count($roles) == 2, t('One user role assigned.'));

    // Now, we assign the user's role to a section and check again.
    $active = workbench_access_get_active_tree();
    workbench_access_role_section_save($this->editor_role, 'workbench_access', $active['access_scheme']['access_scheme']);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(!empty($account->workbench_access['workbench_access']), t('Test user assigned to top-level hierarchy.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('Assigned sections by role'), TRUE);

    // Revoke the role and test again.
    workbench_access_role_section_delete($this->editor_role, 'workbench_access', $active['access_scheme']['access_scheme']);
    $account = user_load($account->uid, TRUE);
    $this
      ->assertTrue(empty($account->workbench_access['workbench_access']), t('Test user removed from top-level hierarchy.'));
    $this
      ->assertWorkbenchAccessCheck($nodes, $account, t('No assigned sections by role'), FALSE);
  }

}