You are here

access.test in Access Control Kit 7

Tests for the access control kit module.

File

access.test
View source
<?php

/**
 * @file
 * Tests for the access control kit module.
 */

/**
 * Provides common helper methods for access control kit module tests.
 */
class AccessWebTestCase extends DrupalWebTestCase {

  /**
   * Creates and returns a new access scheme with random properties.
   *
   * @param string $type
   *   (optional) The scheme type. Defaults to 'boolean'.
   * @param array $settings
   *   (optional) The scheme settings. Defaults to array().
   *
   * @return object
   *   An access scheme.
   */
  public function createScheme($type = 'boolean', $settings = array()) {
    $machine_name = drupal_strtolower($this
      ->randomName());
    $name = $this
      ->randomName();
    $description = $this
      ->randomName();

    // Create a scheme.
    $scheme = entity_get_controller('access_scheme')
      ->create(array(
      'machine_name' => $machine_name,
      'name' => $name,
      'type' => $type,
      'description' => $description,
    ));
    $scheme->settings = $settings;
    $this
      ->assertNull($scheme->sid);

    // Save the scheme and confirm that values come back correct.
    $this
      ->assertIdentical(access_scheme_save($scheme), SAVED_NEW, 'New scheme saved.');
    $this
      ->assertNotNull($scheme->sid);
    $this
      ->assertIdentical($scheme->machine_name, $machine_name);
    $this
      ->assertIdentical($scheme->name, $name);
    $this
      ->assertIdentical($scheme->type, $type);
    $this
      ->assertIdentical($scheme->description, $description);
    $this
      ->assertIdentical($scheme->settings, $settings);
    return $scheme;
  }

  /**
   * Creates a list-based scheme with generic realms.
   *
   * @param string $type
   *   The list type: 'integer', 'float', or 'text'.
   *
   * @return object
   *   An access scheme.
   */
  public function createListScheme($type) {
    switch ($type) {
      case 'integer':
        $realms = array(
          1 => '1 day',
          7 => '1 week',
          31 => '1 month',
        );
        break;
      case 'float':
        $realms = array(
          '0' => '0',
          '.25' => '1/4',
          '.75' => '3/4',
          '1' => '1',
        );
        break;
      case 'text':
        $realms = array(
          'IL' => 'Illinois',
          'IA' => 'Iowa',
          'IN' => 'Indiana',
        );
        break;
      default:
        $realms = array();
    }
    field_create_field(array(
      'field_name' => 'field_' . $type,
      'type' => 'list_' . $type,
      'settings' => array(
        'allowed_values' => $realms,
      ),
    ));
    $scheme = $this
      ->createScheme('list_' . $type, array(
      'field_name' => 'field_' . $type,
    ));
    return access_scheme_load($scheme->sid);
  }

  /**
   * Creates a taxonomy-based scheme with randomly named realms.
   *
   * @param string $machine_name
   *   The vocabulary machine name.
   *
   * @return object
   *   An access scheme.
   */
  public function createTaxonomyTermScheme($machine_name) {
    $vocabulary = new stdClass();
    $vocabulary->name = $this
      ->randomName();
    $vocabulary->machine_name = $machine_name;
    $vocabulary->hierarchy = 0;
    taxonomy_vocabulary_save($vocabulary);
    for ($i = 0; $i < 3; $i++) {
      $term = new stdClass();
      $term->vid = $vocabulary->vid;
      $term->name = $this
        ->randomName();
      taxonomy_term_save($term);
    }
    $scheme = $this
      ->createScheme('taxonomy_term', array(
      'vocabulary' => $vocabulary->machine_name,
    ));
    return access_scheme_load($scheme->sid);
  }

  /**
   * Creates and returns a new access grant for the given scheme.
   *
   * @param object $scheme
   *   An access scheme.
   * @param object $role
   *   (optional) A user role. If omitted, uses the first available scheme role.
   *   If no role is available, creates one using drupalCreateRole().
   * @param object $account
   *   (optional) A user account. If omitted, uses drupalCreateUser() and adds
   *   the user to the role.
   *
   * @return object
   *   An access grant.
   */
  public function createGrant($scheme, $role = NULL, $account = NULL) {

    // Make sure we have a user role to assign.
    if (!isset($role)) {

      // Get the list of roles enabled for this scheme.
      $scheme_roles = variable_get('access_scheme_roles_' . $scheme->machine_name, array());

      // If the scheme has no enabled roles, create one for it.
      if (empty($scheme_roles)) {
        $rid = $this
          ->drupalCreateRole(array(
          'access content',
        ));
        $role = user_role_load($rid);
        $scheme_roles = array(
          $role->rid => $role->name,
        );
        variable_set('access_scheme_roles_' . $scheme->machine_name, $scheme_roles);
      }
      else {
        $rid = key($scheme_roles);
        $role = user_role_load($rid);
      }
    }

    // Make sure we have a user to assign.
    if (!isset($account)) {

      // Create an account and add it to the role.
      $account = $this
        ->drupalCreateUser(array(
        'access content',
      ));
      $account->original = clone $account;
      $user_roles = $account->roles + array(
        $role->rid => $role->name,
      );
      user_save($account, array(
        'roles' => $user_roles,
      ));
    }

    // Create an access grant.
    $grant = entity_get_controller('access_grant')
      ->create(array(
      'scheme' => $scheme->machine_name,
      'rid' => $role->rid,
      'uid' => $account->uid,
    ));
    $this
      ->assertNull($grant->gid);

    // Save the grant and confirm that values come back correct.
    $this
      ->assertIdentical(access_grant_save($grant), SAVED_NEW, 'New grant saved.');
    $this
      ->assertNotNull($grant->gid);
    $this
      ->assertIdentical($grant->scheme, $scheme->machine_name);
    $this
      ->assertIdentical($grant->rid, $role->rid);
    $this
      ->assertIdentical($grant->uid, $account->uid);
    return $grant;
  }

}

/**
 * Tests the access scheme interface.
 */
class AccessSchemeInterfaceTest extends AccessWebTestCase {

  /**
   * A user with administrative access.
   *
   * @var object
   */
  protected $adminUser;

  /**
   * The role ID of an ACK-enabled role.
   *
   * @var int
   */
  protected $ackRoleRid;

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access scheme interface',
      'description' => 'Tests the access scheme admin interface.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {

    // Enable the access control kit module.
    parent::setUp(array(
      'access',
    ));

    // Create and log in our admin user.
    $this->adminUser = $this
      ->drupalCreateUser(array(
      'administer access schemes',
    ));
    $this
      ->drupalLogin($this->adminUser);

