You are here

acquia_lift.test in Acquia Lift Connector 7

Same filename and directory in other branches
  1. 7.3 tests/acquia_lift.test
  2. 7.2 tests/acquia_lift.test

Integration tests for Acquia Lift module.

File

tests/acquia_lift.test
View source
<?php

/**
 * @file
 * Integration tests for Acquia Lift module.
 */
class AcquiaLiftWebTestBase extends DrupalWebTestCase {

  /**
   * The string to use as the runtime API key.
   *
   * @var string
   */
  protected $liftAPIKey = 'api-key-123';

  /**
   * The string to use as the admin API key.
   *
   * @var string
   */
  protected $liftAdminKey = 'admin-key-123';

  /**
   * The string to use as the admin API URL.
   *
   * @var string
   */
  protected $liftAPIUrl = 'http://some.valid.url';

  /**
   * The string to use as the owner code.
   *
   * @var string
   */
  protected $liftAOwnerCode = 'Some_valid-owner123-code';
  protected $adminUser;
  protected $managerUser;
  protected $personalizedQueue;
  public function setUp() {
    require_once dirname(__FILE__) . '/../includes/acquia_lift.classes.inc';
    require_once dirname(__FILE__) . '/acquia_lift.test_classes.inc';
    parent::setUp(array(
      'ctools',
      'personalize',
      'acquia_lift',
      'personalize_blocks',
      'personalize_test',
    ));
    $this->adminUser = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'administer personalize configuration',
    ));
    $this->managerUser = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
      'administer visitor actions',
    ));
    $this
      ->drupalLogin($this->managerUser);
    $this->personalizedQueue = DrupalQueue::get('acquia_lift_sync');
    variable_set('acquia_lift_account_info', array(
      'owner_code' => $this->liftAOwnerCode,
      'api_key' => $this->liftAPIKey,
      'admin_key' => $this->liftAdminKey,
    ));
  }
  public function tearDown() {
    variable_del('acquia_lift_web_test_data');
    parent::tearDown();
  }

  /**
   * Asserts that the expected items are in the queue.
   *
   * @param $expected_items
   *   An array of queued items.
   */
  protected function assertQueueItems($expected_items) {
    $actual_items = array();
    $result = db_query('SELECT data FROM {queue} q WHERE name = \'acquia_lift_sync\' ORDER BY created ASC');
    foreach ($result as $row) {
      $actual_items[] = unserialize($row->data);
    }
    $this
      ->assertEqual($expected_items, $actual_items);
  }

  /**
   * Help function to create and test queue creation of Personalize Agent
   *
   * @param array $data
   *  array (
   *    'name' => Agent title
   *    'machine_name' => string processed by personalize_generate_machine_name()
   *  )
   * @param bool $cleanQueue Clean or not Drupal queue after 'saveAgent' queue testing
   * @return NULL|PersonalizeAgentInterface
   *
   * @see personalize_generate_machine_name()
   * @see testSaveAgent()
   */
  protected function createTestAgent($data = array(), $cleanQueue = TRUE, $assertResults = TRUE) {
    $this
      ->configureAcquiaLiftAccount();
    $this
      ->drupalLogin($this->managerUser);
    $data += array(
      'name' => $this
        ->randomName(),
      'agent_type' => 'acquia_lift',
      'decision_style' => 'adaptive',
      'control_rate' => 10,
      'explore_rate' => 20,
      'cache_decisions' => 1,
      'auto_stop' => 0,
    );
    $data += array(
      'machine_name' => personalize_generate_machine_name($data['name'], 'personalize_agent_machine_name_exists'),
    );
    $edit = array(
      'agent_basic_info[title]' => $data['name'],
      'agent_basic_info[machine_name]' => $data['machine_name'],
      'agent_basic_info[agent_type]' => $data['agent_type'],
      'agent_basic_info[options][acquia_lift][decision_style]' => $data['decision_style'],
      'agent_basic_info[options][acquia_lift][control_rate]' => $data['control_rate'],
      'agent_basic_info[options][acquia_lift][explore_rate]' => $data['explore_rate'],
      'cache_decisions' => $data['cache_decisions'],
    );
    $this
      ->drupalPost('admin/structure/personalize/add', $edit, $this
      ->getButton('agent'));
    if ($data['auto_stop']) {
      $agent = personalize_agent_load($data['machine_name']);
      $agent->data['auto_stop'] = 1;
      AcquiaLiftAPI::setTestInstance();
      personalize_agent_save($agent);
    }
    $agent = personalize_agent_load_agent($data['machine_name'], TRUE);
    if ($assertResults) {
      $this
        ->assertTrue($agent instanceof AcquiaLiftAgent);
    }
    $expected_queue_items = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $data['machine_name'],
          $data['name'],
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          isset($data['control_rate']) ? $data['control_rate'] / 100 : 0.1,
          isset($data['explore_rate']) ? $data['explore_rate'] / 100 : 0.2,
          isset($data['cache_decisions']) && $data['cache_decisions'],
        ),
        'agent' => $data['machine_name'],
      ),
    );
    if ($assertResults) {
      $this
        ->assertQueueItems($expected_queue_items);
    }
    if ($cleanQueue) {
      $this->personalizedQueue
        ->deleteQueue();
    }
    return $agent;
  }
  protected function createOptionSet($index, $optionData, $withQueueItems = TRUE) {
    $option_set = array(
      'plugin' => $optionData['plugin'],
      'label' => 'Option Set ' . ($index + 1),
      'agent' => $optionData['agent'],
    );
    $options = array();
    $choice_ids = array();
    if (!isset($optionData['option_ids'])) {
      for ($j = 0; $j < $optionData['num_options']; $j++) {
        $option_id = personalize_generate_option_id($j);
        $choice_ids[] = $option_id;
        $options[$j] = array(
          'option_id' => $option_id,
          'option_label' => personalize_generate_option_label($j),
        );
      }
    }
    else {
      foreach ($optionData['option_ids'] as $i => $option_id) {
        $choice_ids[] = $option_id;
        $options[$i] = array(
          'option_id' => $option_id,
          'option_label' => personalize_generate_option_label($i),
        );
      }
    }
    $option_set['options'] = $options;
    $option_set = (object) $option_set;
    try {
      personalize_option_set_save($option_set);
    } catch (PersonalizeException $e) {
      $this
        ->fail('Exception thrown with message: . ' . $e
        ->getMessage());
    }
    $expected_queue_items = array();
    if ($withQueueItems) {
      $expected_queue_items[] = array(
        'method' => 'savePoint',
        'args' => array(
          $optionData['agent'],
          personalize_get_decision_point_name_for_option_set($option_set),
        ),
        'agent' => $optionData['agent'],
      );

      // An item will be added to the queue to save the decision.
      $expected_queue_items[] = array(
        'method' => 'saveDecision',
        'args' => array(
          $optionData['agent'],
          personalize_get_decision_point_name_for_option_set($option_set),
          personalize_get_decision_name_for_option_set($option_set),
        ),
        'agent' => $optionData['agent'],
      );

      // An item will be added to teh queue to save each option.
      foreach ($choice_ids as $choice) {
        $expected_queue_items[] = array(
          'method' => 'saveChoice',
          'args' => array(
            $optionData['agent'],
            personalize_get_decision_point_name_for_option_set($option_set),
            personalize_get_decision_name_for_option_set($option_set),
            $choice,
          ),
          'agent' => $optionData['agent'],
        );
      }
    }
    return array(
      $option_set,
      $expected_queue_items,
    );
  }

  /**
   * Creates a user profile field which can be used for targeting.
   *
   * @return array
   *   An associative array representing the field.
   */
  protected function createUserProfileField() {
    $field = array(
      'field_name' => 'field_' . drupal_strtolower($this
        ->randomName()),
      'type' => 'text',
      'cardinality' => 1,
    );
    field_create_field($field);
    $fieldInstance = array(
      'field_name' => $field['field_name'],
      'entity_type' => 'user',
      'bundle' => 'user',
      'settings' => array(
        'user_register_form' => FALSE,
      ),
    );
    field_create_instance($fieldInstance);
    return $field;
  }

  /**
   * Helper method to move buttons names to one pace to simplify
   * it's maintaining
   *
   * Kind of Page Objects Patterns
   *
   * @param $type string
   * @return string
   */
  protected function getButton($type = '') {
    switch ($type) {
      case 'agent':
        return t('Save campaign settings');
      case 'goal':
        return t('Save goals');
      case 'option':
        return t('Save variation sets');
      case 'mvt':
        return t('Save test');
      case 'delete':
        return t('Delete');
      case 'config':
        return t('Save configuration');
      default:
        return t('Save');
    }
  }

  /**
   * Helper method to configure an Acquia Lift account.
   */
  protected function configureAcquiaLiftAccount() {
    $edit = array(
      'acquia_lift_account_info[owner_code]' => $this->liftAOwnerCode,
      'acquia_lift_account_info[api_key]' => $this->liftAPIKey,
      'acquia_lift_account_info[admin_key]' => $this->liftAdminKey,
      'acquia_lift_account_info[api_url]' => $this->liftAPIUrl,
    );
    $this
      ->drupalLogin($this->adminUser);
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->resetAll();
  }

  /**
   * Creates the required number of custom blocks.
   *
   * @param int $num
   *   The number of blocks to create.
   *
   * @return array
   *   An array of block deltas for use in personalized blocks.
   */
  protected function createPersonalizedBlock($index, $agent, $num_options = 2) {
    $option_set = array(
      'plugin' => 'block',
      'label' => 'Option Set ' . ($index + 1),
      'agent' => $agent->machine_name,
      'data' => array(
        'block_title' => 'Personalized Block ' . ($index + 1),
      ),
    );
    $options = array();
    module_load_include('inc', 'personalize_blocks', 'personalize_blocks.admin');
    for ($i = 1; $i <= $num_options; $i++) {
      $title = t('Custom block @num', array(
        '@num' => $i,
      ));
      $values = array(
        'title' => $title,
        'info' => $title,
        'body' => array(
          'format' => 'filtered_html',
          'value' => 'Some value',
        ),
      );
      $option = array(
        'option_label' => 'Option ' . $i,
        'option_id' => 'option-' . $i,
        'bid' => _personalize_blocks_add_custom_block($values),
      );
      $options[] = $option;
    }
    $option_set['options'] = $options;
    $option_set = (object) $option_set;
    try {
      personalize_option_set_save($option_set);
    } catch (PersonalizeException $e) {
      $this
        ->fail('Exception thrown with message: . ' . $e
        ->getMessage());
      return NULL;
    }
    return $option_set;
  }

}
class AcquiaLiftWebTestConfig extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Configuration'),
      'description' => t('Tests related to configuring Acquia Lift.'),
      'group' => t('Personalize'),
    );
  }
  public function testLiftAgentAvailibility() {

    // remove the account information
    $lift_info = variable_get('acquia_lift_account_info', array());
    variable_del('acquia_lift_account_info');
    $this
      ->resetAll();

    // Try to add a new campaign and verify that the Acquia Lift agent isn't presented.
    $this
      ->drupalGet('admin/structure/personalize/add');
    $this
      ->assertText(t('Your Acquia Lift account info has not been configured.'));
    $this
      ->assertNoRaw('<option value="acquia_lift">acquia_lift</option>');

    // Add the configuration information for the Lift account.
    $this
      ->configureAcquiaLiftAccount();
    $this
      ->drupalLogin($this->managerUser);

    // Now confirm that Acquia Lift is an option for new campaigns.
    $this
      ->drupalGet('admin/structure/personalize/add');
    $this
      ->assertNoText(t('Your Acquia Lift account info has not been configured.'));
    $this
      ->assertRaw('<option value="acquia_lift">acquia_lift</option>');
  }
  public function testConfigForm() {
    $this
      ->drupalLogin($this->adminUser);

    // Try entering an invalid owner code.
    $edit = array(
      'acquia_lift_account_info[owner_code]' => 'some invalid string',
      'acquia_lift_account_info[api_key]' => $this->liftAPIKey,
      'acquia_lift_account_info[admin_key]' => $this->liftAdminKey,
      'acquia_lift_account_info[api_url]' => $this->liftAPIUrl,
    );
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertText('You must enter a valid owner code');

    // Now try with a valid code.
    $edit['acquia_lift_account_info[owner_code]'] = $this->liftAOwnerCode;
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertNoText('You must enter a valid owner code');
    $account_info = variable_get('acquia_lift_account_info', array());
    $this
      ->assertEqual($account_info['owner_code'], $this->liftAOwnerCode);
    $this
      ->assertEqual($account_info['api_key'], $this->liftAPIKey);
    $this
      ->assertEqual($account_info['admin_key'], $this->liftAdminKey);
    $this
      ->assertEqual($account_info['api_url'], 'some.valid.url');

    // Try entering an invalid API url.
    $edit['acquia_lift_account_info[api_url]'] = 'some\\invalid\\url';
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertText('You must enter a valid URL');

    // Try a valid URL with no scheme.
    $edit['acquia_lift_account_info[api_url]'] = 'some.valid.url';
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertNoText('You must enter a valid URL');

    // Try a valid URL with scheme.
    $edit['acquia_lift_account_info[api_url]'] = 'https://some.valid.url';
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertNoText('You must enter a valid URL');
    $this
      ->resetAll();
    $account_info = variable_get('acquia_lift_account_info', array());

    // The scheme should have been stripped out before saving.
    $this
      ->assertEqual($account_info['api_url'], 'some.valid.url');

    // Submit bogus confidence measures.
    $bad_measures = array(
      'abcd' => t('Confidence measure must be a number.'),
      145 => t('Confidence measure must be a value between 0 and 100.'),
      -45 => t('Confidence measure must be a value between 0 and 100.'),
    );
    foreach ($bad_measures as $measure => $message) {
      $edit['acquia_lift_confidence_measure'] = $measure;
      $this
        ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
        ->getButton('config'));
      $this
        ->assertText($message);
      $this
        ->assertNoText(t('A minimum confidence measure of 80% is recommended to ensure proper evaluation of test results.'));
      $this
        ->assertNoText(t('The configuration options have been saved.'));
    }

    // Submit a low confidence measure and test warning.
    $edit['acquia_lift_confidence_measure'] = '10';
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertText(t('A minimum confidence measure of 95% is recommended to ensure proper evaluation of test results.'));
    $this
      ->assertText(t('The configuration options have been saved.'));

    // Submit a valid measure and confirm it is saved without warning.
    $edit['acquia_lift_confidence_measure'] = '95';
    $this
      ->drupalPost('admin/config/content/personalize/acquia_lift', $edit, $this
      ->getButton('config'));
    $this
      ->assertNoText(t('A minimum confidence measure of 95% is recommended to ensure proper evaluation of test results.'));
    $this
      ->assertText(t('The configuration options have been saved.'));
  }
  function testPersonalizeElementsConfigurationIntegration() {
    $fullAdminUser = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'administer personalize configuration',
      'use advanced personalize elements features',
    ));
    $this
      ->drupalLogin($fullAdminUser);

    // Test edit in context settings added to personalize elements configuration form.
    $this
      ->drupalGet('admin/config/content/personalize/personalize-elements');

    // Should be initially set to 1.
    $this
      ->assertFieldChecked('edit-acquia-lift-html-context-strip');
    $edit = array(
      'acquia_lift_html_context_strip' => FALSE,
    );
    $this
      ->drupalPost(NULL, $edit, $this
      ->getButton('config'));

    // Check the javascript setting.
    $this
      ->drupalLogin($this->managerUser);
    $this
      ->drupalGet('');
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($settings['acquia_lift']['edit_in_context_html_strip'], 0);

    // Set it back.
    $this
      ->drupalLogin($fullAdminUser);
    $edit = array(
      'acquia_lift_html_context_strip' => TRUE,
    );
    $this
      ->drupalPost('admin/config/content/personalize/personalize-elements', $edit, $this
      ->getButton('config'));

    // Check that the JavaScript settings changed.
    $this
      ->drupalLogin($this->managerUser);
    $this
      ->drupalGet('');
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($settings['acquia_lift']['edit_in_context_html_strip'], 1);
  }

}
class AcquiaLiftWebTestVariationSets extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Variation Sets'),
      'description' => t('Tests functionality related to variation set creation.'),
      'group' => t('Personalize'),
    );
  }
  function testPersonalizeElementsVariationSets() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.page_variations');
    $agent_name = 'test-agent';

    // Variation 1 (only one element).
    $os1 = personalize_elements_get_option_set_for_variation('my first os', $agent_name, 'div.some-class', 'addClass', 'node');
    acquia_lift_page_variation_create('my_first_varset', $os1, array(
      'personalize_elements_content' => 'test-class',
    ));

    // Variation 2 (2 elements).
    $os2 = personalize_elements_get_option_set_for_variation('my second os', $agent_name, 'div.some-class', 'appendHtml', 'node');
    acquia_lift_page_variation_create('my_first_varset', $os2, array(
      'personalize_elements_content' => 'appended html',
    ));
    $os3 = personalize_elements_get_option_set_for_variation('my first os', $agent_name, 'div.some-class', 'addClass', 'node');
    acquia_lift_page_variation_create('my_first_varset', $os3, array(
      'personalize_elements_content' => 'other-class',
    ), 2);
    $this
      ->resetAll();
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'agent' => $agent_name,
    ));
    $osids = array_keys($option_sets);
    $this
      ->assertEqual(2, count($option_sets));
    foreach ($option_sets as $osid => $option_set) {
      $this
        ->assertEqual('my_first_varset', $option_set->decision_name);
      $this
        ->assertEqual(3, count($option_set->options));
    }
    $expected_options_os1 = array(
      array(
        'option_id' => PERSONALIZE_CONTROL_OPTION_ID,
        'option_label' => PERSONALIZE_CONTROL_OPTION_LABEL,
      ),
      array(
        'option_id' => 'variation-1',
        'option_label' => 'Variation #1',
        'personalize_elements_content' => 'test-class',
      ),
      array(
        'option_id' => 'variation-2',
        'option_label' => 'Variation #2',
        'personalize_elements_content' => 'other-class',
      ),
    );
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $expected_options_os2 = array(
      array(
        'option_id' => PERSONALIZE_CONTROL_OPTION_ID,
        'option_label' => PERSONALIZE_CONTROL_OPTION_LABEL,
      ),
      array(
        'option_id' => 'variation-1',
        'option_label' => 'Variation #1',
      ),
      array(
        'option_id' => 'variation-2',
        'option_label' => 'Variation #2',
        'personalize_elements_content' => 'appended html',
      ),
    );
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
  }

  /**
   * Tests variation set creation.
   *
   * A variation set can be made up of multiple option sets.
   *
   * @todo This should probably be combined with
   * testPersonalizeElementsVariationSets() above and simplified to just
   * personalize_elements elements (but with the complexity of this test).
   */
  function testVariationSets() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.page_variations');
    $admin_user = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
      'administer blocks',
    ));
    $this
      ->drupalLogin($admin_user);

    // Create a new agent via the UI.
    $agent_title = $this
      ->randomName();
    $agent_name = personalize_generate_machine_name($agent_title, 'personalize_agent_machine_name_exists');
    $edit = array(
      'agent_basic_info[title]' => $agent_title,
      'agent_basic_info[machine_name]' => $agent_name,
      'agent_basic_info[agent_type]' => 'test_agent',
    );
    $this
      ->drupalPost('admin/structure/personalize/add', $edit, $this
      ->getButton('agent'));
    $option_set = personalize_elements_get_option_set_for_variation('option-1', $agent_name, '#selector1', 'addCss', 'page1');
    $var_num = acquia_lift_page_variation_create('first_variation_set', $option_set, array(
      'personalize_elements_content' => 'test-class',
    ));
    $this
      ->assertEqual(1, $var_num);

    // Now create another option set and use it for the second variation.
    $option_set_2 = personalize_elements_get_option_set_for_variation('option-2', $agent_name, '#selector2', 'editHtml', 'page1');
    $var_num = acquia_lift_page_variation_create('first_variation_set', $option_set_2, array(
      'personalize_elements_content' => '<p>some html</p>',
    ));
    $this
      ->assertEqual(2, $var_num);

    // Now use the first option set for variation 3.
    $os_1 = personalize_option_set_load($option_set->osid, TRUE);
    $var_num = acquia_lift_page_variation_create('first_variation_set', $os_1, array(
      'personalize_elements_content' => 'another-test-class',
    ));
    $this
      ->assertEqual(3, $var_num);

    // Now add a different option to both for variation 4.
    $os_1 = personalize_option_set_load($option_set->osid, TRUE);
    $var_num = acquia_lift_page_variation_create('first_variation_set', $os_1, array(
      'personalize_elements_content' => 'third-test-class',
    ));
    $this
      ->assertEqual(4, $var_num);

    // Rename the third variation to test if the new names persist across changes.
    acquia_lift_page_variation_rename('first_variation_set', $agent_name, 3, 'three');
    $os_2 = personalize_option_set_load($option_set_2->osid, TRUE);
    $var_num = acquia_lift_page_variation_create('first_variation_set', $os_2, array(
      'personalize_elements_content' => '<p>some other html</p>',
    ), 4);
    $this
      ->assertEqual(4, $var_num);

    // Try to create variation 6, it should correct it to 5.
    $var_num = acquia_lift_page_variation_create('first_variation_set', $os_2, array(
      'personalize_elements_content' => '<p>third html</p>',
    ), 6);
    $this
      ->assertEqual(5, $var_num);

    // Create another option set and add it to variation 1. (We need to
    // make sure an option gets created for each subsequent variation.)
    $option_set_3 = personalize_elements_get_option_set_for_variation('option-3', $agent_name, '#selector3', 'editText', 'page1');
    $var_num = acquia_lift_page_variation_create('first_variation_set', $option_set_3, array(
      'personalize_elements_content' => 'edited text',
    ), 1);
    $this
      ->assertEqual(1, $var_num);
    $this
      ->resetAll();
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'agent' => $agent_name,
    ));
    $osids = array_keys($option_sets);
    $this
      ->assertEqual(3, count($option_sets));
    foreach ($option_sets as $osid => $option_set) {
      $this
        ->assertEqual('first_variation_set', $option_set->decision_name);
      $this
        ->assertEqual(6, count($option_set->options));
    }
    $expected_options_os1 = array(
      array(
        'option_id' => PERSONALIZE_CONTROL_OPTION_ID,
        'option_label' => PERSONALIZE_CONTROL_OPTION_LABEL,
      ),
      array(
        'option_id' => 'variation-1',
        'option_label' => 'Variation #1',
        'personalize_elements_content' => 'test-class',
      ),
      array(
        'option_id' => 'variation-2',
        'option_label' => 'Variation #2',
      ),
      array(
        'option_id' => 'variation-3',
        'option_label' => 'three',
        'personalize_elements_content' => 'another-test-class',
      ),
      array(
        'option_id' => 'variation-4',
        'option_label' => 'Variation #4',
        'personalize_elements_content' => 'third-test-class',
      ),
      array(
        'option_id' => 'variation-5',
        'option_label' => 'Variation #5',
      ),
    );
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $expected_options_os2 = array(
      array(
        'option_id' => PERSONALIZE_CONTROL_OPTION_ID,
        'option_label' => PERSONALIZE_CONTROL_OPTION_LABEL,
      ),
      array(
        'option_id' => 'variation-1',
        'option_label' => 'Variation #1',
      ),
      array(
        'option_id' => 'variation-2',
        'option_label' => 'Variation #2',
        'personalize_elements_content' => '<p>some html</p>',
      ),
      array(
        'option_id' => 'variation-3',
        'option_label' => 'three',
      ),
      array(
        'option_id' => 'variation-4',
        'option_label' => 'Variation #4',
        'personalize_elements_content' => '<p>some other html</p>',
      ),
      array(
        'option_id' => 'variation-5',
        'option_label' => 'Variation #5',
        'personalize_elements_content' => '<p>third html</p>',
      ),
    );
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
    $expected_options_os3 = array(
      array(
        'option_id' => PERSONALIZE_CONTROL_OPTION_ID,
        'option_label' => PERSONALIZE_CONTROL_OPTION_LABEL,
      ),
      array(
        'option_id' => 'variation-1',
        'option_label' => 'Variation #1',
        'personalize_elements_content' => 'edited text',
      ),
      array(
        'option_id' => 'variation-2',
        'option_label' => 'Variation #2',
      ),
      array(
        'option_id' => 'variation-3',
        'option_label' => 'three',
      ),
      array(
        'option_id' => 'variation-4',
        'option_label' => 'Variation #4',
      ),
      array(
        'option_id' => 'variation-5',
        'option_label' => 'Variation #5',
      ),
    );
    $this
      ->assertEqual($expected_options_os3, $option_sets[$osids[2]]->options);

    // Rename the second variation.
    acquia_lift_page_variation_rename('first_variation_set', $agent_name, 2, 'Hello');
    $expected_options_os1[2]['option_label'] = 'Hello';
    $expected_options_os2[2]['option_label'] = 'Hello';
    $expected_options_os3[2]['option_label'] = 'Hello';
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'decision_name' => 'first_variation_set',
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
    $this
      ->assertEqual($expected_options_os3, $option_sets[$osids[2]]->options);

    // Delete the third variation.
    acquia_lift_page_variation_delete('first_variation_set', $agent_name, 3);
    unset($expected_options_os1[3]);
    unset($expected_options_os2[3]);
    unset($expected_options_os3[3]);
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'decision_name' => 'first_variation_set',
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
    $this
      ->assertEqual($expected_options_os3, $option_sets[$osids[2]]->options);

    // Delete another out of order and verify.
    acquia_lift_page_variation_delete('first_variation_set', $agent_name, 1);
    unset($expected_options_os1[1]);
    unset($expected_options_os2[1]);
    unset($expected_options_os3[1]);
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'decision_name' => 'first_variation_set',
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
    $this
      ->assertEqual($expected_options_os3, $option_sets[$osids[2]]->options);

    // Now delete down to one left
    acquia_lift_page_variation_delete('first_variation_set', $agent_name, 2);
    acquia_lift_page_variation_delete('first_variation_set', $agent_name, 4);
    unset($expected_options_os1[2]);
    unset($expected_options_os2[2]);
    unset($expected_options_os3[2]);
    unset($expected_options_os1[4]);
    unset($expected_options_os2[4]);
    unset($expected_options_os3[4]);
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'decision_name' => 'first_variation_set',
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual($expected_options_os1, $option_sets[$osids[0]]->options);
    $this
      ->assertEqual($expected_options_os2, $option_sets[$osids[1]]->options);
    $this
      ->assertEqual($expected_options_os3, $option_sets[$osids[2]]->options);

    // Delete the last and verify that all variations (including control) are
    // removed.
    acquia_lift_page_variation_delete('first_variation_set', $agent_name, 5);
    $option_sets = personalize_option_set_load_multiple(FALSE, array(
      'decision_name' => 'first_variation_set',
      'agent' => $agent_name,
    ), TRUE);
    $this
      ->assertTrue(count($option_sets) === 0);
  }
  function testPersonalizeBlockRedirection() {

    // Create a new agent
    $agent = $this
      ->createTestAgent();

    // Create a personalized block from the form with a destination in the
    // url query string.
    $this
      ->drupalGet('/admin/structure/personalize/variations/personalize-blocks/add', array(
      'query' => array(
        'destination' => 'node',
      ),
    ));
    $edit = array(
      'agent_select' => $agent
        ->getMachineName(),
      'title' => 'My test block',
      'blocks[0][option_label]' => 'Option A',
      'blocks[0][block][bid]' => 'comment_delta_recent',
      'blocks[1][option_label]' => 'Option B',
      'blocks[1][block][bid]' => 'system_delta_main',
    );
    $this
      ->drupalPost(NULL, $edit, t('Save'));

    // Verify that the user is redirected to the original page with a custom
    // URL query parameter.
    $this
      ->assertUrl('node', array(
      'query' => array(
        'liftpm' => 'new_block|My test block',
      ),
    ));

    // Verify that the pending message has been added to the settings.
    $expected_message = t('Created the new <em class="placeholder">My test block</em> personalized block. The block will not appear on your website until you add the block to a region on the !blocks page.', array(
      '!blocks' => l('Structure > Blocks', 'admin/structure/blocks'),
    ));
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($settings['acquia_lift']['pendingMessage'], array(
      $expected_message,
    ));
  }

}
class AcquiaLiftWebTestAgentAdmin extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Agent Administration'),
      'description' => t('Tests functionality of adminstering Acquia Lift agents and their components.'),
      'group' => t('Personalize'),
    );
  }
  public function testSaveAgent() {

    // Create a new agent via the UI.
    $agent = $this
      ->createTestAgent(array(
      'control_rate' => 10,
      'explore_rate' => 30,
    ));
    $agent_name = $agent
      ->getTitle();
    $machine_name = $agent
      ->getMachineName();
    $expected_queue_items = $option_set_queue_items = array();

    // Add some dummy option sets to this agent.
    $option_set_values = array(
      array(
        'agent' => $machine_name,
        'plugin' => 'type1',
        'num_options' => 3,
      ),
      array(
        'agent' => $machine_name,
        'plugin' => 'type2',
        'num_options' => 2,
      ),
    );
    foreach ($option_set_values as $i => $values) {
      list($option_set, $new_queues) = $this
        ->createOptionSet($i, $values);
      $expected_queue_items = array_merge($expected_queue_items, $new_queues);

      // We need to keep track of the option set items that get added to the
      // queue separately from the other items as we need them again later.
      // Dirty way to avoid 'saveAgent' method be included to $option_set_queue_items
      $option_set_queue_items = array_merge($option_set_queue_items, $new_queues);
      if ($i == 0) {
        $expected_queue_items[] = array(
          'method' => 'saveAgent',
          'args' => array(
            $machine_name,
            $agent_name,
            'adaptive',
            PERSONALIZE_STATUS_NOT_STARTED,
            0.1,
            0.3,
            1,
          ),
          'agent' => $machine_name,
        );
      }
    }
    $this
      ->assertQueueItems($expected_queue_items);
    $this->personalizedQueue
      ->deleteQueue();
    $expected_queue_items = array();

    // Save a goal for the agent.
    $goal_name = 'form_submit';
    personalize_goal_save($machine_name, $goal_name, 2);
    $expected_queue_items[] = array(
      'method' => 'saveGoal',
      'args' => array(
        $machine_name,
        $goal_name,
      ),
      'agent' => $machine_name,
    );
    $expected_queue_items[] = array(
      'method' => 'saveAgent',
      'args' => array(
        $machine_name,
        $agent_name,
        'adaptive',
        PERSONALIZE_STATUS_NOT_STARTED,
        0.1,
        0.3,
        1,
      ),
      'agent' => $machine_name,
    );
    $this
      ->assertQueueItems($expected_queue_items);
    $this->personalizedQueue
      ->deleteQueue();
    $expected_queue_items = array();
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", array(), $this
      ->getButton('agent'));
    $expected_queue_items = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $machine_name,
          $agent_name,
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.3,
          1,
        ),
        'agent' => $machine_name,
      ),
    );
    $this
      ->assertQueueItems($expected_queue_items);
    $this->personalizedQueue
      ->deleteQueue();
    $expected_queue_items = array();
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", array(), $this
      ->getButton('agent'));

    // Now the only thing that should get added is an item for the agent
    // because neither goals nor option sets will have changed.
    $expected_queue_items[] = array(
      'method' => 'saveAgent',
      'args' => array(
        $machine_name,
        $agent_name,
        'adaptive',
        PERSONALIZE_STATUS_NOT_STARTED,
        0.1,
        0.3,
        1,
      ),
      'agent' => $machine_name,
    );
    $this
      ->assertQueueItems($expected_queue_items);
    $this->personalizedQueue
      ->deleteQueue();

    // Create an MVT and add the two option sets to it.
    $mvt_label = $this
      ->randomName();
    $mvt_machine_name = personalize_generate_machine_name($mvt_label, 'personalize_mvt_machine_name_exists');
    $edit = array(
      'mvt[add][mvt_basic_info][label]' => $mvt_machine_name,
      'mvt[add][mvt_basic_info][option_sets][]' => array(
        1,
        2,
      ),
    );
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", $edit, $this
      ->getButton('mvt'));

    // The option sets will get added to the queue
    foreach ($option_set_queue_items as &$item) {

      // The second argument, which is the decision point name, will have
      // changed to the MVT name.
      $item['args'][1] = $mvt_machine_name;
    }
    $expected_queue_items = array_slice($option_set_queue_items, 0, 5);

    // @todo Commenting this out as for some reason after saving the new
    // point and decision/choices for osid-1, it saves the old point and
    // decision/choices for osid-2, before deleting the old osid-1 point,
    // saving the second decision and deleting the osid-2 point.

    //$this->assertQueueItems($expected_queue_items);
  }
  public function testSaveAutoTargetingRule() {
    $agent = $this
      ->createTestAgent();

    // as acquia_lift_context options are fetched from Acquia Lift via webservice - use hardcoded ones in tests
    module_load_include('inc', 'personalize', 'personalize.admin');
    $agentStructure = _personalize_agent_from_form_values(array(
      'machine_name' => $agent
        ->getMachineName(),
      'title' => $agent
        ->getTitle(),
      'agent_type' => $agent
        ->getType(),
      'data' => $agent
        ->getData(),
    ));
    $agentStructure->data['visitor_context'] = array(
      // Fake Aquia Lift context to check saveAutoTargetingRule method queue
      'acquia_lift_context' => array(
        'some_acquia_lift_context' => 'some_acquia_lift_context',
      ),
    );
    $this
      ->drupalGet('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit');
    personalize_agent_save($agentStructure);
    $this
      ->drupalGet('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit');
    $agent = personalize_agent_load_agent($agent
      ->getMachineName(), TRUE);
    $agentData = $agent
      ->getData();
    $expected_queues = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          $agentData['decision_style'],
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'saveAutoTargetingRule',
        'args' => array(
          $agent
            ->getMachineName(),
          array_keys(array_filter($agentData['visitor_context']['acquia_lift_context'])),
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();

    // Now remove the acquia_lift_context items and we should be sending a delete call
    // to Acquia Lift.
    $agentStructure->data['visitor_context'] = array(
      'acquia_lift_context' => array(),
    );
    $this
      ->drupalGet('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit');
    personalize_agent_save($agentStructure);
    $this
      ->drupalGet('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit');
    $agent = personalize_agent_load_agent($agent
      ->getMachineName(), TRUE);
    $agentData = $agent
      ->getData();
    $expected_queues = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          $agentData['decision_style'],
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'deleteAutoTargetingRule',
        'args' => array(
          $agent
            ->getMachineName(),
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
  }

  /**
   * Tests syncing of Option Set information to Acquia Lift.
   */
  public function testSyncOptionSets() {
    $agent = $this
      ->createTestAgent();

    // Create a couple of user profile fields for targeting.
    $user_profile_field_1 = $this
      ->createUserProfileField();
    $user_profile_field_2 = $this
      ->createUserProfileField();

    // Include _personalize_agent_from_form_values() function to build agent data.
    module_load_include('inc', 'personalize', 'personalize.admin');
    $agentStructure = _personalize_agent_from_form_values(array(
      'machine_name' => $agent
        ->getMachineName(),
      'title' => $agent
        ->getTitle(),
      'agent_type' => $agent
        ->getType(),
      'data' => $agent
        ->getData(),
    ));

    // Add the user profile fields as context.
    $agentStructure->data['visitor_context'] = array(
      'user_profile_context' => array(
        str_replace('field_', '', $user_profile_field_1['field_name']) => str_replace('field_', '', $user_profile_field_1['field_name']),
        str_replace('field_', '', $user_profile_field_2['field_name']) => str_replace('field_', '', $user_profile_field_2['field_name']),
      ),
    );
    personalize_agent_save($agentStructure);
    $agent = personalize_agent_load_agent($agent
      ->getMachineName(), TRUE);
    $agentData = $agent
      ->getData();
    $agent_queue_item = array(
      'method' => 'saveAgent',
      'args' => array(
        $agent
          ->getMachineName(),
        $agent
          ->getTitle(),
        $agentData['decision_style'],
        PERSONALIZE_STATUS_NOT_STARTED,
        $agentData['control_rate'] / 100,
        $agentData['explore_rate'] / 100,
        isset($agentData['cache_decisions']) && $agentData['cache_decisions'],
      ),
      'agent' => $agent
        ->getMachineName(),
    );
    $expected_queues = array(
      $agent_queue_item,
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
    $expected_queue_items = array();

    // Now add an option set to the agent.
    $personalized_blocks_form_state = array(
      'values' => array(
        'agent_select' => $agent
          ->getMachineName(),
        'title' => $this
          ->randomName(),
        'blocks' => array(
          array(
            'option_label' => 'Option A',
            'option_id' => 'option-A',
            'weight' => 0,
            'block' => array(
              'bid' => 'comment_delta_recent',
            ),
          ),
          array(
            'option_label' => 'Option B',
            'option_id' => 'option-B',
            'weight' => 1,
            'block' => array(
              'bid' => 'system_delta_main',
            ),
          ),
          array(
            'option_label' => 'Option C',
            'option_id' => 'option-C',
            'weight' => 2,
            'block' => array(
              'bid' => 'system_delta_help',
            ),
          ),
        ),
      ),
    );
    personalize_option_set_save(_personalize_blocks_convert_form_to_personalized_block($personalized_blocks_form_state));
    $option_sets = personalize_option_set_load_by_agent($agent
      ->getMachineName(), TRUE);
    $osid = key($option_sets);
    $option_set = $option_sets[$osid];
    $point_name = personalize_get_decision_point_name_for_option_set($option_set);
    $decision_name = personalize_get_decision_name_for_option_set($option_set);
    $agent = personalize_agent_load_agent($agent
      ->getMachineName(), TRUE);
    $agentData = $agent
      ->getData();
    $this
      ->assertTrue(isset($agentData['decisions']) && isset($agentData['decisions'][$osid]));
    $expected_queue_items['point'] = array(
      'method' => 'savePoint',
      'args' => array(
        $agent
          ->getMachineName(),
        $point_name,
      ),
      'agent' => $agent
        ->getMachineName(),
    );
    $expected_queue_items['decision'] = array(
      'method' => 'saveDecision',
      'args' => array(
        $agent
          ->getMachineName(),
        $point_name,
        $decision_name,
      ),
      'agent' => $agent
        ->getMachineName(),
    );
    foreach ($option_set->options as $key => $option) {
      $expected_queue_items[$option['option_id']] = array(
        'method' => 'saveChoice',
        'args' => array(
          $agent
            ->getMachineName(),
          $point_name,
          $decision_name,
          personalize_generate_option_id($key),
        ),
        'agent' => $agent
          ->getMachineName(),
      );
    }
    $expected_queue_items['agent'] = $agent_queue_item;
    $this
      ->assertQueueItems(array_values($expected_queue_items));
    $this->personalizedQueue
      ->deleteQueue();

    // Set up fixed targeting on Option A with two features OR'd together.
    $context_1 = str_replace('field_', '', $user_profile_field_1['field_name']);
    $context_2 = str_replace('field_', '', $user_profile_field_2['field_name']);

    // We can't use the form to add multiple fixed targeting contexts because we can't
    // make simpletest use the "Add context" button, so we send our form values directly
    // to the submit function.
    module_load_include('inc', 'personalize', 'personalize.admin');
    $form_state = array(
      'values' => array(
        'agent' => $agent
          ->getMachineName(),
        'option_sets' => array(
          'option_set_1' => array(
            'winner' => 'option-A',
            'advanced' => array(
              'label' => $personalized_blocks_form_state['values']['title'],
              'stateful' => 0,
            ),
            'options' => array(
              'option-A' => array(
                'explicit_targeting' => array(
                  'mapping' => array(
                    'contexts' => array(
                      array(
                        'context' => 'user_profile_context' . PERSONALIZE_TARGETING_ADMIN_SEPARATOR . $context_1,
                        'value' => array(
                          'match' => 'some value',
                          'operator' => 'equals',
                        ),
                      ),
                      array(
                        'context' => 'user_profile_context' . PERSONALIZE_TARGETING_ADMIN_SEPARATOR . $context_2,
                        'value' => array(
                          'match' => 'some other value',
                          'operator' => 'contains',
                        ),
                      ),
                    ),
                  ),
                  'strategy' => 'OR',
                ),
              ),
            ),
          ),
        ),
      ),
    );
    personalize_agent_option_sets_form_submit(array(), $form_state);
    $feature_1 = $context_1 . '::some-value';
    $feature_2 = $context_2 . '::sc-some-other-value';
    $expected_queue_items['targeting'] = array(
      'method' => 'saveFixedTargetingMapping',
      'args' => array(
        $agent
          ->getMachineName(),
        $point_name,
        array(
          array(
            'feature' => $feature_1,
            'decision' => $decision_name . ':option-A',
          ),
          array(
            'feature' => $feature_2,
            'decision' => $decision_name . ':option-A',
          ),
        ),
      ),
      'agent' => $agent
        ->getMachineName(),
    );

    // Because the decision structure has not changed only the agent and targeting
    // should be sync'd.
    $this
      ->assertQueueItems(array(
      $expected_queue_items['agent'],
      $expected_queue_items['targeting'],
    ));
    $this->personalizedQueue
      ->deleteQueue();

    // Change the fixed targeting strategy to AND the features together.
    $edit = array(
      'option_sets[option_set_1][options][option-A][explicit_targeting][strategy]' => 'AND',
    );
    $this
      ->drupalPost('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit', $edit, $this
      ->getButton('option'));

    // Now the fixed targeting mapping should have a single mapping with
    // comma-separated features.
    $expected_queue_items['targeting']['args'][2] = array(
      array(
        'feature' => $feature_1 . ',' . $feature_2,
        'decision' => $decision_name . ':option-A',
      ),
    );
    $this
      ->assertQueueItems(array(
      $expected_queue_items['agent'],
      $expected_queue_items['targeting'],
    ));
    $this->personalizedQueue
      ->deleteQueue();

    // Remove Option B
    $this
      ->drupalGet('admin/structure/personalize/variations/personalize-blocks/manage/' . $osid . '/edit');
    $this
      ->drupalPost(NULL, array(), 'remove_1');
    $this
      ->drupalPost(NULL, array(), t('Save'));
    $expected_queue_items['delete-B'] = array(
      'method' => 'deleteChoice',
      'args' => array(
        $agent
          ->getMachineName(),
        $point_name,
        $decision_name,
        'option-B',
      ),
      'agent' => $agent
        ->getMachineName(),
    );
    $new_queue_items = array();
    $expected_order = array(
      'point',
      'decision',
      'option-A',
      'option-C',
      'delete-B',
      'agent',
      'targeting',
    );
    foreach ($expected_order as $key) {
      $new_queue_items[$key] = $expected_queue_items[$key];
    }
    $this
      ->assertQueueItems(array_values($new_queue_items));
    $this->personalizedQueue
      ->deleteQueue();
  }
  public function testSyncGoalsFromCampaignUI() {
    $agent = $this
      ->createTestAgent();
    $edit = array(
      'goals[0][action_name]' => 'form_submit',
      'goals[0][value]' => '20',
    );
    $this
      ->drupalPost('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit', $edit, $this
      ->getButton('goal'));
    $expected_queues = array(
      array(
        'method' => 'saveGoal',
        'args' => array(
          $agent
            ->getMachineName(),
          'form_submit',
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
    $this
      ->drupalPost('admin/structure/personalize/manage/' . $agent
      ->getMachineName() . '/edit', array(), $this
      ->getButton('goal'));

    // As we're triggering only goals form and do not change anything
    // saveAgent won't be invoked also
    $expected_queues = array();
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
  }
  public function testSyncGoalsFromVisitorUI() {

    // include visitor_actions_action_name_exists() function to validate action machine name
    module_load_include('inc', 'visitor_actions', 'visitor_actions.admin');
    $agent = $this
      ->createTestAgent();
    $actionTitle = $this
      ->randomName();
    $actionMachineName = personalize_generate_machine_name($actionTitle, 'visitor_actions_action_name_exists');

    // Create new Visitor Action but WITHOUT connection to active agent
    $edit = array(
      'title' => $actionTitle,
      'machine_name' => $actionMachineName,
      'actionable_element' => 'form',
      'identifier[form]' => 'some_form_id',
      'event[form]' => 'server::submit_server',
      'personalize_goal' => FALSE,
      'personalize_goal_value' => 50,
    );
    $this
      ->drupalPost('admin/structure/visitor_actions/add', $edit, $this
      ->getButton());

    // Without connection to the agent we should run any acquia_lift' syncing
    // @see personalize_visitor_action_form_submit()
    $expected_queues = array();
    $this
      ->assertQueueItems($expected_queues);

    // Try to create new with connection to the agent
    $actionTitle = $this
      ->randomName();
    $actionMachineName = personalize_generate_machine_name($actionTitle, 'visitor_actions_action_name_exists');

    // Create new Visitor Action but WITH connection to active agent
    $edit = array(
      'title' => $actionTitle,
      'machine_name' => $actionMachineName,
      'actionable_element' => 'form',
      'identifier[form]' => 'some_form_id',
      'event[form]' => 'server::submit_server',
      'personalize_goal' => TRUE,
      'personalize_goal_value' => 50,
    );
    $this
      ->drupalPost('admin/structure/visitor_actions/add', $edit, $this
      ->getButton());

    // With connection to the agent we should run AcquiaLiftAgent->syncGoals
    $expected_queues = array(
      array(
        'method' => 'saveGoal',
        'args' => array(
          $agent
            ->getMachineName(),
          $actionMachineName,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();

    // Remove machine name from post because it's not available as a field in the edit form.
    unset($edit['machine_name']);

    // Try to modify with same goal value to check that anything won't be synced
    $this
      ->drupalPost('admin/structure/visitor_actions/manage/' . $actionMachineName . '/edit', $edit, $this
      ->getButton());

    // With connection to the agent but with old goal value we should not run AcquiaLiftAgent->syncGoals or saveAgent
    $expected_queues = array();
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();

    // If goal value is changed syncGoals and saveAgent should be invoked
    $edit['personalize_goal_value'] += 10;
    $this
      ->drupalPost('admin/structure/visitor_actions/manage/' . $actionMachineName . '/edit', $edit, $this
      ->getButton());

    // With connection to the agent but with old goal value we should not run AcquiaLiftAgent->syncGoals or saveAgent
    $expected_queues = array(
      array(
        'method' => 'saveGoal',
        'args' => array(
          $agent
            ->getMachineName(),
          $actionMachineName,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
    $this
      ->resetAll();

    // Verify that agent has last visitor action data
    $agentGoals = personalize_goal_load_by_conditions(array(
      'agent' => $agent
        ->getMachineName(),
    ));
    $firstGoal = reset($agentGoals);
    $this
      ->assertEqual($firstGoal->action, $actionMachineName);
    $this
      ->assertEqual($firstGoal->value, $edit['personalize_goal_value']);

    // Try to delete goal that connected to Agent from Visitor Action UI
    $this
      ->drupalPost('admin/structure/visitor_actions/manage/' . $actionMachineName . '/delete', array(), t('Delete'));
    $expected_queues = array(
      array(
        'method' => 'deleteGoal',
        'args' => array(
          $agent
            ->getMachineName(),
          $actionMachineName,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent
            ->getMachineName(),
          $agent
            ->getTitle(),
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent
          ->getMachineName(),
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
    $this
      ->resetAll();

    // No goal should be attached after deletion
    $agentGoals = personalize_goal_load_by_conditions(array(
      'agent' => $agent
        ->getMachineName(),
    ));
    $this
      ->assertEqual($agentGoals, array());
  }
  public function testDeleteAgentGoals() {
    $agent = $this
      ->createTestAgent();
    $agent_name = $agent
      ->getMachineName();

    // Add goals.
    personalize_goal_save($agent_name, 'first-goal', 1);
    personalize_goal_save($agent_name, 'second-goal', 1);
    $this
      ->resetAll();

    // Clear the queue as we're just testing goal deletion.
    $this->personalizedQueue
      ->deleteQueue();
    $goals = personalize_goal_load_by_conditions(array(
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual(2, count($goals));
    $goal_ids = array_keys($goals);
    $first_goal_id = $goal_ids[0];
    $second_goal_id = $goal_ids[1];

    // Delete one of the goals.
    personalize_goal_delete($first_goal_id);
    $expected_queues = array(
      array(
        'method' => 'saveGoal',
        'args' => array(
          $agent_name,
          $goals[$second_goal_id]->action,
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'deleteGoal',
        'args' => array(
          $agent_name,
          $goals[$first_goal_id]->action,
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent_name,
          $agent
            ->getTitle(),
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          0.1,
          0.2,
          1,
        ),
        'agent' => $agent_name,
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
  }
  public function testUpdateAgentStatus() {
    $agent = $this
      ->createTestAgent();
    $agent_name = $agent
      ->getMachineName();

    // Manually set the agent's status to running, bypassing the verification check.
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);
    $this
      ->resetAll();

    // Now use the API function to update the status.
    personalize_agent_set_status($agent_name, PERSONALIZE_STATUS_PAUSED);
    $expected_queues = array(
      array(
        'method' => 'updateAgentStatus',
        'args' => array(
          $agent_name,
          PERSONALIZE_STATUS_PAUSED,
        ),
        'agent' => $agent_name,
      ),
    );
    $this
      ->assertQueueItems($expected_queues);
    $this->personalizedQueue
      ->deleteQueue();
  }

  /**
   * Tests that agents are paused when they need to be paused.
   */
  function testPauseAgents() {

    // Create an agent with a couple of option sets and a couple of goals.
    $control_rate = 10;
    $explore_rate = 30;
    $agent_name = 'my-test-lift-agent';
    $this
      ->createTestAgent(array(
      'name' => $agent_name,
      'control_rate' => $control_rate,
      'explore_rate' => $explore_rate,
    ));
    $option_set_values = array(
      array(
        'agent' => $agent_name,
        'plugin' => 'type1',
        'option_ids' => array(
          'option-1',
          'option-2',
        ),
      ),
      array(
        'agent' => $agent_name,
        'plugin' => 'type2',
        'option_ids' => array(
          'option-a',
          'option-b',
          'option-c',
        ),
      ),
    );
    $option_sets = array();
    foreach ($option_set_values as $i => $values) {
      list($option_set, $new_queue_items) = $this
        ->createOptionSet($i, $values);
      $option_sets[] = $option_set;
    }
    $first_osid = $option_sets[0]->osid;
    $second_osid = $option_sets[1]->osid;

    // Add goals.
    personalize_goal_save($agent_name, 'first-goal', 1);
    personalize_goal_save($agent_name, 'second-goal', 1);
    $this
      ->resetAll();
    $goals = array();
    foreach (personalize_goal_load_by_conditions(array(
      'agent' => $agent_name,
    )) as $goal) {
      $goals[] = $goal;
    }

    // We need to bypass the personalize_agent_set_status() function because it
    // does the verification check, which would fail.
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);

    // Delete a goal - this should not pause the agent.
    personalize_goal_delete($goals[0]->id);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_RUNNING);

    // Now delete the remaining goal - this should pause the agent.
    personalize_goal_delete($goals[1]->id);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_PAUSED);

    // Add a goal back and set it to running again.
    personalize_goal_save($agent_name, 'third-goal', 1);
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);

    // Delete an option set - this should not pause the agent.
    personalize_option_set_delete($first_osid);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_RUNNING);

    // Delete an option from the remaining option set, it should remain running
    $option_set = personalize_option_set_load($second_osid);
    unset($option_set->options[2]);
    $option_set = personalize_option_set_save($option_set);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_RUNNING);

    // Delete another option, it should be paused.
    unset($option_set->options[1]);
    $option_set = personalize_option_set_save($option_set);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_PAUSED);

    // Clear the queue.
    $this->personalizedQueue
      ->deleteQueue();

    // Add the option back and start the campaign again.
    $option_set->options[1] = array(
      'option_id' => 'new-option',
      'option_label' => 'New Option',
    );
    $option_set = personalize_option_set_save($option_set);
    $this
      ->resetAll();
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);

    // Cause the queue to fail based on a bad request.
    AcquiaLiftAPI::setTestInstance(TRUE, TRUE);

    // Run the queue - the 400 error will abort the queue and the handleFailedItem() method
    // should pause the agent.
    $_SESSION['acquia_lift_queue_trigger'] = true;
    acquia_lift_process_queue(FALSE);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_PAUSED);

    // Set it to running again and delete that last remaining option set.
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);
    personalize_option_set_delete($second_osid);
    $this
      ->resetAll();
    $status = personalize_agent_get_status($agent_name);
    $this
      ->assertEqual($status, PERSONALIZE_STATUS_PAUSED);
  }

}
class AcquiaLiftWebTestFundamentals extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Fundamentals'),
      'description' => t('Tests the module\'s underlying machinery.'),
      'group' => t('Personalize'),
    );
  }
  public function testAcquiaLiftQueue() {
    $marketer = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
    ));
    $this
      ->drupalLogin($marketer);

    // Create a new agent via the UI.
    $agent = $this
      ->createTestAgent(array(), FALSE);
    $agent_name = $agent
      ->getTitle();
    $machine_name = $agent
      ->getMachineName();

    // There should now be a js setting for triggering queue processing.
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual(1, $settings['acquia_lift']['sync_queue']);
    $expected_queue_items = array();
    $queued_agent_item = array(
      'method' => 'saveAgent',
      'args' => array(
        $machine_name,
        $agent_name,
        'adaptive',
        PERSONALIZE_STATUS_NOT_STARTED,
        0.1,
        0.2,
        1,
      ),
      'agent' => $machine_name,
    );
    $expected_queue_items[] = $queued_agent_item;
    $this
      ->assertQueueItems($expected_queue_items);

    // Now cause the queue to be processed, which would normally happen
    // via an ajax request.
    $this
      ->drupalGet('acquia_lift/queue');

    // Confirm that the queue is now empty.
    $this
      ->assertQueueItems(array());
    $this
      ->drupalGet('admin/structure/personalize');
    $this
      ->assertNoText(t('At least one of your campaigns has configuration that has not been fully synchronized with Acquia Lift. This should resolve itself on the next cron run.'));

    // Now save it again but don't process the queue.
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", array(), $this
      ->getButton('agent'));

    // Save a goal for the agent.
    $goal_name = 'form_submit';
    personalize_goal_save($machine_name, $goal_name, 2);
    $expected_queue_items = array();
    $expected_queue_items[] = $queued_agent_item;
    $expected_queue_items[] = array(
      'method' => 'saveGoal',
      'args' => array(
        $machine_name,
        $goal_name,
      ),
      'agent' => $machine_name,
    );
    $expected_queue_items[] = $queued_agent_item;
    $this
      ->assertQueueItems($expected_queue_items);

    // Since we can't simulate the queued requests timing out during a web
    // test, we simply unset the queue trigger session without processing
    // the queue.
    $this
      ->assertTrue(isset($_SESSION['acquia_lift_queue_trigger']));

    // Log the user out and back in again to get rid of the session variable.
    $this
      ->drupalLogout();
    $this
      ->drupalLogin($marketer);

    // Now they should get a message warning them that there are items that
    // need to get sync'd to Acquia Lift.
    $this
      ->drupalGet('admin/structure/personalize');
    $this
      ->assertText(t('At least one of your campaigns has configuration that has not been fully synchronized with Acquia Lift. This should resolve itself on the next cron run.'));
    $this
      ->drupalLogout();
    $admin_user = $this
      ->drupalCreateUser(array(
      'administer site configuration',
      'access administration pages',
      'manage personalized content',
    ));
    $this
      ->drupalLogin($admin_user);
    $this
      ->drupalGet('admin/structure/personalize');
    $this
      ->assertRaw(t('At least one of your campaigns has configuration that has not been fully synchronized with Acquia Lift. This should resolve itself on the next cron run.') . t(' Click here to <a href="@cron">run cron manually</a>.', array(
      '@cron' => url('admin/reports/status/run-cron'),
    )));

    // Now run cron
    $this
      ->drupalGet('admin/reports/status/run-cron');
    $this
      ->assertQueueItems(array());

    // Now save the agent again.
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", array(), $this
      ->getButton('agent'));
    $expected_queue_items = array(
      $queued_agent_item,
    );
    $this
      ->assertQueueItems($expected_queue_items);

    // Cause the queue to fail.
    AcquiaLiftAPI::setTestInstance(TRUE);

    // Run the queue.
    $_SESSION['acquia_lift_queue_trigger'] = true;
    acquia_lift_process_queue(FALSE);
    $expected_logs = array();
    for ($i = 0; $i <= AcquiaLiftQueue::MAX_RETRIES; $i++) {
      $expected_logs[] = array(
        'level' => 'error',
        'message' => "The campaign {$machine_name} could not be pushed to Acquia Lift",
      );
    }
    $test_logger = new AcquiaLiftTestLogger(FALSE);
    $logs = $test_logger
      ->getLogs();
    $this
      ->assertEqual($expected_logs, $logs);

    // Item should be removed after final attempt.
    $this
      ->assertQueueItems(array());
    $test_logger
      ->clearLogs();

    // Edit the agent again.
    $edit = array(
      'cache_decisions' => FALSE,
    );
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", $edit, $this
      ->getButton('agent'));
    personalize_agent_set_status($machine_name, PERSONALIZE_STATUS_PAUSED);
    $this
      ->resetAll();
    $queued_agent_item['args'][6] = FALSE;
    $expected_queue_items = array(
      $queued_agent_item,
    );
    $expected_queue_items[] = array(
      'method' => 'updateAgentStatus',
      'args' => array(
        $machine_name,
        PERSONALIZE_STATUS_PAUSED,
      ),
      'agent' => $machine_name,
    );
    $this
      ->assertQueueItems($expected_queue_items);
    $this
      ->resetAll();

    // Cause the queue to fail based on a bad request. There should not be any retries,
    // it should just be aborted.
    AcquiaLiftAPI::setTestInstance(TRUE, TRUE);

    // 2nd param specifies simulating client-side error, i.e. 400 error.
    // Run the queue.
    $_SESSION['acquia_lift_queue_trigger'] = true;
    acquia_lift_process_queue(FALSE);
    $expected_logs = array();
    $expected_logs[] = array(
      'level' => 'error',
      'message' => "The campaign {$machine_name} could not be pushed to Acquia Lift",
    );
    $logs = $test_logger
      ->getLogs();
    $this
      ->assertEqual($expected_logs, $logs);

    // Item should be removed after final attempt.
    $this
      ->assertQueueItems(array());
  }

  /**
   * Tests the logic in AcquiaLiftAgent's implementation of convertContextToFeatureString().
   */
  public function testConvertContextToFeatureString() {

    // No truncation should happen if name and value are short enough.
    $name = $this
      ->randomName(20);
    $value = $this
      ->randomName(20);
    $string = AcquiaLiftAgent::convertContextToFeatureString($name, $value);
    $expected = $name . '::' . $value;
    $this
      ->assertEqual($expected, $string);
    $string = AcquiaLiftAgent::convertContextToFeatureString($name, $value, TRUE);
    $expected = $name . ':' . $value;
    $this
      ->assertEqual($expected, $string);

    // If we have a really long name and value test that they are truncated
    // correctly.
    $long_name = $this
      ->randomName(40);
    $long_value = $this
      ->randomName(40);
    $string = AcquiaLiftAgent::convertContextToFeatureString($long_name, $long_value);
    $expected = substr($long_name, 0, 24) . '::' . substr($long_value, 0, 24);
    $this
      ->assertEqual($expected, $string);
    $string = AcquiaLiftAgent::convertContextToFeatureString($long_name, $long_value, TRUE);
    $expected = substr($long_name, 0, 24) . ':' . substr($long_value, 0, 25);
    $this
      ->assertEqual($expected, $string);
  }

  /**
   * Tests that new agents on the site cannot override existing agents in Lift.
   */
  public function testMachineNameValidation() {
    $test_data = array(
      'agents' => array(
        array(
          'code' => 'my-existing-agent',
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);

    // Try to create a new agent with the same name as one that exists in Lift.
    $this
      ->createTestAgent(array(
      'name' => 'my-existing-agent',
      'control_rate' => 10,
      'explore_rate' => 30,
    ), TRUE, FALSE);
    $this
      ->resetAll();
    $agent = personalize_agent_load_agent('my-existing-agent');
    $this
      ->assertNull($agent);
    $agent = personalize_agent_load_agent('my-existing-agent-0');
    $this
      ->assertTrue($agent instanceof AcquiaLiftAgentInterface);
  }

  /**
   * Tests the function that gathers operations for syncing of agents and their
   * components to Lift.
   */
  public function testBatchSyncOperations() {

    // Create an agent on the site with a couple of option sets.
    // Note: we do this part first rather than loading the test data to
    // the server because otherwise it wouldn't allow us to create an
    // agent with the same name as an existing one.
    $control_rate = 10;
    $explore_rate = 30;
    $this
      ->createTestAgent(array(
      'name' => 'first-existing-agent',
      'control_rate' => $control_rate,
      'explore_rate' => $explore_rate,
    ));
    $option_set_values = array(
      array(
        'agent' => 'first-existing-agent',
        'plugin' => 'type1',
        'option_ids' => array(
          'option-1',
          'option-2',
        ),
      ),
      array(
        'agent' => 'first-existing-agent',
        'plugin' => 'type2',
        'option_ids' => array(
          'option-a',
          'option-b',
          'option-c',
        ),
      ),
    );
    $option_sets = array();
    foreach ($option_set_values as $i => $values) {
      list($option_set, $new_queue_items) = $this
        ->createOptionSet($i, $values);
      $option_sets[] = $option_set;
    }
    $first_osid = 'osid-' . $option_sets[0]->osid;
    $second_osid = 'osid-' . $option_sets[1]->osid;

    // Add a goal.
    personalize_goal_save('first-existing-agent', 'new-goal', 2);
    $this
      ->resetAll();

    // Set up some test data to be returned by the dummy http client.
    $test_data = array(
      'agents' => array(
        array(
          'code' => 'first-existing-agent',
        ),
      ),
      'points' => array(
        'first-existing-agent' => array(
          // The agent on the Drupal site will have this point.
          $first_osid,
          // The agent on the Drupal site will not have this point so it
          // should get deleted upon sync.
          'second-point',
        ),
      ),
      'decisions' => array(
        'first-existing-agent' => array(
          $first_osid => array(
            $first_osid,
            // This decision should get deleted upon sync.
            'another-decision',
          ),
        ),
      ),
      'choices' => array(
        'first-existing-agent' => array(
          $first_osid => array(
            $first_osid => array(
              'option-1',
              'option-2',
              // These choices should get deleted upon sync.
              'option-3',
              'last-option',
            ),
          ),
        ),
      ),
      'goals' => array(
        'first-existing-agent' => array(
          // This goal should get deleted upon sync.
          'first-goal',
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);

    // Now try syncing this agent to Lift.
    $agents = personalize_agent_load_multiple(array(
      'first-existing-agent',
    ));

    // Ensure the test API client is used during the call for operations.
    AcquiaLiftAPI::setTestInstance();
    module_load_include('inc', 'acquia_lift', 'acquia_lift.batch');

    // Confirm that the correct operations will be performed.
    $operations = acquia_lift_get_sync_operations_for_agents($agents);
    $expected_operations = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          'first-existing-agent',
          'first-existing-agent',
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          $control_rate / 100,
          $explore_rate / 100,
          TRUE,
        ),
      ),
      // PUT requests for all the points, decisions and choices that exist
      // in the agent being sync'd, regardless of whether they already exist
      // in Lift.
      array(
        'method' => 'savePoint',
        'args' => array(
          'first-existing-agent',
          $first_osid,
        ),
      ),
      array(
        'method' => 'saveDecision',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          $first_osid,
        ),
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          $first_osid,
          'option-1',
        ),
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          $first_osid,
          'option-2',
        ),
      ),
      array(
        'method' => 'savePoint',
        'args' => array(
          'first-existing-agent',
          $second_osid,
        ),
      ),
      array(
        'method' => 'saveDecision',
        'args' => array(
          'first-existing-agent',
          $second_osid,
          $second_osid,
        ),
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          'first-existing-agent',
          $second_osid,
          $second_osid,
          'option-a',
        ),
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          'first-existing-agent',
          $second_osid,
          $second_osid,
          'option-b',
        ),
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          'first-existing-agent',
          $second_osid,
          $second_osid,
          'option-c',
        ),
      ),
      // DELETE request for non-existent choices.
      array(
        'method' => 'deleteChoice',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          $first_osid,
          'option-3',
        ),
      ),
      array(
        'method' => 'deleteChoice',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          $first_osid,
          'last-option',
        ),
      ),
      // DELETE request for non-existent decision.
      array(
        'method' => 'deleteDecision',
        'args' => array(
          'first-existing-agent',
          $first_osid,
          'another-decision',
        ),
      ),
      // DELETE request for non-existent decision point.
      array(
        'method' => 'deletePoint',
        'args' => array(
          'first-existing-agent',
          'second-point',
        ),
      ),
      // PUT request for the new goal
      array(
        'method' => 'saveGoal',
        'args' => array(
          'first-existing-agent',
          'new-goal',
        ),
      ),
      // DELETE request for the goal that exists in Lift but not in the agent
      // being sync'd.
      array(
        'method' => 'deleteGoal',
        'args' => array(
          'first-existing-agent',
          'first-goal',
        ),
      ),
    );
    $this
      ->assertEqual($expected_operations, $operations);
  }

}
class AcquiaLiftWebTestReports extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Reporting'),
      'description' => t('Tests the reporting functionality for Acquia Lift.'),
      'group' => t('Personalize'),
    );
  }
  public function testConversionReportAjaxEndpoint() {
    $this
      ->drupalLogin($this->managerUser);
    $agent = $this
      ->createTestAgent(array(), TRUE, FALSE);
    $agent_name = $agent
      ->getMachineName();

    // Set the agent to read report data from the test file.
    $path = drupal_get_path('module', 'acquia_lift');
    variable_set("acquia_lift_report_source_" . $agent_name, "/" . $path . '/tests/test_reports.json');

    // Call the conversion report endpoint.
    $first_osid = 'osid-1';
    $report = $this
      ->drupalGet('acquia_lift/reports/conversion', array(
      'query' => array(
        'campaign' => $agent_name,
        'decision' => $first_osid,
      ),
    ));
    $report = drupal_json_decode($report);
    $pattern = '/class="lift-statistic-category"/';
    preg_match($pattern, $report, $matches);
    $this
      ->assertEqual(count($matches), 1, 'Only one category of results returned.');
    $pattern = '/(<table)/';
    preg_match($pattern, $report, $matches);
    $this
      ->assertEqual(count($matches), 2, 'Only two tables returned.');
    $pattern = '/data-acquia-lift-campaign=\\"([a-z0-9\\_\\-]+)\\"/';
    preg_match($pattern, $report, $matches);
    $this
      ->assertEqual($matches[1], $agent_name, 'Campaign name was passed in tabular data.');
    $pattern = '/data-acquia-lift-decision-name=\\"([a-z0-9\\_\\-]+)\\"/';
    preg_match($pattern, $report, $matches);
    $this
      ->assertEqual($matches[1], $first_osid, 'Decision name was passed in tabular data.');
  }
  public function testReadReportsFromFile() {
    $this
      ->drupalLogin($this->managerUser);
    $first_agent = 'new-test-agent';
    $second_agent = 'second-test-agent';
    $control_rate = 10;
    $explore_rate = 30;
    $this
      ->createTestAgent(array(
      'name' => $first_agent,
      'control_rate' => $control_rate,
      'explore_rate' => $explore_rate,
    ));

    // Our second agent will continu to read reports from the API when our first switches
    // to reading from a file.
    $this
      ->createTestAgent(array(
      'name' => $second_agent,
      'control_rate' => $control_rate,
      'explore_rate' => $explore_rate,
    ));
    $test_data = array();

    // Set each agent up with an option set with 2 options, and a goal, and set it to
    // running.
    $option_sets = array();
    foreach (array(
      $first_agent,
      $second_agent,
    ) as $i => $agent_name) {
      list($option_set, $new_queue_items) = $this
        ->createOptionSet($i, array(
        'agent' => $agent_name,
        'plugin' => 'type1',
        'option_ids' => array(
          'option-1',
          'option-2',
        ),
      ));
      $option_sets[] = $option_set;
      $osid = $option_set->osid;

      // Add a goal.
      personalize_goal_save($agent_name, 'new-goal', 1);

      // We need to bypass the personalize_agent_set_status() function because it
      // does the verification check, which would fail.
      variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);

      // Set the started time of the agent to now.
      $agent = personalize_agent_load($agent_name);
      $started = time();
      $agent->started = $started;
      personalize_agent_save($agent);
      $report_start_date = 1407499650;

      // Test to ensure that an initial report at this point returns no data.
      AcquiaLiftAPI::setTestInstance();
      $agent_instance = personalize_agent_load_agent($first_agent);
      $report = $agent_instance
        ->buildCampaignReports(array(
        'decision' => 'osid-' . $option_sets[0]->osid,
        'start' => $report_start_date,
      ));
      $this
        ->assertFalse($report['#has_data'], 'Report has no data.');

      // Add some test data so that calls to get reports for this agent will
      // return basic reports.
      $test_data = array_merge_recursive($test_data, array(
        // We need to replicate this test agent on the server so that the errors()
        // check will pass.
        'agents' => array(
          array(
            'code' => $agent_name,
          ),
        ),
        'points' => array(
          $agent_name => array(
            $osid,
          ),
        ),
        'decisions' => array(
          $agent_name => array(
            $osid => array(
              $osid,
            ),
          ),
        ),
        'choices' => array(
          $agent_name => array(
            $osid => array(
              $osid => array(
                'option-1',
                'option-2',
              ),
            ),
          ),
        ),
        'goals' => array(
          $agent_name => array(
            // This goal should get deleted upon sync.
            'new-goal',
          ),
        ),
        // Now add the reports.
        'reports' => array(
          $agent_name => array(
            'confidence' => AcquiaLiftTestReports::getBasicConfidenceReport($agent_name),
            'targeting-features' => AcquiaLiftTestReports::getBasicTargetingReport($agent_name),
            'learning' => array(),
            'agent-status' => AcquiaLiftTestReports::getBasicStatusReport($agent_name),
            'context-filters' => AcquiaLiftTestReports::getBasicContextFilters(),
          ),
        ),
      ));
    }
    variable_set('acquia_lift_web_test_data', $test_data);
    $this
      ->resetAll();
    AcquiaLiftAPI::setTestInstance();
    $agent_instance = personalize_agent_load_agent($first_agent);

    // We use the date that the test report was created.
    $report = $agent_instance
      ->buildCampaignReports(array(
      'decision' => 'osid-' . $option_sets[0]->osid,
      'start' => $report_start_date,
    ));
    $this
      ->assertFalse($report['#has_data'], 'Empty reports have no data to show.');

    // The experiment report should show that Option A has a 0% conversion rate.
    $this
      ->assertEqual("Option A", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data']);
    $this
      ->assertEqual("Control", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data-acquia-lift-variation-label']);
    $this
      ->assertEqual("0%", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][2]['data']);

    // The report for the second agent will be identical
    $agent_instance = personalize_agent_load_agent($second_agent);
    $report = $agent_instance
      ->buildCampaignReports(array(
      'decision' => 'osid-' . $option_sets[0]->osid,
      'start' => $report_start_date,
    ));
    $this
      ->assertEqual("Option A", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data']);
    $this
      ->assertEqual("Control", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data-acquia-lift-variation-label']);
    $this
      ->assertEqual("0%", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][2]['data']);
    $agent_instance = NULL;
    $this
      ->resetAll();

    // Now set the first agent to use a report from a file.
    $path = drupal_get_path('module', 'acquia_lift');
    variable_set("acquia_lift_report_source_{$first_agent}", "/" . $path . '/tests/test_reports.json');
    $agent_instance = personalize_agent_load_agent($first_agent);
    $report = $agent_instance
      ->buildCampaignReports(array(
      'decision' => 'osid-' . $option_sets[0]->osid,
      'start' => $report_start_date,
    ));
    $this
      ->assertTrue($report['#has_data'], 'Report data file indicates data to show.');

    // Now the experiment report should show that Option A has a conversion rate of .19%
    $this
      ->assertEqual("osid-76:Option A", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data']);
    $this
      ->assertEqual("Control", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data-acquia-lift-variation-label']);
    $this
      ->assertEqual("0.19%", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][2]['data']);

    // The "Winner" flag should appear in the third option's last column, but
    // nowhere else.
    $this
      ->assertEqual('<span class="lift-winner">Winner</span>', $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][2]['data'][5]);
    foreach (array(
      0,
      1,
      3,
      4,
    ) as $variation_index) {
      $this
        ->assertEqual('', $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][$variation_index]['data'][5]);
    }

    // The report for the second agent should still show the basic report data.
    $agent_instance = personalize_agent_load_agent($second_agent);
    $report = $agent_instance
      ->buildCampaignReports(array(
      'decision' => 'osid-' . $option_sets[0]->osid,
      'start' => $report_start_date,
    ));
    $this
      ->assertFalse($report['#has_data'], 'Empty report data still indicates no data to show.');
    $this
      ->assertEqual("Option A", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data']);
    $this
      ->assertEqual("Control", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][0]['data-acquia-lift-variation-label']);
    $this
      ->assertEqual("0%", $report['experiment']['reports']['conversion']['reports']['summary']['summary_holder']['summary_table']['#rows'][0]['data'][2]['data']);
  }
  function testReportEndDate() {
    $this
      ->drupalLogin($this->managerUser);
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');
    $agent_name = 'new-test-agent';
    $control_rate = 10;
    $explore_rate = 30;
    $this
      ->createTestAgent(array(
      'name' => $agent_name,
      'control_rate' => $control_rate,
      'explore_rate' => $explore_rate,
    ));

    // Set the agent up with an option set with 2 options, and a goal, and set it to
    // running.
    list($option_set, $new_queue_items) = $this
      ->createOptionSet(0, array(
      'agent' => $agent_name,
      'plugin' => 'type1',
      'option_ids' => array(
        'option-1',
        'option-2',
      ),
    ));
    $osid = $option_set->osid;

    // Add a goal.
    personalize_goal_save($agent_name, 'new-goal', 1);

    // We need to bypass the personalize_agent_set_status() function because it
    // does the verification check, which would fail.
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);

    // Set the started time of the agent to 48 hours ago.
    $agent = personalize_agent_load($agent_name);
    $started = time() - 48 * 60 * 60;
    $agent->started = $started;
    personalize_agent_save($agent);

    // We need to replicate this test agent on the server so that the errors()
    // check will pass.
    $test_data = array(
      'agents' => array(
        array(
          'code' => $agent_name,
        ),
      ),
      'points' => array(
        $agent_name => array(
          $osid,
        ),
      ),
      'decisions' => array(
        $agent_name => array(
          $osid => array(
            $osid,
          ),
        ),
      ),
      'choices' => array(
        $agent_name => array(
          $osid => array(
            $osid => array(
              'option-1',
              'option-2',
            ),
          ),
        ),
      ),
      'goals' => array(
        $agent_name => array(
          // This goal should get deleted upon sync.
          'new-goal',
        ),
      ),
      'reports' => array(
        $agent_name => array(
          'confidence' => AcquiaLiftTestReports::getBasicConfidenceReport($agent_name),
          'targeting-features' => AcquiaLiftTestReports::getBasicTargetingReport($agent_name),
          'learning' => array(),
          'agent-status' => AcquiaLiftTestReports::getBasicStatusReport($agent_name),
          'context-filters' => AcquiaLiftTestReports::getBasicContextFilters(),
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);
    $this
      ->resetAll();
    DummyAcquiaLiftHttpClient::clearLoggedRequests();
    AcquiaLiftAPI::setTestInstance();
    $agent_instance = personalize_agent_load_agent($agent_name);
    $form_state = array();
    $agent_data = personalize_agent_load($agent_name);

    // We call the report generating function and then check the request made to the
    // Lift API.
    acquia_lift_report_custom(array(), $form_state, $agent_instance, $agent_data, $option_set);
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $start_date = date('Y-m-d', $started);
    $now = time();
    $end_date = date('Y-m-d', $now);
    $confidence_report_uri = $requests[0]['uri'];
    $pattern = "/http\\:\\/\\/api\\.example\\.com\\/test\\-owner\\-code\\/{$agent_name}\\/report\\/confidence\\/{$start_date}\\/(\\d{4}\\-\\d{2}\\-\\d{2})/";
    $matches = array();
    preg_match($pattern, $confidence_report_uri, $matches);
    $this
      ->assertEqual($end_date, $matches[1]);

    // Now set the campaign to completed
    personalize_agent_set_status($agent_name, PERSONALIZE_STATUS_COMPLETED);

    // Specify the completed time as yesterday.
    $yesterday = $now - 24 * 60 * 60;
    variable_set(_personalize_agent_get_stoptime_variable($agent_name), $yesterday);
    $this
      ->resetAll();

    // Clear the logged requests.
    DummyAcquiaLiftHttpClient::clearLoggedRequests();

    // Again call the report generating function and then check the request made to the
    // Lift API. The end date should be different
    acquia_lift_report_custom(array(), $form_state, $agent_instance, $agent_data, $option_set);
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $start_date = date('Y-m-d', $started);
    $confidence_report_uri = $requests[0]['uri'];
    $pattern = "/http\\:\\/\\/api\\.example\\.com\\/test\\-owner\\-code\\/{$agent_name}\\/report\\/confidence\\/{$start_date}\\/(\\d{4}\\-\\d{2}\\-\\d{2})/";
    $matches = array();
    preg_match($pattern, $confidence_report_uri, $matches);
    $this
      ->assertEqual(date('Y-m-d', $yesterday), $matches[1]);
  }
  function testPerGoalReport() {
    AcquiaLiftAPI::setTestInstance();
    $agent = $this
      ->createTestAgent();
    $this
      ->resetAll();
    DummyAcquiaLiftHttpClient::clearLoggedRequests();

    // Call the conversion report specifying a particular goal.
    $agent
      ->buildConversionReport(array(
      'goal' => 'user_login',
    ));
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $this
      ->assertEqual(2, count($requests));
    $agent_name = $agent
      ->getMachineName();
    $date = date('Y-m-d');

    // Confirm that the report requests made include the goal in the parameters.
    $summary_url = "http://api.example.com/test-owner-code/{$agent_name}/report/confidence/{$date}/{$date}?features=&apikey=test-admin-key&confidence-measure=0.95&aggregated-over-dates=true&goal=user_login&policies=explore";
    $detail_url = "http://api.example.com/test-owner-code/{$agent_name}/report/confidence/{$date}/{$date}?features=&apikey=test-admin-key&confidence-measure=0.95&aggregated-over-dates=false&goal=user_login&policies=explore";
    $this
      ->assertEqual($summary_url, $requests[0]['uri']);
    $this
      ->assertEqual($detail_url, $requests[1]['uri']);
  }
  public function testReportDates() {
    $this
      ->drupalLogin($this->managerUser);
    AcquiaLiftAPI::setTestInstance();
    $agents = array(
      'first-agent' => array(
        'num_options' => 2,
        'num_days' => 12,
      ),
      'second-agent' => array(
        'num_options' => 2,
        'num_days' => 30,
      ),
    );
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');
    $now = time();
    foreach ($agents as $agent_name => &$agent_info) {
      $agent_instance = $this
        ->createTestAgent(array(
        'name' => $agent_name,
      ), TRUE, FALSE);
      $agent_name = $agent_instance
        ->getMachineName();
      $this
        ->createOptionSet(0, array(
        'plugin' => 'some_plugin',
        'agent' => $agent_name,
        'num_options' => $agent_info['num_options'],
      ));
      personalize_goal_save($agent_name, 'user_login', 1);
      variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);
      $agent = personalize_agent_load($agent_name);
      $started = $now;
      $agent->started = $started - $agent_info['num_days'] * 86400;
      personalize_agent_save($agent);
      $agent_info['report_dates'] = acquia_lift_get_report_dates_for_agent($agent);
    }

    // Now check that the report dates are properly adjusted for each agent.
    // The first agent should show the complete history, i.e. 12 days.
    $this
      ->assertEqual(array(
      date('Y-m-d', $now - 12 * 86400),
      date('Y-m-d', $now),
    ), $agents['first-agent']['report_dates']);

    // The second agent has been runnign for 60 days but should only show 14.
    $this
      ->assertEqual(array(
      date('Y-m-d', $now - 14 * 86400),
      date('Y-m-d', $now),
    ), $agents['second-agent']['report_dates']);

    // Now set the max days to 30 instead of 14.
    variable_set('acquia_lift_report_max_days', 30);
    $this
      ->resetAll();
    foreach ($agents as $agent_name => &$agent_info) {
      $agent = personalize_agent_load($agent_name);
      $agent_info['report_dates'] = acquia_lift_get_report_dates_for_agent($agent);
    }

    // The first agent should still show the complete history, i.e. 12 days.
    $this
      ->assertEqual(array(
      date('Y-m-d', $now - 12 * 86400),
      date('Y-m-d', $now),
    ), $agents['first-agent']['report_dates']);

    // The second agent should now also show the complete history, i.e. 30 days.
    $this
      ->assertEqual(array(
      date('Y-m-d', $now - 30 * 86400),
      date('Y-m-d', $now),
    ), $agents['second-agent']['report_dates']);
  }

}
class AcquiaLiftWebTestWorkflow extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Workflows'),
      'description' => t('Tests functionality related to particular campaign workflows.'),
      'group' => t('Personalize'),
    );
  }

  /**
   * Tests automatic creation of a goal for personalized fields and auto-
   * starting of the campaign.
   */
  public function testPersonalizeFieldsAutoCreateGoal() {
    $this
      ->resetAll();
    module_enable(array(
      'personalize_fields',
    ));
    $this
      ->resetAll();
    $admin_user = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'administer site configuration',
      'access content',
      'administer content types',
      'administer nodes',
      'bypass node access',
      'manage personalized content',
    ));
    $this
      ->drupalLogin($admin_user);

    // Add personalizable field to the article node type.
    $field = array(
      'type' => 'text',
      'field_name' => 'article_headline',
      'cardinality' => -1,
      'settings' => array(
        'personalize' => array(
          'enabled' => 1,
          'agent_type' => 'acquia_lift',
          'options' => array(
            'acquia_lift' => array(
              'control_rate' => 10,
              'decision_style' => 'adaptive',
              'explore_rate' => 25,
            ),
          ),
          'create_goal' => 1,
          'auto_start' => 1,
        ),
      ),
    );
    field_create_field($field);
    $instance = array(
      'field_name' => 'article_headline',
      'entity_type' => 'node',
      'label' => 'Personalizable Headline',
      'bundle' => 'article',
      'required' => FALSE,
    );
    field_create_instance($instance);
    list($node1, $os1, $agent_name) = $this
      ->createPersonalizedField();
    $first_osid = 'osid-' . $os1->osid;
    $goals = personalize_goal_load_by_conditions(array(
      'agent' => $agent_name,
    ));
    $this
      ->assertEqual(1, count($goals));
    $goal = reset($goals);
    $action = visitor_actions_custom_load($goal->action);
    $this
      ->assertEqual('click', $action['event']);
    $this
      ->assertEqual('[data-personalize=osid-1]', $action['identifier']);
    $action_name = personalize_generate_machine_name(t('Clicks @option_set', array(
      '@option_set' => $os1->label,
    )), NULL, '_');
    $this
      ->assertEqual($action_name, $action['machine_name']);
    $this
      ->assertTrue($action['limited_use']);
    $expected_queue_items = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $agent_name,
          'Article: Personalizable Headline 1',
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          10 / 100,
          25 / 100,
          true,
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'savePoint',
        'args' => array(
          $agent_name,
          $first_osid,
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'saveDecision',
        'args' => array(
          $agent_name,
          $first_osid,
          $first_osid,
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          $agent_name,
          $first_osid,
          $first_osid,
          'first-value',
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          $agent_name,
          $first_osid,
          $first_osid,
          'second-value',
        ),
        'agent' => $agent_name,
      ),
      array(
        'method' => 'saveGoal',
        'args' => array(
          $agent_name,
          $action_name,
        ),
        'agent' => $agent_name,
      ),
      array(
        'callback' => 'personalize_agent_set_status',
        'args' => array(
          $agent_name,
          PERSONALIZE_STATUS_RUNNING,
        ),
      ),
    );
    $this
      ->assertQueueItems($expected_queue_items);

    // Manually set the agent's status to running, bypassing the verification check.
    variable_set(_personalize_agent_get_status_variable($agent_name), PERSONALIZE_STATUS_RUNNING);
    $this
      ->resetAll();
    $expected_action_listeners = array(
      $action_name => array(
        array(
          'agent' => $agent_name,
          'value' => 1,
        ),
      ),
    );

    // Confirm that the goal is in the js settings on the /user page.
    $this
      ->drupalGet('user');
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($expected_action_listeners, $settings['personalize']['actionListeners']);

    // Change the field settings to specify particular pages for the goal.
    $edit = array(
      'field[settings][personalize][goal_pages]' => "node",
    );
    $this
      ->drupalPost('admin/structure/types/manage/article/fields/article_headline', $edit, t('Save settings'));
    $this
      ->resetAll();

    // Create another personalized field on a new node.
    list($node2, $os2, $second_agent) = $this
      ->createPersonalizedField();

    // Manually set the agent's status to running, bypassing the verification check.
    variable_set(_personalize_agent_get_status_variable($second_agent), PERSONALIZE_STATUS_RUNNING);
    $this
      ->resetAll();
    $goals = personalize_goal_load_by_conditions(array(
      'agent' => $second_agent,
    ));
    $this
      ->assertEqual(1, count($goals));
    $goal = reset($goals);

    // Confirm that the goal is *not* in the js settings on the user page but is
    // on the node page.
    $this
      ->drupalGet('user');
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertFalse(isset($settings['personalize']['actionListeners'][$goal->action]));
    $this
      ->drupalGet('node');
    $settings = $this
      ->drupalGetSettings();
    $expected_action_listeners[$goal->action] = array(
      array(
        'agent' => $second_agent,
        'value' => 1,
      ),
    );
    $this
      ->assertEqual($expected_action_listeners, $settings['personalize']['actionListeners']);
    $this->personalizedQueue
      ->deleteQueue();

    // Now change the field settings to not auto-create a goal.
    $edit = array(
      'field[settings][personalize][create_goal]' => FALSE,
    );
    $this
      ->drupalPost('admin/structure/types/manage/article/fields/article_headline', $edit, t('Save settings'));
    list($node3, $os3, $third_agent) = $this
      ->createPersonalizedField();
    $third_osid = 'osid-' . $os3->osid;
    $this
      ->resetAll();

    // Confirm that our agent and option set were created.
    $option_set = personalize_option_set_load($os3->osid);
    $this
      ->assertEqual($third_agent, $option_set->agent);

    // There should be no goal for this agent.
    $goals = personalize_goal_load_by_conditions(array(
      'agent' => $third_agent,
    ));
    $this
      ->assertTrue(empty($goals));

    // We should only have items in the queue for saving the agent and the
    // option set.
    $expected_queue_items = array(
      array(
        'method' => 'saveAgent',
        'args' => array(
          $third_agent,
          'Article: Personalizable Headline 3',
          'adaptive',
          PERSONALIZE_STATUS_NOT_STARTED,
          10 / 100,
          25 / 100,
          true,
        ),
        'agent' => $third_agent,
      ),
      array(
        'method' => 'savePoint',
        'args' => array(
          $third_agent,
          $third_osid,
        ),
        'agent' => $third_agent,
      ),
      array(
        'method' => 'saveDecision',
        'args' => array(
          $third_agent,
          $third_osid,
          $third_osid,
        ),
        'agent' => $third_agent,
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          $third_agent,
          $third_osid,
          $third_osid,
          'first-value',
        ),
        'agent' => $third_agent,
      ),
      array(
        'method' => 'saveChoice',
        'args' => array(
          $third_agent,
          $third_osid,
          $third_osid,
          'second-value',
        ),
        'agent' => $third_agent,
      ),
    );
    $this
      ->assertQueueItems($expected_queue_items);
  }

  /**
   * Tests the logic of AcquiaLiftAgent's stopNow() implementation in the
   * simplest use case.
   */
  function testAutoStop() {

    // Create a new agent via the UI.
    $agent = $this
      ->createTestAgent(array(
      'control_rate' => 10,
      'explore_rate' => 30,
      'auto_stop' => 1,
    ), TRUE, FALSE);
    $machine_name = $agent
      ->getMachineName();
    $agent_data = personalize_agent_load($machine_name, TRUE);

    // Set the start time of the agent to 2 minutes ago.
    $agent_data->started = time() - 120;

    // Most basic case - a single option set with 2 options.
    list($option_set, $new_queue_items) = $this
      ->createOptionSet(0, array(
      'agent' => $machine_name,
      'plugin' => 'type1',
      'num_options' => 3,
    ));
    $osid = 'osid-' . $option_set->osid;

    // Generate the reporting data to be returned by the dummy http client for this
    // agent.
    $points = array(
      $osid => array(
        $osid . ':option-A',
        $osid . ':option-B',
        $osid . ':option-C',
      ),
    );
    $test_data = array(
      'reports' => array(
        $machine_name => array(
          // Generate a fake confidence report with no clear winner.
          'confidence' => AcquiaLiftTestReports::generateConfidenceReportWithWinners($machine_name, $points),
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);

    // Ensure no other criteria are used to determine whether we should stop.
    variable_set('acquia_lift_min_decisions', 0);
    variable_set('acquia_lift_min_runtime_num', 0);
    $this
      ->resetAll();

    // Make sure we are using a test instance of the API client.
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent
      ->stopNow();

    // The call should return FALSE as neither minimum runtime nor minimum decisions
    // has been configured as a basis on which to stop.
    $this
      ->assertFalse($stop);

    // No winner should have been set on the option set.
    $option_set = personalize_option_set_load(1, TRUE);
    $this
      ->assertNull($option_set->winner);

    // Set min decisions to 30 and we should get option C set as the winner (just
    // because the test data is set up to give higher values to later options
    // if not otherwise specified.)
    variable_set('acquia_lift_min_decisions', 30);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);

    // Confirm the winner has been set on the option set.
    $option_set = personalize_option_set_load($option_set->osid, TRUE);
    $this
      ->assertEqual('option-C', $option_set->winner);

    // Now generate a report where option B has the highest estimated value.
    $test_data = array(
      'reports' => array(
        $machine_name => array(
          'confidence' => AcquiaLiftTestReports::generateConfidenceReportWithWinners($machine_name, $points, array(
            $osid => $osid . ':option-B',
          )),
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);
    $this
      ->resetAll();
    AcquiaLiftAPI::setTestInstance();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);

    // Confirm the winner has been set on the option set.
    $option_set = personalize_option_set_load($option_set->osid, TRUE);
    $this
      ->assertEqual('option-B', $option_set->winner);

    // Setting the minimum number of decisions to 100 will mean we won't have enough
    // decisions so we should get FALSE.
    variable_set('acquia_lift_min_decisions', 100);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertFalse($stop);

    // Setting it to 30 should get TRUE again.
    variable_set('acquia_lift_min_decisions', 30);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);

    // Setting the minimum runtime to 1 day should give us FALSE.
    variable_set('acquia_lift_min_runtime_num', 1);
    variable_set('acquia_lift_min_runtime_unit', 'day');
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertFalse($stop);

    // Setting the minimum runtime to 1 minute should give us TRUE again.
    variable_set('acquia_lift_min_runtime_unit', 'minute');
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);
  }

  /**
   * Tests the logic of AcquiaLiftAgent's stopNow() implementation when the agent
   * has mulitple decision points including an MVT.
   */
  function testAutoStopMultiplePoints() {

    // Create a new agent.
    $agent = $this
      ->createTestAgent(array(
      'control_rate' => 10,
      'explore_rate' => 30,
      'auto_stop' => 1,
    ), TRUE, FALSE);
    $machine_name = $agent
      ->getMachineName();

    // This time we'll add 3 option sets.
    $option_set_values = array(
      array(
        'agent' => $machine_name,
        'plugin' => 'type1',
        'num_options' => 3,
      ),
      array(
        'agent' => $machine_name,
        'plugin' => 'type2',
        'num_options' => 2,
      ),
      array(
        'agent' => $machine_name,
        'plugin' => 'type2',
        'num_options' => 3,
      ),
    );
    $option_sets = array();
    foreach ($option_set_values as $i => $values) {
      list($option_set, $new_queue_items) = $this
        ->createOptionSet($i, $values);
      $option_sets[] = $option_set;
    }
    $first_osid = 'osid-' . $option_sets[0]->osid;
    $second_osid = 'osid-' . $option_sets[1]->osid;
    $third_osid = 'osid-' . $option_sets[2]->osid;

    // Create an MVT and add two of the option sets to it.
    $mvt_label = $this
      ->randomName();
    $mvt_machine_name = personalize_generate_machine_name($mvt_label, 'personalize_mvt_machine_name_exists');
    $edit = array(
      'mvt[add][mvt_basic_info][label]' => $mvt_machine_name,
      'mvt[add][mvt_basic_info][option_sets][]' => array(
        $option_sets[1]->osid,
        $option_sets[2]->osid,
      ),
    );
    $this
      ->drupalPost("admin/structure/personalize/manage/{$machine_name}/edit", $edit, $this
      ->getButton('mvt'));

    // Generate the reporting data to be returned by the dummy http client for this
    // agent. All possible choices at each decision point need to be accounted for in
    // the report.
    $points = array(
      $first_osid => array(
        $first_osid . ':option-A',
        $first_osid . ':option-B',
        $first_osid . ':option-C',
      ),
      $mvt_machine_name => array(
        $second_osid . ':option-A,' . $third_osid . ':option-A',
        $second_osid . ':option-A,' . $third_osid . ':option-B',
        $second_osid . ':option-A,' . $third_osid . ':option-C',
        $second_osid . ':option-B,' . $third_osid . ':option-A',
        $second_osid . ':option-B,' . $third_osid . ':option-B',
        $second_osid . ':option-B,' . $third_osid . ':option-C',
      ),
    );
    $test_data = array(
      'reports' => array(
        $machine_name => array(
          // We'll specify a winner at each decision point - for option set 1 it will be
          // Option B and for the MVT it will be the combination of Option A and Option C
          // in Option Sets 2 and 3 respectively.
          'confidence' => AcquiaLiftTestReports::generateConfidenceReportWithWinners($machine_name, $points, array(
            $first_osid => $first_osid . ':option-B',
            $mvt_machine_name => $second_osid . ':option-A,' . $third_osid . ':option-C',
          )),
        ),
      ),
    );
    variable_set('acquia_lift_web_test_data', $test_data);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();

    // Set the minimum number of decisions to 30, and the minium runtime to 0 so
    // that we know we'll get passed these two checks.
    variable_set('acquia_lift_min_decisions', 30);
    variable_set('acquia_lift_min_runtime_num', 0);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);

    // CHeck that the winner has been set on each option set.
    $option_sets = personalize_option_set_load_by_agent($machine_name, TRUE);
    $osids = array_keys($option_sets);
    $this
      ->assertEqual('option-B', $option_sets[$osids[0]]->winner);
    $this
      ->assertEqual('option-A', $option_sets[$osids[1]]->winner);
    $this
      ->assertEqual('option-C', $option_sets[$osids[2]]->winner);

    // Setting the minimum number of decisions to 70 will mean the first decision point
    // won't satisfy this so we should get FALSE from teh stopNow() call.
    variable_set('acquia_lift_min_decisions', 70);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertFalse($stop);

    // Setting it to 60 should get TRUE again.
    variable_set('acquia_lift_min_decisions', 60);
    $this
      ->resetAll();
    $agent_instance = personalize_agent_load_agent($machine_name, TRUE);
    AcquiaLiftAPI::setTestInstance();
    $stop = $agent_instance
      ->stopNow();
    $this
      ->assertTrue($stop);
  }

  /**
   * Tests campaign javascript settings.
   */
  function testCampaignSettings() {
    module_enable(array(
      'personalize_test_extra_agent',
    ));
    $this
      ->resetAll();
    $this
      ->resetAll();
    $admin_user = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
    ));
    $this
      ->drupalLogin($admin_user);

    // Create two agents of different types.
    $first_agent_name = $this
      ->randomName();
    $first_agent_machine_name = personalize_generate_machine_name($first_agent_name, 'personalize_agent_machine_name_exists');
    $second_agent_name = $this
      ->randomName();
    $second_agent_machine_name = personalize_generate_machine_name($second_agent_name, 'personalize_agent_machine_name_exists');
    $agents = array(
      array(
        'label' => $first_agent_name,
        'machine_name' => $first_agent_machine_name,
        'agent_type' => 'test_agent',
      ),
      array(
        'label' => $second_agent_name,
        'machine_name' => $second_agent_machine_name,
        'agent_type' => 'test_extra_agent',
      ),
    );
    foreach ($agents as $agent) {
      $edit = array(
        'agent_basic_info[title]' => $agent['label'],
        'agent_basic_info[machine_name]' => $agent['machine_name'],
        'agent_basic_info[agent_type]' => $agent['agent_type'],
      );
      $this
        ->drupalPost('admin/structure/personalize/add', $edit, $this
        ->getButton('agent'));
    }
    $expected = array(
      $first_agent_machine_name => array(
        'name' => $first_agent_machine_name,
        'label' => $first_agent_name,
        'type' => 'test_agent',
        'status' => PERSONALIZE_STATUS_NOT_STARTED,
        'nextStatus' => array(
          'status' => PERSONALIZE_STATUS_RUNNING,
          'text' => t('Start'),
        ),
        'links' => array(
          'edit' => url('admin/structure/personalize/manage/' . $first_agent_machine_name . '/edit'),
          'report' => '',
          'view' => url('admin/structure/personalize/manage/' . $first_agent_machine_name),
        ),
        'supportsGoals' => TRUE,
        'optionSetTypes' => array(),
        'goals' => NULL,
        'verified' => TRUE,
      ),
      $second_agent_machine_name => array(
        'name' => $second_agent_machine_name,
        'label' => $second_agent_name,
        'type' => 'test_extra_agent',
        'status' => PERSONALIZE_STATUS_NOT_STARTED,
        'nextStatus' => array(
          'status' => PERSONALIZE_STATUS_RUNNING,
          'text' => t('Start'),
        ),
        'links' => array(
          'edit' => url('admin/structure/personalize/manage/' . $second_agent_machine_name . '/edit'),
          'report' => '',
          'view' => url('admin/structure/personalize/manage/' . $second_agent_machine_name),
        ),
        'supportsGoals' => FALSE,
        'optionSetTypes' => array(),
        'goals' => NULL,
        'verified' => TRUE,
      ),
    );
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$first_agent_machine_name], $expected[$first_agent_machine_name]);
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$second_agent_machine_name], $expected[$second_agent_machine_name]);

    // Click the "start" button for the first agent.
    $html_id_first_agent = "personalize-change-status-{$first_agent_machine_name}-form";
    $this
      ->drupalPost('admin/structure/personalize', array(), t('Start'), array(), array(), $html_id_first_agent);

    // As soon as the agent is started, we are able to access its reports.
    $expected[$first_agent_machine_name]['links']['report'] = url('admin/structure/personalize/manage/' . $first_agent_machine_name . '/report');
    $expected[$first_agent_machine_name]['status'] = PERSONALIZE_STATUS_RUNNING;
    $expected[$first_agent_machine_name]['nextStatus'] = array(
      'status' => PERSONALIZE_STATUS_PAUSED,
      'text' => t('Pause'),
    );
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$first_agent_machine_name], $expected[$first_agent_machine_name]);
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$second_agent_machine_name], $expected[$second_agent_machine_name]);

    // Add a goal to the agent.
    AcquiaLiftAPI::setTestInstance();
    personalize_goal_save($first_agent_machine_name, 'user_login', 3);
    $this
      ->drupalGet('/');
    $settings = $this
      ->drupalGetSettings();
    $expected[$first_agent_machine_name]['goals'] = array(
      'user_login' => 'logs in',
    );
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$first_agent_machine_name], $expected[$first_agent_machine_name]);
    $this
      ->assertEqual($settings['acquia_lift']['campaigns'][$second_agent_machine_name], $expected[$second_agent_machine_name]);
  }

  /**
   * Tests the effect of the acquia_lift_unibar_allow_status_change variable to
   * prevent verification checks on every page load.
   */
  function testVerificationChecks() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.ui');
    AcquiaLiftAPI::setTestInstance();

    // Create a bunch of agents.
    $num_agents = 10;
    for ($i = 0; $i < $num_agents; $i++) {
      $this
        ->createTestAgent(array(
        'control_rate' => 10,
        'explore_rate' => 30,
      ));
    }
    $this
      ->resetAll();
    DummyAcquiaLiftHttpClient::clearLoggedRequests();
    $page = array(
      'page_top' => array(),
    );
    acquia_lift_build_page($page);

    // There should be 3 requests per agent, and there are 11 agents (the 10 we
    // just created plus the default.)
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $this
      ->assertEqual(count($requests), 3 + 3 * $num_agents);

    // Don't clear cache but clear logged requests and call the function again,
    // there shouldn't be any new requests.
    DummyAcquiaLiftHttpClient::clearLoggedRequests();
    acquia_lift_build_page($page);
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $this
      ->assertEqual(count($requests), 0);

    // Clear the cache and confirm they are made again.
    $this
      ->resetAll();
    DummyAcquiaLiftHttpClient::clearLoggedRequests();
    acquia_lift_build_page($page);
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $this
      ->assertEqual(count($requests), 3 + 3 * $num_agents);

    // Now disable allowing status changes from the unibar.
    variable_set('acquia_lift_unibar_allow_status_change', 0);
    $this
      ->resetAll();
    DummyAcquiaLiftHttpClient::clearLoggedRequests();
    acquia_lift_build_page($page);
    $requests = DummyAcquiaLiftHttpClient::getLoggedRequests();
    $this
      ->assertEqual(count($requests), 0);
  }

  // Tests changing the status of an agent via AJAX.
  function testAgentStatusChangeAJAX() {
    module_enable(array(
      'personalize_test_extra_agent',
    ));
    $this
      ->resetAll();

    // Need to call resetAll again to force ctools to load the class files.
    $this
      ->resetAll();
    $admin_user = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
    ));
    $this
      ->drupalLogin($admin_user);

    // Create two agents of different types.
    $first_agent_name = $this
      ->randomName();
    $first_agent_machine_name = personalize_generate_machine_name($first_agent_name, 'personalize_agent_machine_name_exists');
    $second_agent_name = $this
      ->randomName();
    $second_agent_machine_name = personalize_generate_machine_name($second_agent_name, 'personalize_agent_machine_name_exists');
    $agents = array(
      array(
        'label' => $first_agent_name,
        'machine_name' => $first_agent_machine_name,
        'agent_type' => 'test_agent',
      ),
      array(
        'label' => $second_agent_name,
        'machine_name' => $second_agent_machine_name,
        'agent_type' => 'test_invalid_agent',
      ),
    );
    foreach ($agents as $agent) {
      $edit = array(
        'agent_basic_info[title]' => $agent['label'],
        'agent_basic_info[machine_name]' => $agent['machine_name'],
        'agent_basic_info[agent_type]' => $agent['agent_type'],
      );
      $this
        ->drupalPost('admin/structure/personalize/add', $edit, $this
        ->getButton('agent'));
    }

    // Now test the ajax callback.
    // Expected case to update to next status.
    $expected = array(
      'success' => TRUE,
      'nextStatus' => array(
        'status' => PERSONALIZE_STATUS_PAUSED,
        'text' => t('Pause'),
      ),
      'currentStatus' => PERSONALIZE_STATUS_RUNNING,
    );
    $result = $this
      ->drupalGetAjax('admin/structure/personalize/manage/' . $first_agent_machine_name . '/ajax_status/' . PERSONALIZE_STATUS_RUNNING);
    $this
      ->assertEqual($expected, $result);

    // Try an update that doesn't change the status.
    $result = $this
      ->drupalGetAjax('admin/structure/personalize/manage/' . $first_agent_machine_name . '/ajax_status/' . PERSONALIZE_STATUS_RUNNING);
    $this
      ->assertEqual($expected, $result);

    // Try to update an agent that cannot be verified.
    $expected = array(
      'success' => FALSE,
    );
    $result = $this
      ->drupalGetAjax('admin/structure/personalize/manage/' . $second_agent_machine_name . '/ajax_status/' . PERSONALIZE_STATUS_RUNNING);
    $this
      ->assertEqual($expected, $result);

    // Try to call the path without the proper permissions.
    $this
      ->drupalLogout();
    $this
      ->drupalGet('admin/structure/personalize/manage/' . $first_agent_machine_name . '/ajax_status/' . PERSONALIZE_STATUS_RUNNING);
    $this
      ->assertText(t('Access denied'));
  }

  /**
   * Creates an article node with a personalized headline field.
   */
  protected function createPersonalizedField() {
    AcquiaLiftAPI::setTestInstance();

    // Create a node which we will attach a fields-based option set to. We can't
    // do this via the form because of the way we alter the form, which makes the
    // "Add an option" button not findable by simpletest.
    $node = new stdClass();
    $node->type = 'article';
    $node->language = LANGUAGE_NONE;
    node_object_prepare($node);
    $node->title = $this
      ->randomName();
    $node->article_headline['und'][0] = array(
      'value' => 'first value',
    );
    $node->article_headline['und'][1] = array(
      'value' => 'second value',
    );
    node_save($node);

    // Now create the option set.
    $option_set = new stdClass();
    $option_set->is_new = TRUE;
    $option_set->data = array();
    $option_set->options = array();
    $option_set->plugin = 'fields';
    $option_set->new_agent_title = 'Article: Personalizable Headline';
    $option_set->options = array(
      array(
        'option_label' => personalize_fields_generate_option_label(0, array(
          'value' => 'first value',
        )),
      ),
      array(
        'option_label' => personalize_fields_generate_option_label(1, array(
          'value' => 'second value',
        )),
      ),
    );
    if (personalize_fields_option_set_prepare($option_set, 'node', $node->nid, $node, 'article_headline')) {
      personalize_fields_option_set_save($option_set, 'node', $node->nid, 'article_headline');
    }
    $this
      ->resetAll();
    $agent_name = $option_set->agent;
    return array(
      $node,
      $option_set,
      $agent_name,
    );
  }

}
class AcquiaLiftWebTestTarget extends AcquiaLiftWebTestBase {
  public static function getInfo() {
    return array(
      'name' => t('Acquia Lift Web Tests - Target'),
      'description' => t('Tests functionality related acquia_lift_target campaigns.'),
      'group' => t('Personalize'),
    );
  }
  public function setUp() {
    parent::setUp();
    variable_set('acquia_lift_target_enabled', TRUE);
  }
  function testCreateTargetAudiences() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');
    $agent = $this
      ->createTargetingAgent();
    $this
      ->resetAll();
    $label = $this
      ->randomName();
    $contexts = array(
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'some_plugin',
          'some_context',
        )),
        'operator' => 'contains',
        'match' => 'ohai',
      ),
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'some_plugin',
          'some_other_context',
        )),
        'operator' => 'starts',
        'match' => 'stuff',
      ),
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'some_other_plugin',
          'some_context',
        )),
        'operator' => 'equals',
        'match' => 'kthxbai',
      ),
    );

    // Try saving a target audience without having created an option set.
    $saved = acquia_lift_target_audience_save($label, $agent->machine_name, $contexts, 'AND');
    $this
      ->assertFalse($saved);

    // Now create an option set for hte agent.
    list($option_set, ) = $this
      ->createOptionSet(0, array(
      'plugin' => 'blocks',
      'agent' => $agent->machine_name,
      'option_ids' => array(
        'first-choice',
        'second-choice',
        'third-choice',
      ),
    ), FALSE);
    $this
      ->resetAll();
    $saved = acquia_lift_target_audience_save($label, $agent->machine_name, $contexts, 'AND');
    $this
      ->assertTrue($saved);

    // Check that the expected targeting rules were saved on the option set.
    $option_set = personalize_option_set_load($option_set->osid, TRUE);
    $this
      ->assertNotNull($option_set->targeting);
    $audience_name = key($option_set->targeting);
    $expected_targeting = array(
      $audience_name => array(
        'label' => $label,
        'weight' => 0,
        'targeting_features' => array(
          'some_context::sc-ohai',
          'some_other_context::ss-stuff',
          'some_context::kthxbai',
        ),
        'targeting_rules' => array(
          'some_context::sc-ohai' => array(
            'context' => 'some_context',
            'operator' => 'contains',
            'match' => 'ohai',
            'plugin' => 'some_plugin',
          ),
          'some_other_context::ss-stuff' => array(
            'context' => 'some_other_context',
            'operator' => 'starts',
            'match' => 'stuff',
            'plugin' => 'some_plugin',
          ),
          'some_context::kthxbai' => array(
            'context' => 'some_context',
            'operator' => 'equals',
            'match' => 'kthxbai',
            'plugin' => 'some_other_plugin',
          ),
        ),
        'targeting_strategy' => 'AND',
      ),
    );
    $this
      ->assertEqual($expected_targeting, $option_set->targeting);

    // Add another audience.
    $label = $this
      ->randomName();
    $contexts = array(
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'boolean_plugin',
          'some_context',
        )),
        'operator' => 'equals',
        'match' => 1,
      ),
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'boolean_plugin',
          'some_other_context',
        )),
        'operator' => 'equals',
        'match' => '0',
      ),
    );
    $this
      ->resetAll();
    $saved = acquia_lift_target_audience_save($label, $agent->machine_name, $contexts, 'OR');
    $this
      ->assertTrue($saved);
    $option_set = personalize_option_set_load($option_set->osid, TRUE);
    $audience_names = array_keys($option_set->targeting);
    $expected_targeting[$audience_names[1]] = array(
      'label' => $label,
      'weight' => 1,
      'targeting_features' => array(
        'some_context::1',
        'some_other_context::0',
      ),
      'targeting_rules' => array(
        'some_context::1' => array(
          'context' => 'some_context',
          'operator' => 'equals',
          'match' => '1',
          'plugin' => 'boolean_plugin',
        ),
        'some_other_context::0' => array(
          'context' => 'some_other_context',
          'operator' => 'equals',
          'match' => '0',
          'plugin' => 'boolean_plugin',
        ),
      ),
      'targeting_strategy' => 'OR',
    );
    $this
      ->assertEqual($expected_targeting, $option_set->targeting);
  }
  function testAssignVariations() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');
    $agent = $this
      ->createTargetingAgent();
    $this
      ->assertFalse(isset($agent->data['lift_targeting']));
    $this
      ->resetAll();
    list($option_set, ) = $this
      ->createOptionSet(0, array(
      'plugin' => 'blocks',
      'agent' => $agent->machine_name,
      'option_ids' => array(
        'first-choice',
        'second-choice',
        'third-choice',
      ),
    ), FALSE);
    $this
      ->resetAll();

    // Try to set up targeting with a non-existent audience.
    $audience_name = personalize_generate_machine_name($this
      ->randomName(), NULL, '-');
    $targeting = array(
      $audience_name => array(
        'second-choice',
        'third-choice',
      ),
    );
    try {
      acquia_lift_save_targeting_structure($agent, $targeting);
      $this
        ->fail('Should not reach here');
    } catch (AcquiaLiftException $e) {
      $this
        ->assertEqual('Invalid audience', $e
        ->getMessage());
    }

    // Now create the audience and try again.
    $this
      ->createTargetAudience($option_set, $audience_name);
    $this
      ->resetAll();
    try {
      acquia_lift_save_targeting_structure($agent, $targeting);
    } catch (AcquiaLiftException $e) {
      $this
        ->fail('Exception thrown when none expected.');
    }
    $this
      ->assertEqual($targeting, $agent->data['lift_targeting']);
  }
  function testImplementTargetingStructure() {
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');

    // First, set up our agent, option set, audience and desired targeting
    // structure.
    $agent = $this
      ->createTargetingAgent();
    $this
      ->resetAll();
    $parent_option_set = $this
      ->createPersonalizedBlock(0, $agent, 3);
    if (empty($parent_option_set)) {
      $this
        ->fail('Could not create option set');
      return;
    }

    // Keep the option ids in an array.
    $option_ids = array();
    foreach ($parent_option_set->options as $option) {
      $option_ids[] = $option['option_id'];
    }
    $this
      ->resetAll();
    $audience_name = personalize_generate_machine_name($this
      ->randomName(), NULL, '-');
    $this
      ->createTargetAudience($parent_option_set, $audience_name);
    $targeting = array(
      $audience_name => array(
        $option_ids[1],
        $option_ids[2],
      ),
    );
    try {
      acquia_lift_save_targeting_structure($agent, $targeting);
    } catch (AcquiaLiftException $e) {
      $this
        ->fail('Exception thrown when none expected.');
    }

    // Now implement the targeting structure that is currently just stored in
    // the 'lift_targeting' property.
    AcquiaLiftAPI::setTestInstance();
    acquia_lift_update_targeting($agent);

    // We should have a new nested acquia_lift agent and option set.
    $agents = personalize_agent_load_by_type('acquia_lift');
    $this
      ->assertEqual(1, count($agents));
    $nested_agent = reset($agents);
    $option_sets = personalize_option_set_load_by_agent($nested_agent->machine_name);
    $this
      ->assertEqual(1, count($option_sets));
    $nested_osid = key($option_sets);
    $nested_option_set = reset($option_sets);
    $this
      ->assertEqual('options', $nested_option_set->plugin);

    // Confirm the correct options have been added.
    $expected_options = array(
      array(
        'option_id' => $option_ids[1],
      ),
      array(
        'option_id' => $option_ids[2],
      ),
    );
    $this
      ->assertEqual($expected_options, $nested_option_set->options);

    // Confirm this osid is on the original option set's targeting rule for that
    // audience.
    $option_set = personalize_option_set_load($parent_option_set->osid, TRUE);
    $this
      ->assertEqual($nested_osid, $option_set->targeting[$audience_name]['osid']);

    // Create a new target audience and change the strucutre of our campaign.
    $second_audience = personalize_generate_machine_name($this
      ->randomName(), NULL, '-');
    $this
      ->createTargetAudience($option_set, $second_audience, array(
      array(
        'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
          'some_plugin',
          'some_context',
        )),
        'operator' => 'equals',
        'match' => 'kthxbai',
      ),
    ));
    $targeting = array(
      $audience_name => array(
        $option_ids[2],
      ),
      $second_audience => array(
        $option_ids[0],
        $option_ids[1],
      ),
    );
    try {
      acquia_lift_save_targeting_structure($agent, $targeting);
    } catch (AcquiaLiftException $e) {
      $this
        ->fail('Exception thrown when none expected.');
    }
    $this
      ->resetAll();

    // When we implement the new structure it should delete the existing nested
    // option set and create a new one.
    AcquiaLiftAPI::setTestInstance();
    acquia_lift_update_targeting($agent);
    $nested_option_set = personalize_option_set_load($nested_osid, TRUE);
    $this
      ->assertFalse($nested_option_set);

    // Check the new agent and option set.
    $agents = personalize_agent_load_by_type('acquia_lift');
    $this
      ->assertEqual(1, count($agents));
    $nested_agent = reset($agents);
    $option_sets = personalize_option_set_load_by_agent($nested_agent->machine_name);
    $this
      ->assertEqual(1, count($option_sets));
    $nested_osid = key($option_sets);
    $nested_option_set = reset($option_sets);
    $this
      ->assertEqual('options', $nested_option_set->plugin);

    // Confirm the correct options have been added.
    $expected_options = array(
      array(
        'option_id' => $option_ids[0],
      ),
      array(
        'option_id' => $option_ids[1],
      ),
    );
    $this
      ->assertEqual($expected_options, $nested_option_set->options);

    // Confirm this osid is on the original option set's targeting rule for that
    // audience.
    $parent_option_set = personalize_option_set_load($parent_option_set->osid, TRUE);
    $this
      ->assertEqual($nested_osid, $parent_option_set->targeting[$second_audience]['osid']);

    // This audience should not have an option id property.
    $this
      ->assertFalse(isset($parent_option_set->targeting[$second_audience]['option_id']));

    // Confirm the option_id property is now on the original audience.
    $this
      ->assertEqual($option_ids[2], $parent_option_set->targeting[$audience_name]['option_id']);

    // It should no longer have an osid property.
    $this
      ->assertFalse(isset($parent_option_set->targeting[$audience_name]['osid']));

    // Now let's place our personalized block so we can test how it gets rendered.
    $admin_user = $this
      ->drupalCreateUser(array(
      'access administration pages',
      'manage personalized content',
      'administer blocks',
    ));
    $this
      ->drupalLogin($admin_user);
    $edit = array(
      'blocks[personalize_blocks_' . $parent_option_set->osid . '][region]' => 'content',
    );
    $this
      ->drupalPost('admin/structure/block', $edit, t('Save blocks'));
    $this
      ->drupalLogout();
    $this
      ->drupalGet('');
    $settings = $this
      ->drupalGetSettings();

    // THe parent agent and option set should be in the personalize js settings.
    $this
      ->assertTrue(isset($settings['personalize']['agent_map'][$agent->machine_name]));
    $this
      ->assertTrue(isset($settings['personalize']['option_sets']['osid-' . $parent_option_set->osid]));

    // The nested agent and option set should *not* be in the personlaize js settings.
    $this
      ->assertFalse(isset($settings['personalize']['agent_map'][$nested_agent->machine_name]));
    $this
      ->assertFalse(isset($settings['personalize']['option_sets']['osid-' . $nested_osid]));

    // The nested agent and option set should be in the acquia_lift_target js settings.
    $this
      ->assertTrue(isset($settings['acquia_lift_target']['agent_map'][$nested_agent->machine_name]));
    $this
      ->assertTrue(isset($settings['acquia_lift_target']['option_sets']['osid-' . $nested_osid]));

    // The parent agent and option set should *not* be in the acquia_lift_target js settings.
    $this
      ->assertFalse(isset($settings['acquia_lift_target']['agent_map'][$agent->machine_name]));
    $this
      ->assertEqual(1, count($settings['acquia_lift_target']['option_sets']));

    // Change the targeting structure again to have two nested tests.
    $targeting = array(
      $audience_name => array(
        $option_ids[0],
        $option_ids[1],
      ),
      $second_audience => array(
        $option_ids[0],
        $option_ids[2],
      ),
    );
    try {
      acquia_lift_save_targeting_structure($agent, $targeting);
    } catch (AcquiaLiftException $e) {
      $this
        ->fail('Exception thrown when none expected.');
    }
    $this
      ->resetAll();

    // When we implement the new structure it should not delete the existing nested
    // option set but it should change the audience it belongs to.
    AcquiaLiftAPI::setTestInstance();
    acquia_lift_update_targeting($agent);
    $nested_option_set = personalize_option_set_load($nested_osid, TRUE);
    $this
      ->assertTrue($nested_option_set);

    // We should have two nested agents each with one option set.
    $agents = personalize_agent_load_by_type('acquia_lift');
    $this
      ->assertEqual(2, count($agents));

    // Find the osid of the new option set so we can check that it has been set
    // on the correct target audience.
    foreach ($agents as $nested) {

      // Find the *new* nested agent.
      if ($nested->machine_name != $nested_agent->machine_name) {
        $option_sets = personalize_option_set_load_by_agent($nested->machine_name);
        $nested_os = reset($option_sets);
        $nested_osid2 = $nested_os->osid;
      }
    }
    if (isset($nested_osid2)) {

      // Confirm that the correct osid is assigend to each of the two target
      // audiences.
      $parent_option_set = personalize_option_set_load($parent_option_set->osid, TRUE);
      $this
        ->assertEqual(2, count($parent_option_set->targeting));
      foreach (array(
        $audience_name => $nested_osid,
        $second_audience => $nested_osid2,
      ) as $audience => $osid) {
        $this
          ->assertEqual($osid, $parent_option_set->targeting[$audience]['osid']);
        $this
          ->assertFalse(isset($parent_option_set->targeting[$audience]['option_id']));
      }
    }
    else {
      $this
        ->fail('Could not find the second option set');
    }
  }
  protected function createTargetingAgent($label = NULL) {
    AcquiaLiftAPI::setTestInstance();
    if (empty($label)) {
      $label = $this
        ->randomName();
    }
    $agent = new stdClass();
    $agent->label = $label;
    $agent->plugin = 'acquia_lift_target';
    $agent->data = array();
    $agent->machine_name = personalize_generate_machine_name($label, 'personalize_agent_machine_name_exists');
    $agent = personalize_agent_save($agent);
    $this
      ->resetAll();
    return $agent;
  }

  /**
   * Helper that adds a target audience using two contexts AND'd together.
   *
   * @param $option_set
   * @param null $machine_name
   * @return null|string
   */
  protected function createTargetAudience($option_set, $machine_name = NULL, $contexts = array()) {
    if (empty($machine_name)) {
      $label = $this
        ->randomName();
      $machine_name = personalize_generate_machine_name($label, NULL, '-');
    }
    if (empty($contexts)) {
      $contexts = array(
        array(
          'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
            'some_plugin',
            'some_context',
          )),
          'operator' => 'contains',
          'match' => 'ohai',
        ),
        array(
          'context' => implode(PERSONALIZE_TARGETING_ADMIN_SEPARATOR, array(
            'some_plugin',
            'some_other_context',
          )),
          'operator' => 'starts',
          'match' => 'stuff',
        ),
      );
    }
    module_load_include('inc', 'acquia_lift', 'acquia_lift.admin');
    acquia_lift_target_audience_save($machine_name, $option_set->agent, $contexts, 'AND');
    $this
      ->resetAll();
    return $machine_name;
  }

}