    // Create a user role for use in the access scheme.
    $this->ackRoleRid = $this
      ->drupalCreateRole(array(
      'access content',
    ));
  }

  /**
   * Create and edit an access scheme via the user interface.
   */
  public function testSchemeInterface() {

    // Visit the scheme admin overview page.
    $this
      ->drupalGet('admin/structure/access');

    // Create a new scheme through the admin form.
    $this
      ->clickLink(t('Add access scheme'));
    $this
      ->clickLink(t('Boolean'));
    $this
      ->assertText(t('Add access scheme: @type', array(
      '@type' => t('Boolean'),
    )));
    $edit = array();
    $machine_name = drupal_strtolower($this
      ->randomName());
    $edit['machine_name'] = $machine_name;
    $edit['name'] = $this
      ->randomName();
    $edit['description'] = $this
      ->randomName();
    $edit['roles[' . $this->ackRoleRid . ']'] = TRUE;
    $this
      ->drupalPost(NULL, $edit, t('Save access scheme and continue'));
    $this
      ->assertRaw(t('Added access scheme %name.', array(
      '%name' => $edit['name'],
    )), 'Scheme created successfully.');
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->assertText(t('@name (Machine name: @machine_name)', array(
      '@name' => $edit['name'],
      '@machine_name' => $machine_name,
    )), 'Scheme found in the scheme admin overview listing.');

    // Confirm that all submitted values were saved.
    $this
      ->clickLink(t('edit'));
    $this
      ->assertFieldByName('name', $edit['name'], 'The name field is correctly set to ' . $edit['name']);
    $this
      ->assertFieldByName('machine_name', $edit['machine_name'], 'The machine_name field is correctly set to ' . $edit['machine_name']);
    $machine_name_disabled = $this
      ->xpath('//input[@id=:id and @disabled="disabled"]', array(
      ':id' => 'edit-machine-name',
    ));
    $this
      ->assertTrue($machine_name_disabled, 'The machine name cannot be changed.');
    $this
      ->assertFieldByName('description', $edit['description'], 'The description field is correctly set to ' . $edit['description']);
    $this
      ->assertFieldChecked('edit-roles-' . $this->ackRoleRid, 'The role is correctly selected.');
    $this
      ->assertText(t('No access-controllable objects available.'), 'Informs that no access-controllable object types have been defined.');

    // Edit the scheme.
    $edit = array();
    $edit['name'] = $this
      ->randomName();
    $this
      ->drupalPost(NULL, $edit, t('Save access scheme'));
    $this
      ->assertRaw(t('Updated access scheme %name.', array(
      '%name' => $edit['name'],
    )), 'Scheme updated successfully.');
    $this
      ->assertText(t('@name (Machine name: @machine_name)', array(
      '@name' => $edit['name'],
      '@machine_name' => $machine_name,
    )), 'Updated scheme found in the scheme admin overview listing.');

    // Check integration with the Field UI.
    $this
      ->clickLink(t('manage fields'));
    $this
      ->assertText(t('Manage fields'));
    $this
      ->assertText(t('Access control kit user reference'));
    $this
      ->assertText(t('Access control kit role reference'));
    $this
      ->assertText('ack_' . $machine_name);
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->clickLink(t('manage display'));
    $this
      ->assertText(t('Manage display'));
    $this
      ->assertText(t('User'));
    $this
      ->assertText(t('Role'));

    // Try to submit a new scheme with a duplicate human-readable name.
    $edit['machine_name'] = drupal_strtolower($this
      ->randomName());
    $this
      ->drupalPost('admin/structure/access/add/boolean', $edit, t('Save access scheme and continue'));
    $this
      ->assertRaw(t('The name %name is already in use.', array(
      '%name' => $edit['name'],
    )));

    // Try to submit a new scheme with a duplicate machine name.
    $edit['name'] = $this
      ->randomName();
    $edit['machine_name'] = $machine_name;
    $this
      ->drupalPost('admin/structure/access/add/boolean', $edit, t('Save access scheme and continue'));
    $this
      ->assertText(t('The machine-readable name is already in use. It must be unique.'));

    // Try to submit an invalid machine name.
    $edit['machine_name'] = '!&^%';
    $this
      ->drupalPost('admin/structure/access/add/boolean', $edit, t('Save access scheme and continue'));
    $this
      ->assertText(t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));

    // Try to submit some disallowed machine names.
    foreach (array(
      'add',
      '0',
    ) as $bad_name) {
      $edit['machine_name'] = $bad_name;
      $this
        ->drupalPost('admin/structure/access/add/boolean', $edit, t('Save access scheme and continue'));
      $this
        ->assertText(t('Invalid machine-readable name. Enter a name other than @invalid.', array(
        '@invalid' => $bad_name,
      )));
    }

    // Ensure that scheme names and descriptions are escaped properly.
    $edit = array();
    $description = '<strong>' . $this
      ->randomName() . '</strong>';
    $edit['machine_name'] = 'don_t_panic';
    $edit['name'] = 'Don\'t Panic & Carry a Towel';
    $edit['description'] = '<script>' . $description . '</script>';
    $this
      ->drupalPost('admin/structure/access/add/boolean', $edit, t('Save access scheme and continue'));

    // Check that the name isn't double-filtered when used as the page title.
    $site_name = variable_get('site_name', 'Drupal');
    $this
      ->assertTitle(t("Don't Panic & Carry a Towel | @site-name", array(
      '@site-name' => $site_name,
    )));
    $this
      ->assertNoTitle(t('Don&#039;t Panic &amp; Carry a Towel | @site-name', array(
      '@site-name' => $site_name,
    )));

    // Check that the name is sanitized in overview.
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->assertRaw('Don&#039;t Panic &amp; Carry a Towel');
    $this
      ->assertNoRaw($edit['name']);

    // Check that the description is sanitized in overview.
    $this
      ->assertRaw($description);
    $this
      ->assertNoRaw($edit['description']);
  }

  /**
   * Confirm that realm fields are hidden from the scheme UI and Field UI.
   */
  public function testSchemeRealmFieldsHidden() {

    // Confirm that we're starting clean.
    $this
      ->drupalGet('admin/structure/access/add');
    $this
      ->assertNoText(t('List (integer) field'), 'The list (integer) scheme type is not available.');

    // Create a user account scheme, which uses a list_integer realm field.
    $edit = array();
    $machine_name = drupal_strtolower($this
      ->randomName());
    $edit['machine_name'] = $machine_name;
    $edit['name'] = $this
      ->randomName();
    $this
      ->drupalPost('admin/structure/access/add/user', $edit, t('Save access scheme and continue'));
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->assertText(t('@name (Machine name: @machine_name)', array(
      '@name' => $edit['name'],
      '@machine_name' => $machine_name,
    )), 'Scheme found in the scheme admin overview listing.');

    // Confirm that realm fields cannot be deleted through the Field UI.
    $this
      ->drupalGet('admin/structure/access/' . $machine_name . '/fields/ack_' . $machine_name . '/delete');
    $this
      ->assertText(t('This field is locked and cannot be deleted.'), 'Realm fields are locked.');

    // Confirm that the presence of a scheme with a list_integer realm field
    // does not make the list_integer scheme type available.
    $this
      ->drupalGet('admin/structure/access/add');
    $this
      ->assertNoText(t('List (integer) field'), 'Realm fields are ignored when determining whether a list-based scheme can be created.');

    // Create a valid list_integer field and confirm that the scheme type does
    // become available.
    $field = array(
      'field_name' => 'field_valid_list',
      'type' => 'list_integer',
    );
    field_create_field($field);
    $this
      ->drupalGet('admin/structure/access/add');
    $this
      ->assertText(t('List (integer) field'), 'List-based schemes become available when usable fields are present.');

    // Confirm that the scheme's realm field is hidden from the scheme UI.
    $this
      ->clickLink(t('List (integer) field'));
    $this
      ->assertNoRaw('ack_' . $machine_name, 'Realm fields are hidden when selecting the base field for a list-based scheme.');
    $this
      ->assertRaw($field['field_name'], 'Usable list fields are selectable on a list-based scheme.');

    // Confirm that realm fields are hidden from the Field UI.
    $scheme = $this
      ->createScheme();
    $this
      ->drupalGet('admin/structure/access/' . $scheme->machine_name . '/fields');
    $this
      ->assertNoRaw('ack_' . $machine_name, 'Realm fields are hidden from the Field UI.');
  }

  /**
   * Test the scheme overview with no schemes.
   */
  public function testSchemeOverviewEmpty() {

    // Delete all schemes.
    $schemes = access_scheme_load_multiple(FALSE);
    foreach ($schemes as $sid => $scheme) {
      access_scheme_delete($sid);
    }

    // Confirm that no schemes remain in the database.
    $this
      ->assertFalse(access_scheme_load_multiple(FALSE), 'No access schemes found in the database.');

    // Check the default message for no schemes.
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->assertText(t('No access schemes available.'), 'No access schemes were found.');
  }

  /**
   * Delete an access scheme via the user interface.
   */
  public function testSchemeDelete() {

    // Create an access scheme.
    $scheme = $this
      ->createScheme();
    $scheme = access_scheme_load($scheme->sid, TRUE);
    $this
      ->assertTrue($scheme, 'Access scheme found in the database.');

    // Create an access grant in the scheme.
    $grant = $this
      ->createGrant($scheme);
    $grant = access_grant_load($grant->gid, TRUE);
    $this
      ->assertTrue($grant, 'Access grant found in the database.');

    // Check deleting from the overview page.
    $this
      ->drupalGet('admin/structure/access');
    $this
      ->clickLink(t('delete'));
    $this
      ->assertRaw(t('Are you sure you want to delete the access scheme %name?', array(
      '%name' => $scheme->name,
    )), '[confirm deletion] Asks for confirmation.');

    // Delete the scheme.
    $edit = array();
    $this
      ->drupalPost('admin/structure/access/' . $scheme->machine_name, $edit, t('Delete access scheme'));
    $this
      ->assertRaw(t('Are you sure you want to delete the access scheme %name?', array(
      '%name' => $scheme->name,
    )), '[confirm deletion] Asks for confirmation.');
    $this
      ->assertRaw(t('All access grants within the scheme will also be deleted. %scheme currently contains 1 access grant on your site. If you remove this scheme, the user may not be able to exercise the permissions assigned by that grant.', array(
      '%scheme' => $scheme->name,
    )), '[confirm deletion] Informs that all grants will be deleted.');
    $this
      ->assertText(t('This action cannot be undone.'), '[confirm deletion] Informs that deletion is permanent.');

    // Confirm deletion.
    $this
      ->drupalPost(NULL, NULL, t('Delete'));
    $this
      ->assertRaw(t('Deleted access scheme %name.', array(
      '%name' => $scheme->name,
    )), 'Access scheme deleted.');
    $this
      ->assertFalse(access_grant_load($grant->gid, TRUE), 'Access grant is not found in the database.');
    $this
      ->assertFalse(access_scheme_load($scheme->sid, TRUE), 'Access scheme is not found in the database.');
  }

}

/**
 * Tests for access scheme functions.
 */
class AccessSchemeFunctionTest extends AccessWebTestCase {

  /**
   * An array of access schemes.
   *
   * @var array
   */
  protected $schemes;

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access schemes',
      'description' => 'Tests loading, saving and deleting access schemes.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {

    // Enable the access control kit module.
    parent::setUp(array(
      'access',
    ));

    // Create two schemes for testing.
    $this->schemes = array();
    $this->schemes[] = $this
      ->createScheme();
    $this->schemes[] = $this
      ->createScheme();

    // Attach a handler to each.
    foreach ($this->schemes as $scheme) {
      entity_get_controller('access_scheme')
        ->attachHandler($scheme, 'node', 'ACKEntityField');
      access_scheme_save($scheme);
    }
  }

  /**
   * Test basic create, read, update, and delete functions.
   */
  public function testSchemeCRUD() {
    $loaded_schemes = access_scheme_load_multiple(FALSE, TRUE);
    $this
      ->assertTrue(count($loaded_schemes), 'Access schemes exist in the database.');
    $i = 0;
    $count = count($this->schemes);
    $names = access_scheme_names();
    $bundles = field_info_bundles('access_grant');
    foreach ($this->schemes as $scheme) {
      $i++;

      // Find the test scheme in the name list.
      $this
        ->assertTrue(isset($names[$scheme->machine_name]) && $names[$scheme->machine_name] == $scheme->name, "[scheme {$i} of {$count}] Found the scheme in the name list.");

      // Find the test scheme in the bundle list.
      $this
        ->assertTrue(isset($bundles[$scheme->machine_name]), "[scheme {$i} of {$count}] Found the scheme in the bundle list.");

      // Confirm that the loaded scheme matches expected values.
      $loaded_scheme = $loaded_schemes[$scheme->sid];
      $this
        ->assertEqual($scheme->sid, $loaded_scheme->sid, "[scheme {$i} of {$count}] ID is OK.");
      $this
        ->assertEqual($scheme->machine_name, $loaded_scheme->machine_name, "[scheme {$i} of {$count}] Machine name is OK.");
      $this
        ->assertEqual($scheme->name, $loaded_scheme->name, "[scheme {$i} of {$count}] Human-readable name is OK.");
      $this
        ->assertEqual($scheme->type, $loaded_scheme->type, "[scheme {$i} of {$count}] Scheme type is OK.");
      $this
        ->assertEqual($scheme->description, $loaded_scheme->description, "[scheme {$i} of {$count}] Description is OK.");

      // Confirm that the scheme's access realm field exists.
      $this
        ->assertTrue(isset($loaded_scheme->realm_field) && isset($loaded_scheme->realm_field['field_name']) && $loaded_scheme->realm_field['field_name'] == $scheme->realm_field_name, "[scheme {$i} of {$count}] Realm field exists.");

      // Confirm that scheme type and realm information were attached.
      $this
        ->assertTrue(isset($loaded_scheme->info) && isset($loaded_scheme->info['type']) && $loaded_scheme->info['type'] == 'boolean', "[scheme {$i} of {$count}] Scheme type info was attached.");
      $this
        ->assertTrue(isset($loaded_scheme->realms) && is_array($loaded_scheme->realms) && $loaded_scheme->realms[0] == t('False') && $loaded_scheme->realms[1] == t('True'), "[scheme {$i} of {$count}] Realm values were attached.");

      // Confirm that role information was attached.
      $this
        ->assertTrue(isset($loaded_scheme->roles) && is_array($loaded_scheme->roles), "[scheme {$i} of {$count}] Roles were attached.");

      // Confirm that handlers were attached.
      $this
        ->assertTrue(isset($loaded_scheme->handlers) && isset($loaded_scheme->handlers['node']), "[scheme {$i} of {$count}] Handlers were attached.");
      $this
        ->assertEqual(get_class($loaded_scheme->handlers['node']), 'ACKEntityField');
    }

    // Attempt to load a scheme that doesn't exist.
    $sid = count($loaded_schemes) + 1;
    $scheme = access_scheme_load($sid);
    $this
      ->assertFalse($scheme, 'Invalid scheme ID does not load.');

    // Create a new scheme, which should end up with $sid as its ID.
    $this
      ->createScheme();

    // Make sure that the previously invalid ID now loads properly.
    $scheme = access_scheme_load($sid);
    $this
      ->assertTrue(!empty($scheme) && is_object($scheme), 'Newly created access scheme loads properly.');
    $this
      ->assertEqual($scheme->sid, $sid, 'Loaded scheme ID is the same as the previously invalid ID.');

    // Confirm that schemes can be loaded by machine name.
    $loaded_scheme = access_scheme_machine_name_load(str_replace('_', '-', $scheme->machine_name));
    $this
      ->assertEqual($scheme->sid, $loaded_scheme->sid, 'Scheme loads by machine name.');

    // Confirm that the scheme can be updated.
    $new_name = $this
      ->randomName();
    $loaded_scheme->name = $new_name;
    $this
      ->assertEqual(access_scheme_save($loaded_scheme), SAVED_UPDATED, 'Scheme updated.');
    $loaded_scheme = access_scheme_load($scheme->sid, TRUE);
    $this
      ->assertEqual($loaded_scheme->name, $new_name);

    // Confirm that the scheme can be deleted.
    entity_get_controller('access_scheme')
      ->attachHandler($loaded_scheme, 'node', 'ACKEntityField');
    access_scheme_save($loaded_scheme);
    $handlers = db_query('SELECT * FROM {access_handler} WHERE scheme = :scheme', array(
      ':scheme' => $loaded_scheme->machine_name,
    ));
    $handler = $handlers
      ->fetch();
    $this
      ->assertFalse(empty($handler), 'Handlers were attached.');
    $this
      ->assertEqual(access_scheme_delete($loaded_scheme->sid), SAVED_DELETED, 'Scheme deleted.');
    $this
      ->assertFalse(access_scheme_load($loaded_scheme->sid));
    $handlers = db_query('SELECT * FROM {access_handler} WHERE scheme = :scheme', array(
      ':scheme' => $loaded_scheme->machine_name,
    ));
    $this
      ->assertFalse($handlers
      ->fetch(), 'Handlers were detached.');
    $this
      ->assertNull(variable_get('access_scheme_roles_' . $loaded_scheme->machine_name), 'Role configuration was deleted.');
    $bundles = field_info_bundles('access_grant');
    $this
      ->assertFalse(isset($bundles[$loaded_scheme->machine_name]), 'Bundle was deleted.');

    // Confirm that the scheme can be recreated.
    unset($loaded_scheme->sid);
    $this
      ->assertEqual(access_scheme_save($loaded_scheme), SAVED_NEW, 'Scheme created.');
    $this
      ->assertNotNull($loaded_scheme->sid);
  }

  /**
   * Test deleting an access scheme that contains grants.
   */
  public function testSchemeDeleteWithGrants() {
    $this
      ->assertEqual(2, count($this->schemes));

    // Create five access grants each for the two test schemes.
    foreach ($this->schemes as $scheme) {

      // Assert that there are no existing grants for the access scheme.
      $has_rows = (bool) db_query_range('SELECT 1 FROM {access_grant} WHERE scheme = :scheme', 0, 1, array(
        ':scheme' => $scheme->machine_name,
      ))
        ->fetchField();
      $this
        ->assertFalse($has_rows);
      for ($i = 0; $i < 5; $i++) {
        $this
          ->createGrant($scheme);
      }

      // Assert that there are now five grants for the access scheme.
      $this
        ->assertEqual(5, db_query('SELECT COUNT(*) FROM {access_grant} WHERE scheme = :scheme', array(
        ':scheme' => $scheme->machine_name,
      ))
        ->fetchField());
    }

    // Delete the first scheme.
    $scheme = reset($this->schemes);
    access_scheme_delete($scheme->sid);

    // Assert that there are no grants left for the deleted scheme.
    $has_rows = (bool) db_query_range('SELECT 1 FROM {access_grant} WHERE scheme = :scheme', 0, 1, array(
      ':scheme' => $scheme->machine_name,
    ))
      ->fetchField();
    $this
      ->assertFalse($has_rows);

    // Assert that there are still five grants for the remaining scheme.
    $scheme = array_pop($this->schemes);
    $this
      ->assertEqual(5, db_query('SELECT COUNT(*) FROM {access_grant} WHERE scheme = :scheme', array(
      ':scheme' => $scheme->machine_name,
    ))
      ->fetchField());
  }

  /**
   * Ensure that the access scheme static reset works correctly.
   */
  public function testSchemeStaticReset() {
    $original_scheme = access_scheme_load($this->schemes[0]->sid);
    $this
      ->assertTrue(is_object($original_scheme) && $original_scheme->name == $this->schemes[0]->name, 'Scheme loaded successfully.');

    // Change the name and description.
    $scheme = $original_scheme;
    $scheme->name = $this
      ->randomName();
    $scheme->description = $this
      ->randomName();
    access_scheme_save($scheme);

    // Load the scheme.
    $new_scheme = access_scheme_load($original_scheme->sid);
    $this
      ->assertEqual($new_scheme->name, $scheme->name);
    $this
      ->assertEqual($new_scheme->description, $scheme->description);

    // Delete the scheme.
    access_scheme_delete($this->schemes[0]->sid);
    $schemes = access_scheme_load_multiple(FALSE);
    $this
      ->assertTrue(!isset($schemes[$this->schemes[0]->sid]), 'The scheme was deleted.');
  }

}

/**
 * Tests the access grant interface.
 */
class AccessGrantInterfaceTest extends AccessWebTestCase {

  /**
   * A user with administrative access.
   *
   * @var object
   */
  protected $adminUser;

  /**
   * An ACK-enabled role.
   *
   * @var object
   */
  protected $ackRole;

  /**
   * A user to whom access will be granted during the test.
   *
   * @var object
   */
  protected $ackUser;

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access grant interface',
      'description' => 'Tests the access grant admin interface.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {

    // Enable the access control kit module.
    parent::setUp(array(
      'access',
    ));

    // Create and log in our admin user.
    $this->adminUser = $this
      ->drupalCreateUser(array(
      'administer access grants',
    ));
    $this
      ->drupalLogin($this->adminUser);

    // Create a user role for use in an access scheme.
    $rid = $this
      ->drupalCreateRole(array(
      'access content',
    ));
    $this->ackRole = user_role_load($rid);

    // Create a user account for use in access grants.
    $this->ackUser = $this
      ->drupalCreateUser(array(
      'access content',
    ));
  }

  /**
   * Create and edit an access grant via the user interface.
   */
  public function testGrantInterface() {

    // Visit the grant admin overview page.
    $this
      ->drupalGet('admin/access');

    // Check that the add grant page responds to the presence of access schemes.
    $this
      ->clickLink(t('Add access grant'));
    $this
      ->assertText(t('You have not created any access schemes yet.'), 'Informs that an access scheme must exist before grants can be created.');
    $scheme = $this
      ->createScheme();
    $this
      ->drupalGet('admin/access/add');
    $this
      ->assertNoLink(check_plain($scheme->name), 'The "add grant" list page is bypassed when only one scheme exists.');
    $this
      ->assertText(t('Grant access to @scheme', array(
      '@scheme' => $scheme->name,
    )));
    $deleted_scheme = $this
      ->createScheme();
    $this
      ->drupalGet('admin/access/add');
    $this
      ->assertLink(check_plain($scheme->name), 0, 'The "add grant" list page is shown when more than one scheme exists.');
    $this
      ->clickLink(check_plain($deleted_scheme->name));
    $this
      ->assertText(t('Grant access to @scheme', array(
      '@scheme' => $deleted_scheme->name,
    )));
    access_scheme_delete($deleted_scheme->sid);
    $this
      ->drupalGet('admin/access/add');
    $this
      ->assertNoLink(check_plain($deleted_scheme->name), 'Deleted schemes are not shown when adding a grant.');

    // Set the scheme to use the test role.
    $this
      ->assertText(t('No roles are available for new access grants.'));
    $scheme = access_scheme_load($scheme->sid);
    $scheme->roles = array(
      $this->ackRole->rid => $this->ackRole->name,
    );
    variable_set('access_scheme_roles_' . $scheme->machine_name, $scheme->roles);
    $this
      ->drupalGet('admin/access/add');
    $this
      ->assertNoText(t('No roles are available for new access grants.'));

    // Attempt to create an access grant through the admin form for a role that
    // the test user does not yet have.
    $this
      ->assertText(t('Grant access to @scheme', array(
      '@scheme' => $scheme->name,
    )));
    $edit = array();
    $edit['user'] = $this->ackUser->name;
    $edit['role'] = $this->ackRole->rid;
    $field_name = $scheme->realm_field['field_name'];
    $edit[$field_name . '[und][1]'] = TRUE;
    $this
      ->drupalPost(NULL, $edit, t('Save'));
    $this
      ->assertText(t('@user is not a member of the @role role.', array(
      '@user' => $this->ackUser->name,
      '@role' => $this->ackRole->name,
    )), 'Cannot add users to roles without access to administer users.');
    $this->ackUser = user_load($this->ackUser->uid, TRUE);
    $this
      ->assertFalse(isset($this->ackUser->roles[$this->ackRole->rid]), 'User is not a member of the role.');

    // Give the admin user access to grant new roles and try again.
    $this->adminUser->original = clone $this->adminUser;
    $rid = $this
      ->drupalCreateRole(array(
      'administer users',
    ));
    $role = user_role_load($rid);
    $roles = $this->adminUser->roles + array(
      $role->rid => $role->name,
    );
    user_save($this->adminUser, array(
      'roles' => $roles,
    ));
    $this
      ->drupalGet('admin/access/add');
    $this
      ->assertText(t('The user will be added to this role, if not already a member.'), 'User administrators are notified that they can add new roles.');
    $this
      ->drupalPost(NULL, $edit, t('Save'));
    $this
      ->assertRaw(t("Added %scheme for %user's access as %role.", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Grant created successfully.');
    $this->ackUser = user_load($this->ackUser->uid, TRUE);
    $this
      ->assertTrue(isset($this->ackUser->roles[$this->ackRole->rid]), 'User has become a member of the role.');
    $this
      ->drupalGet('admin/access');

    // Check that all grant fields are correctly shown in the overview.
    $this
      ->assertText(check_plain($this->ackUser->name), 'Grant found in the grant admin overview listing.');
    $this
      ->assertText(check_plain($this->ackRole->name));
    $this
      ->assertText(t('True'));
    $this
      ->assertText(check_plain($scheme->name));

    // Test operations links.
    $this
      ->clickLink(t('edit'));
    $this
      ->assertRaw(t('<em>Edit @scheme for</em> @grant', array(
      '@scheme' => $scheme->name,
      '@grant' => t("@user's access as @role", array(
        '@user' => $this->ackUser->name,
        '@role' => $this->ackRole->name,
      )),
    )));
    $this
      ->clickLink(t('Cancel'));
    $this
      ->clickLink(t('delete'));
    $this
      ->assertText(t("Are you sure you want to revoke all @scheme for @user's access as @role?", array(
      '@scheme' => $scheme->name,
      '@user' => $this->ackUser->name,
      '@role' => $this->ackRole->name,
    )));
    $this
      ->clickLink(t('Cancel'));

    // View the grant.
    $this
      ->clickLink(t('view'));
    $this
      ->assertRaw(t("@user&#039;s access as @role", array(
      '@user' => $this->ackUser->name,
      '@role' => $this->ackRole->name,
    )), 'Grant can be rendered.');
    $this
      ->assertText(check_plain($this->ackUser->name));
    $this
      ->assertText(check_plain($this->ackRole->name));
    $this
      ->assertText(t('True'));
    $this
      ->assertText(check_plain($scheme->name));

    // Edit the grant.
    $this
      ->clickLink(t('Edit'));
    $this
      ->assertRaw('<label for="edit-user-display">User </label>', 'User field is display only.');
    $this
      ->assertRaw('<label for="edit-role-display">Role </label>', 'Role field is display only.');
    $edit = array();
    $edit[$field_name . '[und][0]'] = TRUE;
    $edit[$field_name . '[und][1]'] = FALSE;
    $this
      ->drupalPost(NULL, $edit, t('Save'));
    $this
      ->assertRaw(t("Updated %scheme for %user's access as %role.", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Grant updated successfully.');
    $this
      ->assertFieldChecked('edit-' . strtr($field_name, '_', '-') . '-und-0', 'New realm assigned.');
    $this
      ->assertNoFieldChecked('edit-' . strtr($field_name, '_', '-') . '-und-1', 'Old realm removed.');
    $this
      ->drupalGet('admin/access');
    $this
      ->assertText(t('False'), 'Updated grant found in the grant admin overview listing.');
    $this
      ->assertNoText(t('True'));

    // Try to submit a duplicate grant for this user-role-scheme combination.
    $this
      ->drupalGet('admin/access/add');
    $edit = array();
    $edit['user'] = $this->ackUser->name;
    $edit['role'] = $this->ackRole->rid;
    $field_name = $scheme->realm_field['field_name'];
    $edit[$field_name . '[und][1]'] = TRUE;
    $this
      ->drupalPost(NULL, $edit, t('Save'));
    $this
      ->assertRaw(t('%user has already been granted access as %role in @scheme.', array(
      '@scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Duplicate access grants are not permitted.');

    // Attempt to create a grant for an unknown user.
    $edit['user'] = $this
      ->randomName();
    $this
      ->drupalPost(NULL, $edit, t('Save'));
    $this
      ->assertRaw(t('The username %name does not exist.', array(
      '%name' => $edit['user'],
    )), 'Invalid user references are not permitted.');

    // Create a user with an unsafe username.
    $new_user = $this
      ->drupalCreateUser();
    $new_user->name = 'Don\'t';
    user_save($new_user);
    $new_user = user_load($new_user->uid, TRUE);

    // Create a role with an unsafe name.
    $new_rid = $this
      ->drupalCreateRole(array(
      'access content',
    ));
    $new_role = user_role_load($new_rid);
    $new_role->name = 'Panic & Carry';
    user_role_save($new_role);
    $new_role = user_role_load($new_role->rid);

    // Create a scheme with an unsafe name.
    $new_scheme = $this
      ->createScheme();
    $new_scheme = access_scheme_load($new_scheme->sid);
    $new_scheme->roles = array(
      $new_role->rid => $new_role->name,
    );
    variable_set('access_scheme_roles_' . $new_scheme->machine_name, $new_scheme->roles);
    $new_scheme_name = 'a Towel';
    $new_scheme->name = '<script>' . $new_scheme_name . '</script>';
    access_scheme_save($new_scheme);
    $new_scheme = access_scheme_load($new_scheme->sid, TRUE);

    // Create a grant with the unsafe values.
    $new_grant = $this
      ->createGrant($new_scheme, $new_role, $new_user);
    $field_name = $new_scheme->realm_field['field_name'];
    $new_grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 1,
        ),
      ),
    );
    access_grant_save($new_grant);

    // Ensure that grant values are escaped properly in displays.
    for ($i = 0; $i < 2; $i++) {
      switch ($i) {
        case 0:

          // Overview.
          $this
            ->drupalGet('admin/access');

          // Check the scheme name.
          $this
            ->assertRaw('&lt;script&gt;a Towel&lt;/script&gt;');
          $this
            ->assertNoRaw($new_scheme->name);
          break;
        case 1:

          // Rendered view.
          $this
            ->drupalGet('admin/access/grant/' . $new_grant->gid);

          // Check that the name isn't double-filtered in the page title.
          $site_name = variable_get('site_name', 'Drupal');
          $this
            ->assertTitle(t("Don't's access as Panic & Carry | @site-name", array(
            '@site-name' => $site_name,
          )));
          $this
            ->assertNoTitle(t("Don&#039;t's access as Panic &amp; Carry | @site-name", array(
            '@site-name' => $site_name,
          )));
          break;
      }

      // Checks common to all displays.
      $this
        ->assertRaw('Don&#039;t');
      $this
        ->assertNoRaw($new_user->name);
      $this
        ->assertRaw('Panic &amp; Carry');
      $this
        ->assertNoRaw($new_role->name);
    }
  }

  /**
   * Test the grant overview with no grants.
   */
  public function testGrantOverviewEmpty() {

    // Delete all grants.
    $grants = access_grant_load_multiple(FALSE);
    foreach ($grants as $gid => $grant) {
      access_grant_delete($gid);
    }

    // Confirm that no grants remain in the database.
    $this
      ->assertFalse(access_grant_load_multiple(FALSE), 'No access grants found in the database.');

    // Check the default message for no grants.
    $this
      ->drupalGet('admin/access');
    $this
      ->assertText(t('No access grants available.'), 'No access grants were found.');
  }

  /**
   * Test the grant overview filters.
   */
  public function testGrantOverviewFilters() {

    // Create two schemes for testing the scheme filter.
    $schemes = array(
      'good' => $this
        ->createScheme(),
      'bad' => $this
        ->createScheme(),
    );

    // Create two roles for testing the role filter.
    $rid = $this
      ->drupalCreateRole(array(
      'access content',
    ));
    $roles = array(
      'good' => $this->ackRole,
      'bad' => user_role_load($rid),
    );

    // Enable both roles for both schemes.
    $scheme_roles = array(
      $roles['good']->rid => $roles['good']->name,
      $roles['bad']->rid => $roles['bad']->name,
    );
    variable_set('access_scheme_roles_' . $schemes['good']->machine_name, $scheme_roles);
    variable_set('access_scheme_roles_' . $schemes['bad']->machine_name, $scheme_roles);

    // Create two users for testing the user filter.
    $users = array(
      'good' => $this->ackUser,
      'bad' => $this
        ->drupalCreateUser(array(
        'access content',
      )),
    );

    // Create two grants for filtering.
    $good = $this
      ->createGrant($schemes['good'], $roles['good'], $users['good']);
    $bad = $this
      ->createGrant($schemes['bad'], $roles['bad'], $users['bad']);
    $this
      ->drupalGet('admin/access');
    foreach (array(
      'role',
      'scheme',
      'username',
    ) as $filter) {

      // Make sure that both grants are displayed on the unfiltered display.
      $this
        ->assertLinkByHref('admin/access/grant/' . $good->gid, 0);
      $this
        ->assertLinkByHref('admin/access/grant/' . $bad->gid, 0);

      // Filter the display.
      $edit = array();
      $value = '';
      switch ($filter) {
        case 'role':
          $edit = array(
            'rid' => $roles['good']->rid,
          );
          $value = $roles['good']->name;
          break;
        case 'scheme':
          $edit = array(
            'scheme' => $schemes['good']->machine_name,
          );
          $value = $schemes['good']->name;
          break;
        case 'username':
          $edit = array(
            'username' => $users['good']->name,
          );
          $value = $users['good']->name;
          break;
      }
      $this
        ->drupalPost(NULL, $edit, 'Filter');
      $this
        ->assertText(t('where @filter is @value', array(
        '@filter' => $filter,
        '@value' => $value,
      )), 'Applied filter for ' . $filter . '.');

      // Make sure that only the matching grant is displayed.
      $this
        ->assertLinkByHref('admin/access/grant/' . $good->gid, 0, 'Matching grants were displayed.');
      $this
        ->assertNoLinkByHref('admin/access/grant/' . $bad->gid, 'Non-matching grants were not displayed.');

      // Clear the filters.
      $this
        ->drupalPost(NULL, array(), 'Reset');
      $this
        ->assertNoText(t('where @filter is @value', array(
        '@filter' => $filter,
        '@value' => $value,
      )), 'Filter text was removed on reset.');
    }

    // Test multiple filters.
    $this
      ->drupalPost(NULL, array(
      'username' => $users['good']->name,
    ), 'Filter');
    $this
      ->drupalPost(NULL, array(
      'scheme' => $schemes['good']->machine_name,
    ), 'Refine');
    $this
      ->assertText(t('where @filter is @value', array(
      '@filter' => 'username',
      '@value' => $users['good']->name,
    )), 'Applied filter for username.');
    $this
      ->assertText(t('and where @filter is @value', array(
      '@filter' => 'scheme',
      '@value' => $schemes['good']->name,
    )), 'Applied filter for scheme.');
    $this
      ->assertLinkByHref('admin/access/grant/' . $good->gid, 0, 'Matching grants were displayed.');
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $bad->gid, 'Non-matching grants were not displayed.');
    $this
      ->drupalPost(NULL, array(), 'Undo');
    $this
      ->assertText(t('where @filter is @value', array(
      '@filter' => 'username',
      '@value' => $users['good']->name,
    )), 'Retained filter for username.');
    $this
      ->assertNoText(t('and where @filter is @value', array(
      '@filter' => 'scheme',
      '@value' => $schemes['good']->name,
    )), 'Removed filter for scheme.');
    $this
      ->assertLinkByHref('admin/access/grant/' . $good->gid, 0, 'Matching grants were displayed.');
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $bad->gid, 'Non-matching grants were not displayed.');
    $this
      ->drupalPost(NULL, array(
      'scheme' => $schemes['bad']->machine_name,
    ), 'Refine');
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $good->gid, 'Non-matching grants were not displayed.');
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $bad->gid, 'Non-matching grants were not displayed.');
    $this
      ->assertText(t('No access grants available.'));

    // Clear all filters.
    $this
      ->drupalPost(NULL, array(), 'Reset');
    $this
      ->assertLinkByHref('admin/access/grant/' . $good->gid, 0);
    $this
      ->assertLinkByHref('admin/access/grant/' . $bad->gid, 0);
  }

  /**
   * Delete an access grant via the user interface.
   */
  public function testGrantDelete() {

    // Create an access scheme that uses the test role.
    $scheme = $this
      ->createScheme();
    $scheme = access_scheme_load($scheme->sid, TRUE);
    $scheme->roles = array(
      $this->ackRole->rid => $this->ackRole->name,
    );
    variable_set('access_scheme_roles_' . $scheme->machine_name, $scheme->roles);

    // Add the test user to the test role.
    $this->ackUser->original = clone $this->ackUser;
    $roles = $this->ackUser->roles + array(
      $this->ackRole->rid => $this->ackRole->name,
    );
    user_save($this->ackUser, array(
      'roles' => $roles,
    ));
    $this->ackUser = user_load($this->ackUser->uid, TRUE);

    // Create an access grant.
    $grant = $this
      ->createGrant($scheme, $this->ackRole, $this->ackUser);
    $field_name = $scheme->realm_field['field_name'];
    $grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 1,
        ),
      ),
    );
    access_grant_save($grant);
    $grant = access_grant_load($grant->gid, TRUE);
    $this
      ->assertTrue($grant, 'Access grant found in the database.');

    // Check deleting from the overview page.
    $this
      ->drupalGet('admin/access');
    $this
      ->clickLink(t('delete'));
    $this
      ->assertRaw(t("Are you sure you want to revoke all %scheme for %user's access as %role?", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), '[confirm deletion] Asks for confirmation.');

    // Delete the grant without removing the user's role.
    $edit = array();
    $this
      ->drupalPost('admin/access/grant/' . $grant->gid . '/edit', $edit, t('Delete'));
    $this
      ->assertRaw(t("Are you sure you want to revoke all %scheme for %user's access as %role?", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), '[confirm deletion] Asks for confirmation.');
    $this
      ->assertNoRaw(t("Also revoke %user's membership in the %role role?", array(
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Prompt to revoke the role is not shown when user lacks user admin access.');

    // Give the admin user access to administer users and try again.
    $this->adminUser->original = clone $this->adminUser;
    $rid = $this
      ->drupalCreateRole(array(
      'administer users',
    ));
    $role = user_role_load($rid);
    $roles = $this->adminUser->roles + array(
      $role->rid => $role->name,
    );
    user_save($this->adminUser, array(
      'roles' => $roles,
    ));
    $this
      ->drupalGet('admin/access/grant/' . $grant->gid . '/delete');
    $this
      ->assertRaw(t("Also revoke %user's membership in the %role role?", array(
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Prompt to revoke the role is shown when user has user admin access.');
    $this
      ->assertText(t('This action cannot be undone.'), '[confirm deletion] Informs that deletion is permanent.');
    $this
      ->drupalPost(NULL, NULL, t('Delete'));

    // Confirm deletion.
    $this
      ->assertRaw(t("Deleted %scheme for %user's access as %role.", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Access grant deleted.');
    $this
      ->assertFalse(access_grant_load($grant->gid, TRUE), 'Access grant is not found in the database.');
    $this->ackUser = user_load($this->ackUser->uid, TRUE);
    $this
      ->assertTrue(isset($this->ackUser->roles[$this->ackRole->rid]), 'User is still a member of the role.');

    // Repeat with a new grant and the revoke role option checked.
    $grant = $this
      ->createGrant($scheme, $this->ackRole, $this->ackUser);
    $grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 1,
        ),
      ),
    );
    access_grant_save($grant);
    $grant = access_grant_load($grant->gid, TRUE);
    $this
      ->assertTrue($grant, 'Access grant found in the database.');
    $edit = array();
    $edit['revoke_role'] = TRUE;
    $this
      ->drupalPost('admin/access/grant/' . $grant->gid . '/delete', $edit, t('Delete'));

    // Confirm deletion and role removal.
    $this
      ->assertRaw(t("Deleted %scheme for %user's access as %role.", array(
      '%scheme' => $scheme->name,
      '%user' => $this->ackUser->name,
      '%role' => $this->ackRole->name,
    )), 'Access grant deleted.');
    $this
      ->assertFalse(access_grant_load($grant->gid, TRUE), 'Access grant is not found in the database.');
    $this->ackUser = user_load($this->ackUser->uid, TRUE);
    $this
      ->assertFalse(isset($this->ackUser->roles[$this->ackRole->rid]), 'User was removed from the role.');

    // Check multiple deletion.
    $grant_a = $this
      ->createGrant($scheme);
    $grant_b = $this
      ->createGrant($scheme);
    $edit = array();
    $this
      ->drupalPost('admin/access', $edit, t('Delete selected grants'));
    $this
      ->assertText(t('No items selected'));
    $key = 'grants[' . $grant_a->gid . ']';
    $edit[$key] = TRUE;
    $this
      ->drupalPost('admin/access', $edit, t('Delete selected grants'));
    $this
      ->assertText('Are you sure you want to delete this access grant?', '[confirm deletion] Asks for confirmation.');
    $this
      ->assertText(access_grant_label($grant_a));
    $this
      ->clickLink(t('Cancel'));
    $this
      ->assertLinkByHref('admin/access/grant/' . $grant_a->gid);
    $this
      ->assertLinkByHref('admin/access/grant/' . $grant_b->gid);
    $key = 'grants[' . $grant_b->gid . ']';
    $edit[$key] = TRUE;
    $this
      ->drupalPost('admin/access', $edit, t('Delete selected grants'));
    $this
      ->assertText('Are you sure you want to delete these access grants?', '[confirm deletion] Asks for confirmation.');
    $this
      ->assertText(access_grant_label($grant_a));
    $this
      ->assertText(access_grant_label($grant_b));
    $this
      ->assertText(t('This action cannot be undone.'), '[confirm deletion] Informs that deletion is permanent.');
    $this
      ->drupalPost(NULL, array(), t('Delete'));
    $this
      ->assertText('Deleted 2 access grants.');
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $grant_a->gid);
    $this
      ->assertNoLinkByHref('admin/access/grant/' . $grant_b->gid);
    $this
      ->assertFalse(access_grant_load($grant_a->gid, TRUE), 'Access grant is not found in the database.');
    $this
      ->assertFalse(access_grant_load($grant_b->gid, TRUE), 'Access grant is not found in the database.');
  }

}

/**
 * Tests for access grant functions.
 */
class AccessGrantFunctionTest extends AccessWebTestCase {

  /**
   * An access scheme.
   *
   * @var object
   */
  protected $scheme;

  /**
   * An array of access grants.
   *
   * @var array
   */
  protected $grants;

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access grants',
      'description' => 'Tests loading, saving and deleting access grants.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {

    // Enable the access control kit module.
    parent::setUp(array(
      'access',
    ));

    // Create the test scheme.
    $this->scheme = $this
      ->createScheme();
    $this->scheme = access_scheme_load($this->scheme->sid, TRUE);

    // Create two grants for testing.
    $this->grants = array();
    $this->grants[] = $this
      ->createGrant($this->scheme);
    $this->grants[] = $this
      ->createGrant($this->scheme);
  }

  /**
   * Test basic create, read, update, and delete functions.
   */
  public function testGrantCRUD() {
    $loaded_grants = access_grant_load_multiple(FALSE, TRUE);
    $this
      ->assertTrue(count($loaded_grants), 'Access grants exist in the database.');
    $i = 0;
    $count = count($this->grants);
    $field_name = $this->scheme->realm_field['field_name'];
    foreach ($this->grants as $grant) {
      $i++;

      // Confirm that the loaded grant matches expected values.
      $loaded_grant = $loaded_grants[$grant->gid];
      $this
        ->assertEqual($grant->gid, $loaded_grant->gid, "[grant {$i} of {$count}] ID is OK.");
      $this
        ->assertEqual($grant->scheme, $loaded_grant->scheme, "[grant {$i} of {$count}] Scheme is OK.");
      $this
        ->assertEqual($grant->uid, $loaded_grant->uid, "[grant {$i} of {$count}] User reference is OK.");
      $this
        ->assertEqual($grant->rid, $loaded_grant->rid, "[grant {$i} of {$count}] Role reference is OK.");

      // Confirm that the realm field was attached.
      $this
        ->assertTrue(isset($loaded_grant->{$field_name}), "[grant {$i} of {$count}] Realm field is attached.");
    }

    // Attempt to load a grant that doesn't exist.
    $gid = count($loaded_grants) + 1;
    $grant = access_grant_load($gid);
    $this
      ->assertFalse($grant, 'Invalid grant ID does not load.');

    // Create a new grant, which should end up with $gid as its ID.
    $this
      ->createGrant($this->scheme);

    // Make sure that the previously invalid ID now loads properly.
    $grant = access_grant_load($gid);
    $this
      ->assertTrue(!empty($grant) && is_object($grant), 'Newly created access grant loads properly.');
    $this
      ->assertEqual($grant->gid, $gid, 'Loaded grant ID is the same as the previously invalid ID.');

    // Confirm that the grant can be updated.
    if (!empty($grant) && is_object($grant)) {
      $grant->{$field_name} = array(
        'und' => array(
          array(
            'value' => 1,
          ),
        ),
      );
      $this
        ->assertEqual(access_grant_save($grant), SAVED_UPDATED, 'Grant updated.');
      $loaded_grant = access_grant_load($grant->gid, TRUE);
      $field = $loaded_grant->{$field_name};
      $this
        ->assertEqual($field['und'][0]['value'], 1);
      $realms = $loaded_grant->realms;
      $this
        ->assertFalse(isset($realms[0]));
      $this
        ->assertTrue(isset($realms[1]), 'Grant realms found.');

      // Confirm that the grant can be deleted.
      $this
        ->assertEqual(access_grant_delete($grant->gid), SAVED_DELETED, 'Grant deleted.');
      $this
        ->assertFalse(access_grant_load($grant->gid));

      // Confirm that the grant can be recreated.
      unset($grant->gid);
      $this
        ->assertEqual(access_grant_save($grant), SAVED_NEW, 'Grant created.');
    }

    // Test conditional loading.
    $scheme = $this
      ->createListScheme('integer');
    $field_name = $scheme->realm_field['field_name'];
    $grant = $this
      ->createGrant($scheme);
    $grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 7,
        ),
      ),
    );
    access_grant_save($grant);
    $grant = $this
      ->createGrant($scheme);
    $grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 31,
        ),
      ),
    );
    access_grant_save($grant);
    $conditions = array(
      // Load by scheme.
      'scheme' => array(
        'condition' => $scheme->machine_name,
        'count' => 2,
      ),
      // Load by user.
      'uid' => array(
        'condition' => $grant->uid,
        'count' => 1,
      ),
      // Load by role.
      'rid' => array(
        'condition' => $grant->rid,
        'count' => 2,
      ),
    );
    foreach ($conditions as $key => $tests) {
      $match = TRUE;
      $loaded_grants = access_grant_load_by_condition(array(
        $key => $tests['condition'],
      ));
      foreach ($loaded_grants as $loaded_grant) {
        $match = $match && $loaded_grant->{$key} == $tests['condition'];
      }
      $this
        ->assertTrue($match && count($loaded_grants) == $tests['count'], 'Loaded all grants for ' . $key . ' ' . $tests['condition']);
    }

    // Load by realms.
    $conditions = array(
      'scheme' => $scheme->machine_name,
      'realms' => array(
        31,
      ),
    );
    $loaded_grants = access_grant_load_by_condition($conditions);
    $loaded_grant = reset($loaded_grants);
    $this
      ->assertTrue(count($loaded_grants) == 1 && $loaded_grant->gid == $grant->gid, 'Loaded only one grant with matching realms.');
    $conditions['realms'] = array(
      1,
    );
    $loaded_grants = access_grant_load_by_condition($conditions);
    $this
      ->assertTrue(count($loaded_grants) == 0, 'Did not load grants with mismatching realms.');
  }

  /**
   * Test the access grant rendering functions.
   */
  public function testGrantRender() {
    $gids = array(
      $this->grants[0]->gid,
      $this->grants[1]->gid,
    );
    $loaded_grants = access_grant_load_multiple($gids);
    $render = access_grant_view_multiple($loaded_grants, 'full', 3);

    // Check weight and sorting.
    $this
      ->assertEqual($render['grants'][$gids[0]]['#weight'], 3, 'Rendered weight is correct.');
    $this
      ->assertEqual($render['grants'][$gids[1]]['#weight'], 4, 'Rendered weight increments.');
    $this
      ->assertTrue($render['grants']['#sorted'], 'Rendered grants are sorted.');

    // Check the renderable array.
    $this
      ->assertEqual($render['grants'][$gids[0]]['#theme'], 'access_grant', 'Rendered grants are themed.');
    $this
      ->assertEqual($render['grants'][$gids[0]]['#view_mode'], 'full', 'Grants are rendered in full mode.');
    $this
      ->assertEqual($render['grants'][$gids[0]]['#access_grant']->gid, $gids[0], 'Grant object is included in the renderable array.');

    // Check attached fields and pseudo-fields.
    $this
      ->assertEqual($render['grants'][$gids[0]]['#bundle'], $this->scheme->machine_name, 'The bundle property is attached.');
    $this
      ->assertTrue(isset($render['grants'][$gids[0]]['user']), 'The user property is attached.');
    $this
      ->assertTrue(isset($render['grants'][$gids[0]]['role']), 'The role property is attached.');
  }

  /**
   * Ensure that the access grant static reset works correctly.
   */
  public function testGrantStaticReset() {

    // Set the grant's realm value.
    $field_name = $this->scheme->realm_field['field_name'];
    $this->grants[0]->{$field_name} = array(
      'und' => array(
        array(
          'value' => 0,
        ),
      ),
    );
    access_grant_save($this->grants[0]);

    // Load the grant.
    $original_grant = access_grant_load($this->grants[0]->gid);
    $this
      ->assertTrue(is_object($original_grant) && $original_grant->gid == $this->grants[0]->gid, 'Grant loaded successfully.');
    $this_grant_field = $this->grants[0]->{$field_name};
    $original_grant_field = $original_grant->{$field_name};
    $this
      ->assertEqual($original_grant_field['und'][0]['value'], $this_grant_field['und'][0]['value']);

    // Change the grant's realm value.
    $grant = $original_grant;
    $grant->{$field_name} = array(
      'und' => array(
        array(
          'value' => 1,
        ),
      ),
    );
    access_grant_save($grant);

    // Reload the grant.
    $new_grant = access_grant_load($original_grant->gid);
    $grant_field = $grant->{$field_name};
    $new_grant_field = $new_grant->{$field_name};
    $this
      ->assertEqual($new_grant_field['und'][0]['value'], $grant_field['und'][0]['value']);

    // Delete the grant.
    $grants = access_grant_load_multiple(FALSE);
    $this
      ->assertTrue(isset($grants[$this->grants[0]->gid]), 'The grant exists.');
    access_grant_delete($this->grants[0]->gid);
    $grants = access_grant_load_multiple(FALSE);
    $this
      ->assertTrue(!isset($grants[$this->grants[0]->gid]), 'The grant was deleted.');
  }

}

/**
 * Tests the access control kit API.
 */
class AccessAPITest extends AccessWebTestCase {

  /**
   * An ACK-enabled role.
   *
   * @var object
   */
  protected $ackRole;

  /**
   * An access scheme.
   *
   * @var object
   */
  protected $scheme;

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access control kit API',
      'description' => 'Tests the access control kit API and handler interface.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {

    // Enable the access control kit module and our dummy test module.
    parent::setUp(array(
      'access',
      'access_test',
    ));

    // Create a role that uses the permission defined in access_test.module.
    $rid = $this
      ->drupalCreateRole(array(
      'meow',
    ));
    $this->ackRole = user_role_load($rid);

    // Create an access scheme that uses the test role.
    $scheme = $this
      ->createScheme();
    $scheme->roles = array(
      $this->ackRole->rid => $this->ackRole->name,
    );
    variable_set('access_scheme_roles_' . $scheme->machine_name, $scheme->roles);
    $this->scheme = $scheme;
  }

  /**
   * Attach a handler to the test scheme through the UI.
   *
   * Checks that the ACK hooks and their alter hooks fire correctly as we go.
   */
  public function testHandlerInterface() {

    // Create and log in an admin user.
    $admin_user = $this
      ->drupalCreateUser(array(
      'administer access schemes',
    ));
    $this
      ->drupalLogin($admin_user);

    // Go to the "add access scheme" page and check that the boolean scheme type
    // label was successfully altered.
    $this
      ->drupalGet('admin/structure/access/add');
    $this
      ->assertNoText(t('Boolean'), 'hook_access_scheme_info_alter(): The boolean scheme type info was changed.');
    $this
      ->clickLink(t('Yes or no'));

    // Check that the settings callback fired.
    $this
      ->assertText(t('The scheme does not have grants.'), 'Scheme settings callback was called.');

    // Check that the access-controllable object types and handlers were
    // successfully registered and altered.
    $this
      ->drupalGet('admin/structure/access/' . $this->scheme->machine_name);
    $this
      ->assertText(t('Cat'), 'hook_access_info(): The object type was registered.');
    $this
      ->assertNoText(t('Kitten'), 'hook_access_info_alter(): The access-controllable object type info was changed.');
    $this
      ->assertFieldChecked('edit-handlers-cat-handler');
    $this
      ->assertNoFieldChecked('edit-handlers-cat-handler--2');
    $this
      ->assertFieldByName('handlers[cat][handler]', 'ACKTestMeowHandler', 'hook_access_handler_info(): The handler was registered.');
    $this
      ->assertText(t('Dog'), 'hook_access_info(): The object type was registered.');
    $this
      ->assertFieldChecked('edit-handlers-dog-handler');
    $this
      ->assertNoFieldChecked('edit-handlers-dog-handler--2');
    $this
      ->assertFieldByName('handlers[dog][handler]', 'ACKTestMeowHandler', 'hook_access_handler_info_alter(): The supported object types for the handler were changed.');
    $this
      ->assertText(t('No object access handlers are available to manage @object_type objects in a @scheme_type scheme.', array(
      '@object_type' => t('Bird'),
      '@scheme_type' => t('Yes or no'),
    )), 'The handler UI notifies the user when there are no available handlers.');

    // Attach the handler and confirm that the attachment was saved.
    $edit = array(
      'handlers[cat][handler]' => 'ACKTestMeowHandler',
    );
    $this
      ->drupalPost(NULL, $edit, t('Save access scheme'));
    $this
      ->clickLink(t('edit'));
    $this
      ->assertNoFieldChecked('edit-handlers-cat-handler');
    $this
      ->assertFieldChecked('edit-handlers-cat-handler--2', 'The handler was attached to the correct object type.');
    $this
      ->assertFieldChecked('edit-handlers-dog-handler');
    $this
      ->assertNoFieldChecked('edit-handlers-dog-handler--2', 'The handler was not attached to the incorrect object type.');
    $this
      ->assertText(t('The meow handler assigns the boolean TRUE realm to cats and the boolean FALSE realm to everything else.'), 'The handler description was displayed.');
    $this
      ->assertText(t('Meow handler settings would go here.'), 'The handler settings method was called.');

    // Detach the handler and confirm that the attachment was deleted.
    $edit = array(
      'handlers[cat][handler]' => '',
    );
    $this
      ->drupalPost(NULL, $edit, t('Save access scheme'));
    $this
      ->clickLink(t('edit'));
    $this
      ->assertFieldChecked('edit-handlers-cat-handler');
    $this
      ->assertNoFieldChecked('edit-handlers-cat-handler--2', 'The handler was detached.');

    // Check that the scheme settings form changes when grants are present.
    $this
      ->createGrant($this->scheme, $this->ackRole);
    $this
      ->drupalGet('admin/structure/access/' . $this->scheme->machine_name);
    $this
      ->assertText(t('The scheme has grants.'), 'Scheme form detects existing grants.');
  }

  /**
   * Test access arbitration through our dummy handler.
   */
  public function testHandlerMethods() {
    $dummy_object = new stdClass();

    // Attach the test "meow" handler for dummy "cat" and "dog" object types.
    entity_get_controller('access_scheme')
      ->attachHandler($this->scheme, 'cat', 'ACKTestMeowHandler');
    entity_get_controller('access_scheme')
      ->attachHandler($this->scheme, 'dog', 'ACKTestMeowHandler');
    access_scheme_save($this->scheme);

    // Reset the scheme cache.
    $this->scheme = access_scheme_load($this->scheme->sid, TRUE);

    // Create an access grant in the scheme for a test user.
    $grant = $this
      ->createGrant($this->scheme, $this->ackRole);

    // Assign access in the TRUE realm.
    $fieldname = $this->scheme->realm_field_name;
    $grant->{$fieldname} = array(
      LANGUAGE_NONE => array(
        array(
          'value' => TRUE,
        ),
      ),
    );
    access_grant_save($grant);

    // Reset grant cache.
    access_grant_load($grant->gid, TRUE);

    // Get the test user.
    $test_user = user_load($grant->uid, TRUE);

    // Check access.
    $this
      ->assertTrue(access_user_object_access('meow', 'cat', $dummy_object, $test_user), 'User has access to an object in an assigned realm.');
    $this
      ->assertFalse(access_user_object_access('meow', 'dog', $dummy_object, $test_user), 'User does not have access to an object in an unassigned realm.');
  }

  /**
   * Test clean-up functions.
   */
  public function testHandlerCleanup() {

    // Attach a handler provided by the access_test module.
    entity_get_controller('access_scheme')
      ->attachHandler($this->scheme, 'cat', 'ACKTestMeowHandler');
    access_scheme_save($this->scheme);
    $this->scheme = access_scheme_load($this->scheme->sid, TRUE);

    // Create and log in an admin user.
    $admin_user = $this
      ->drupalCreateUser(array(
      'administer access schemes',
    ));
    $this
      ->drupalLogin($admin_user);

    // Confirm that the handler is attached.
    $this
      ->drupalGet('admin/structure/access/' . $this->scheme->machine_name);
    $this
      ->assertFieldChecked('edit-handlers-cat-handler--2', 'The handler is attached.');

    // Disable the access_test module.
    module_disable(array(
      'access_test',
    ), FALSE);
    drupal_flush_all_caches();
    $this->scheme = access_scheme_load($this->scheme->sid, TRUE);
    $this
      ->drupalGet('admin/structure/access/' . $this->scheme->machine_name);
    $this
      ->assertNoText('Meow', 'The handler is not listed.');

    // Uninstall the access_test module, then re-enable and check handlers.
    drupal_uninstall_modules(array(
      'access_test',
    ), FALSE);
    drupal_flush_all_caches();
    module_enable(array(
      'access_test',
    ), FALSE);
    drupal_flush_all_caches();
    $this->scheme = access_scheme_load($this->scheme->sid, TRUE);
    $this
      ->drupalGet('admin/structure/access/' . $this->scheme->machine_name);
    $this
      ->assertNoFieldChecked('edit-handlers-cat-handler--2', 'The handler did not reattach after uninstall.');
  }

}

/**
 * Tests the bundled scheme types and handlers.
 */
class AccessPluginTest extends AccessWebTestCase {

  /**
   * Implements getInfo(), required method for SimpleTest.
   */
  public static function getInfo() {
    return array(
      'name' => 'Access control kit plugins',
      'description' => 'Tests the scheme type and handler plugins.',
      'group' => 'Access control kit',
    );
  }

  /**
   * Overrides DrupalWebTestCase::setUp().
   */
  public function setUp() {
    parent::setUp(array(
      'access',
      'taxonomy',
    ));

    // We need to start without any defined vocabularies.
    foreach (taxonomy_vocabulary_get_names() as $vocabulary) {
      taxonomy_vocabulary_delete($vocabulary->vid);
    }
  }

  /**
   * Check that scheme types are defined correctly.
   */
  public function testSchemeInfo() {
    $info = access_scheme_info();

    // The boolean type should always be available.
    $this
      ->assertTrue(isset($info['boolean']), 'The boolean scheme type is defined.');
    $scheme = $this
      ->createScheme('boolean');
    $scheme = access_scheme_load($scheme->sid);
    $realms = array(
      0 => t('False'),
      1 => t('True'),
    );
    $diff_a = array_diff_assoc($scheme->realms, $realms);
    $diff_b = array_diff_assoc($realms, $scheme->realms);
    $this
      ->assertTrue(empty($diff_a) && empty($diff_b), 'The boolean realms were found.');

    // The user type should always be available.
    $this
      ->assertTrue(isset($info['user']), 'The user scheme type is defined.');
    $scheme = $this
      ->createScheme('user');
    $scheme = access_scheme_load($scheme->sid);
    $realms = db_query('SELECT uid, name FROM {users} WHERE uid > 0')
      ->fetchAllKeyed();
    $diff_a = array_diff_assoc($scheme->realms, $realms);
    $diff_b = array_diff_assoc($realms, $scheme->realms);
    $this
      ->assertTrue(empty($diff_a) && empty($diff_b), 'The user realms were found.');

    // The list types should not be available until a field exists.
    foreach (array(
      'integer',
      'float',
      'text',
    ) as $type) {
      $list = 'list_' . $type;
      $field_name = 'field_' . $type;
      $this
        ->assertFalse(isset($info[$list]), 'The ' . $list . ' scheme type is not available without a usable field.');
      $scheme = $this
        ->createListScheme($type);
      $info = access_scheme_info();
      $this
        ->assertTrue(isset($info[$list]), 'The ' . $list . ' scheme type is available when a usable field exists.');
      $field = field_info_field($field_name);
      $realms = list_allowed_values($field);
      $diff_a = array_diff_assoc($scheme->realms, $realms);
      $diff_b = array_diff_assoc($realms, $scheme->realms);
      $this
        ->assertTrue(empty($diff_a) && empty($diff_b), 'The ' . $list . ' realms were found.');
      $settings_form = call_user_func_array($scheme->info['settings callback'], array(
        $scheme,
        FALSE,
      ));
      $this
        ->assertTrue(isset($settings_form['field_name']), 'The ' . $list . ' settings form was found.');
      $this
        ->assertTrue(isset($settings_form['field_name']['#options'][$field_name]), 'The usable ' . $list . ' field appears as an option.');
    }

    // The taxonomy type should not be available until a vocabulary exists.
    $this
      ->assertFalse(isset($info['taxonomy_term']), 'The taxonomy_term scheme type is not available without a usable vocabulary.');
    $scheme = $this
      ->createTaxonomyTermScheme('vocabulary_taxonomy_term');
    $info = access_scheme_info();
    $this
      ->assertTrue(isset($info['taxonomy_term']), 'The taxonomy_term scheme type is available when a usable vocabulary exists.');
    $vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary_taxonomy_term');
    $realms = db_query('SELECT tid, name FROM {taxonomy_term_data} WHERE vid = :vid', array(
      ':vid' => $vocabulary->vid,
    ))
      ->fetchAllKeyed();
    $diff_a = array_diff_assoc($scheme->realms, $realms);
    $diff_b = array_diff_assoc($realms, $scheme->realms);
    $this
      ->assertTrue(empty($diff_a) && empty($diff_b), 'The taxonomy_term realms were found.');
    $settings_form = call_user_func_array($scheme->info['settings callback'], array(
      $scheme,
      FALSE,
    ));
    $this
      ->assertTrue(isset($settings_form['vocabulary']), 'The taxonomy_term settings form was found.');
    $this
      ->assertTrue(isset($settings_form['vocabulary']['#options'][$vocabulary->machine_name]), 'The usable vocabulary appears as an option.');
  }

  /**
   * Check that handlers are defined correctly.
   */
  public function testHanderInfo() {
    $info = access_handler_info();

    // We need schemes and a representative entity to test the handlers.
    $scheme_list = $this
      ->createListScheme('integer');
    $scheme_term = $this
      ->createTaxonomyTermScheme('vocabulary_taxonomy_term');
    $content_type = $this
      ->drupalCreateContentType();

    // Add a list field to the content type.
    $instance = array(
      'field_name' => 'field_integer',
      'entity_type' => 'node',
      'bundle' => $content_type->type,
    );
    field_create_instance($instance);

    // Add a term reference field to the content type.
    $field = array(
      'field_name' => 'field_term',
      'type' => 'taxonomy_term_reference',
      'settings' => array(
        'allowed_values' => array(
          array(
            'vocabulary' => 'vocabulary_taxonomy_term',
            'parent' => '0',
          ),
        ),
      ),
    );
    field_create_field($field);
    $instance = array(
      'field_name' => 'field_term',
      'entity_type' => 'node',
      'bundle' => $content_type->type,
      'required' => TRUE,
    );
    field_create_instance($instance);

    // Create the test node.
    $node = $this
      ->drupalCreateNode(array(
      'type' => $content_type->type,
    ));
    $node = node_load($node->nid);
    $node->field_integer[LANGUAGE_NONE][]['value'] = 7;
    $node->field_term[LANGUAGE_NONE][]['tid'] = 3;
    node_save($node);

    // Get the node form for later reference.
    $node_form_state = array();
    $node_form_state['build_info']['args'] = array(
      $node,
    );
    module_load_include('inc', 'node', 'node.pages');
    $node_form = drupal_build_form($node->type . '_node_form', $node_form_state);

    // Test the generic field handler.
    $this
      ->assertTrue(isset($info['ACKEntityField']), 'Found the generic field handler.');
    $handler = new ACKEntityField($scheme_list);
    $description = $handler
      ->description();
    $this
      ->assertIdentical($description, t('The value of %field_name will determine realm membership.', array(
      '%field_name' => 'field_integer',
    )), 'Generic handler has the correct description.');
    $form = $handler
      ->settingsForm();
    $this
      ->assertIdentical($form, array(), 'Generic handler has no settings.');
    $realms = $handler
      ->objectRealms('node', $node);
    $this
      ->assertIdentical($realms, array(
      7,
    ), 'Generic handler can find realm values on a node.');

    // Test altering the node form.
    $this
      ->assertTrue(empty($node_form['field_integer'][LANGUAGE_NONE]['#disabled']), 'List field is accessible before handler is applied.');
    $handler
      ->objectFormAlter('node', $node, $node_form, $node_form_state, $node->type . '_node_form');
    $this
      ->assertTrue($node_form['field_integer'][LANGUAGE_NONE]['#disabled'], 'List field is locked after handler is applied.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['_none']), 'List field contains the _none option.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['1']), 'List field contains option 1.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['7']), 'List field contains option 7.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['31']), 'List field contains option 31.');
    $handler
      ->objectFormAlter('node', $node, $node_form, $node_form_state, $node->type . '_node_form', array(
      '7',
      '31',
      '365',
    ));
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['_none']), 'The _none option was not removed from the list.');
    $this
      ->assertFalse(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['1']), 'Option 1 was removed from the the list.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['7']), 'Option 7 remains in the list.');
    $this
      ->assertTrue(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['31']), 'Option 31 remains in the list.');
    $this
      ->assertFalse(isset($node_form['field_integer'][LANGUAGE_NONE]['#options']['365']), 'Option 365 was not added to the list.');

    // Test the taxonomy term reference handler.
    $this
      ->assertTrue(isset($info['ACKEntityTaxonomyTermReference']), 'Found the term reference field handler.');
    $handler = new ACKEntityTaxonomyTermReference($scheme_term, array(
      'field_name' => 'field_term',
    ));
    $description = $handler
      ->description();
    $this
      ->assertIdentical($description, t('The value of the selected term reference field will determine realm membership.'), 'Term handler has the correct description.');
    $form = $handler
      ->settingsForm();
    $this
      ->assertTrue(isset($form['field_name']['#default_value']), 'Term handler has settings.');
    $this
      ->assertIdentical($form['field_name']['#default_value'], 'field_term', 'Term handler settings default to the saved value.');
    $realms = $handler
      ->objectRealms('node', $node);
    $this
      ->assertIdentical($realms, array(
      3,
    ), 'Term handler can find realm values on a node.');
    $this
      ->assertTrue(empty($node_form['field_term'][LANGUAGE_NONE]['#disabled']), 'Term field is accessible before handler is applied.');
    $this
      ->assertTrue(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['1']), 'Term field contains option 1.');
    $this
      ->assertTrue(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['2']), 'Term field contains option 2.');
    $this
      ->assertTrue(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['3']), 'Term field contains option 3.');
    $this
      ->assertEqual($node_form['field_term'][LANGUAGE_NONE]['#default_value'], array(
      '3',
    ), 'Term field defaults to the saved value.');
    $handler
      ->objectFormAlter('node', $node, $node_form, $node_form_state, $node->type . '_node_form', array(
      '2',
    ));
    $this
      ->assertFalse(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['_none']), 'The _none option was not added to the term field.');
    $this
      ->assertFalse(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['1']), 'Option 1 was removed from the term field.');
    $this
      ->assertTrue(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['2']), 'Option 2 remains in the term field.');
    $this
      ->assertFalse(isset($node_form['field_term'][LANGUAGE_NONE]['#options']['3']), 'Option 3 was removed from the term field.');
    $this
      ->assertTrue($node_form['field_term'][LANGUAGE_NONE]['#disabled'], 'Term field is locked after the handler is applied.');
    $this
      ->assertEqual($node_form['field_term'][LANGUAGE_NONE]['#default_value'], array(
      '2',
    ), 'Only allowable term value is preselected by the handler.');
  }

}

Classes

Namesort descending Description
AccessAPITest Tests the access control kit API.
AccessGrantFunctionTest Tests for access grant functions.
AccessGrantInterfaceTest Tests the access grant interface.
AccessPluginTest Tests the bundled scheme types and handlers.
AccessSchemeFunctionTest Tests for access scheme functions.
AccessSchemeInterfaceTest Tests the access scheme interface.
AccessWebTestCase Provides common helper methods for access control kit module tests.