<?php

/**
 * @file
 * Tests for the node destination plugin.
 */

/**
 * Test node migration.
 */
class MigrateNodeUnitTest extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Node migration',
      'description' => 'Test migration of node data',
      'group' => 'Migrate',
    );
  }

  function setUp() {
    parent::setUp('list', 'number', 'taxonomy', 'image', 'migrate', 'migrate_example');

    // Make sure the migrations are registered.
    migrate_static_registration();
  }

  function testNodeImport() {
    $migration = Migration::getInstance('WineVariety');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Variety term import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineRegion');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Region term import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineBestWith');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('"Best With" term import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineFileCopy');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('File import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineRole');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Role import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineUser');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('User import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineProducer');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Producer node import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('WineWine');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import returned RESULT_COMPLETED'));

    // Gather wine and producer nodes, and their corresponding input data
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_producer'), TRUE);
    // Index by title
    $producer_nodes = array();
    foreach ($rawnodes as $node) {
      $producer_nodes[$node->title] = $node;
    }

    $query = db_select('migrate_example_wine_producer', 'p')
      ->fields('p', array(
        'producerid',
        'name',
        'body',
        'excerpt',
        'accountid',
      ));
    // Region term is singletons, handled straighforwardly
    $query->leftJoin('migrate_example_wine_category_producer', 'reg',
      "p.producerid = reg.producerid");
    $query->addField('reg', 'categoryid', 'region');
    $result = $query->execute();
    $producer_rows = array();
    foreach ($result as $row) {
      $producer_rows[$row->name] = $row;
    }
    $this->assertEqual(count($producer_nodes), count($producer_rows),
      t('Counts of producer nodes and input rows match'));

    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    // Index by title
    $wine_nodes = array();
    foreach ($rawnodes as $node) {
      $wine_nodes[$node->title] = $node;
    }
    $query = db_select('migrate_example_wine', 'w')
      ->fields('w', array(
        'wineid',
        'name',
        'body',
        'excerpt',
        'accountid',
        'posted',
        'last_changed',
        'variety',
        'region',
      ));
    $query->leftJoin('migrate_example_wine_category_wine', 'cwbw',
      "w.wineid = cwbw.wineid");
    $query->leftJoin('migrate_example_wine_categories', 'bw',
      "cwbw.categoryid = bw.categoryid AND bw.type = 'best_with'");
    // Gives a single comma-separated list of related terms
    $query->groupBy('cwbw.wineid');
    $query->addExpression('GROUP_CONCAT(bw.categoryid)', 'best_with');
    $result = $query->execute();
    $wine_rows = array();
    foreach ($result as $row) {
      $wine_rows[$row->name] = $row;
    }
    $this->assertEqual(count($wine_nodes), count($wine_rows),
      t('Counts of wine nodes and input rows match'));

    // Test each base node field
    $producer_node = $producer_nodes['Montes'];
    $producer_row = $producer_rows['Montes'];
    $wine_node = $wine_nodes['Montes Classic Cabernet Sauvignon'];
    $wine_row = $wine_rows['Montes Classic Cabernet Sauvignon'];
    $user_migration = MigrationBase::getInstance('WineUser');

    $mapped_uid = $user_migration->getMap()
      ->lookupDestinationID(array($producer_row->accountid));
    if (is_array($mapped_uid)) {
      $this->assertEqual($producer_node->uid, reset($mapped_uid),
        t('uid properly migrated'));
    }
    else {
      $this->error(t('Account ID !id not migrated', array('!id' => $producer_row->accountid)));
    }
    $this->assertEqual($wine_node->created, $wine_row->posted,
      t('created properly migrated'));
    $this->assertEqual($wine_node->changed, $wine_row->last_changed,
      t('changed properly migrated'));

    // Test Field API fields of all types
    // body_with_summary
    $body = field_get_items('node', $wine_node, 'body');
    $this->assertEqual($body[0]['value'], 'REVIEW: ' . drupal_strtoupper($wine_row->body),
      t('body properly migrated'));
    $this->assertEqual($body[0]['summary'], $wine_row->excerpt,
      t('summary properly migrated'));
    // taxonomy_term_reference - single and multiple
    $variety = field_get_items('node', $wine_node, 'migrate_example_wine_varieties');
    $variety_migration = MigrationBase::getInstance('WineVariety');
    $mapped_tid = $variety_migration->getMap()
      ->lookupDestinationID(array($wine_row->variety));
    if (is_array($mapped_tid)) {
      $this->assertEqual($variety[0]['tid'], reset($mapped_tid),
        t('Single taxonomy_term_reference properly migrated'));
    }
    else {
      $this->error(t('Variety !var not migrated', array('!var' => $wine_row->variety)));
    }
    $best_with = field_get_items('node', $wine_node, 'migrate_example_wine_best_with');
    $best_with_migration = MigrationBase::getInstance('WineBestWith');
    $source_ids = explode(',', $wine_row->best_with);
    $mapped_tids = array();
    foreach ($source_ids as $source_id) {
      $tid = $best_with_migration->getMap()
        ->lookupDestinationID(array($source_id));
      if ($tid) {
        $mapped_tids[reset($tid)] = reset($tid);
      }
    }
    $this->assertEqual(count($best_with), count($mapped_tids),
      t('Counts of Best With match'));
    foreach ($best_with as $current) {
      $this->assertNotNull($mapped_tids[$current['tid']],
        t('Multiple value taxonomy_term_reference properly migrated'));
    }

    // Test the vintages field (demonstrating prepareRow() works) - we know
    // the valid vintages for this node are 2006 and 2007
    $expected = array(array('value' => 2006), array('value' => 2007));
    $this->assertEqual($wine_node->field_migrate_example_top_vintag[LANGUAGE_NONE], $expected,
      t('Vintages match (prepareRow works)'));

    // Test updates
    // Capture original nodes
    $original_nodes = node_load_multiple(array(), array('type' => 'migrate_example_wine'));
    $update_migration = Migration::getInstance('WineUpdates');
    $result = $update_migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine updates import returned RESULT_COMPLETED'));
    $final_nodes = node_load_multiple(array(), array('type' => 'migrate_example_wine'), TRUE);
    foreach ($original_nodes as $nid => $original_node) {
      foreach ($original_node as $field => $value) {
        if ($field == 'field_migrate_example_wine_ratin' || $field == 'changed' || $field == 'revision_timestamp') {
          if ($value == $final_nodes[$nid]->{$field}) {
            $this->error(t('Field !field should have changed but did not, value=!value',
              array('!field' => $field, '!value' => print_r($value, TRUE))));
          }
        }
        else {
          if ($value != $final_nodes[$nid]->{$field}) {
            $this->error(t('Field !field mismatch: original !value1, result !value2',
              array(
                '!field' => $field,
                '!value1' => print_r($value, TRUE),
                '!value2' => print_r($final_nodes[$nid]->{$field}, TRUE),
              )));
          }
        }
      }
    }

    // Test highwater marks - add new wines, modify an old one, and see what changes
    $fields = array(
      'wineid',
      'name',
      'body',
      'excerpt',
      'accountid',
      'posted',
      'last_changed',
      'variety',
      'region',
      'rating',
    );
    $query = db_insert('migrate_example_wine')
      ->fields($fields);
    $data = array(
      // Timestamps 1284008523, 1284120550
      array(
        3,
        'Schloss Muhlenhof Dornfelder',
        'Juicy black & red berry fruits',
        'Pretty good',
        9,
        strtotime('2010-09-09 01:02:03'),
        strtotime('2010-09-10 08:09:10'),
        25,
        17,
        95,
      ),
      // Timestamps 1286122209, 1286122209
      array(
        4,
        'Gachot-Monot Bourgogne Rge 06',
        'Funky',
        'Pair with white sauced dishes',
        3,
        strtotime('2010-10-03 12:10:09'),
        strtotime('2010-10-03 12:10:09'),
        26,
        2,
        85,
      ),
    );
    foreach ($data as $row) {
      $query->values(array_combine($fields, $row));
    }
    $query->execute();

    db_update('migrate_example_wine')
      ->fields(array(
        'body' => 'Not so much berry character',
        // Timestamp 1285058521
        'last_changed' => strtotime('2010-10-21 04:42:01'),
      ))
      ->condition('wineid', 2)
      ->execute();

    $migration = Migration::getInstance('WineWine');
    $result = $migration->processImport();

    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    if (!$this->assertEqual(count($rawnodes), 4, t('Now 4 wine nodes exist'))) {
      $this->error(t('There are now !count nodes', array('!count' => count($rawnodes))));
    }
    $nodes = node_load_multiple(FALSE, array('title' => 'Archeo Ruggero di Tasso Nero d\'Avola'), TRUE);
    $node = reset($nodes);
    $body = $node->body[LANGUAGE_NONE][0]['value'];
    if (!$this->assertEqual($body, 'REVIEW: NOT SO MUCH BERRY CHARACTER', t('Body updated'))) {
      $this->error(t('Actual body !body', array('!body' => $body)));
    }

    // Test rollback
    $result = $migration->processRollback(array('force' => TRUE));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node rollback returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    $this->assertEqual(count($rawnodes), 0, t('All nodes deleted'));
    $count = db_select('migrate_map_winewine', 'map')
      ->fields('map', array('sourceid1'))
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertEqual($count, 0, t('Map cleared'));
    $count = db_select('migrate_message_winewine', 'msg')
      ->fields('msg', array('sourceid1'))
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertEqual($count, 0, t('Messages cleared'));

    // Now test highwater with unjoined map table
    $migration->getSource()->setMapJoinable(FALSE);
    $result = $migration->processImport(array(
      'limit' =>
        array('value' => 2, 'unit' => 'items'),
    ));
    db_update('migrate_example_wine')
      ->fields(array(
        'body' => 'Very berry',
        // Timestamp 1286008921
        'last_changed' => strtotime('2010-10-02 04:42:01'),
      ))
      ->condition('wineid', 1)
      ->execute();
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    if (!$this->assertEqual(count($rawnodes), 4, t('Now 4 wine nodes exist'))) {
      $this->error(t('There are now !count nodes', array('!count' => count($rawnodes))));
    }
    $nodes = node_load_multiple(FALSE, array('title' => 'Montes Classic Cabernet Sauvignon'), TRUE);
    $node = reset($nodes);
    $body = $node->body[LANGUAGE_NONE][0]['value'];
    if (!$this->assertEqual($body, 'REVIEW: VERY BERRY', t('Body updated'))) {
      $this->error(t('Actual body !body', array('!body' => $body)));
    }

    // Test itemlimit (joined map table)
    $result = $migration->processRollback(array('force' => TRUE));
    $migration->getSource()->setMapJoinable(TRUE);
    $result = $migration->processImport(array(
      'limit' =>
        array('value' => 1, 'unit' => 'item'),
    ));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import with itemlimit returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    $this->assertEqual(count($rawnodes), 1, t('One node imported'));

    // Test idlist (joined map table)
    $result = $migration->processRollback(array('force' => TRUE));
    $result = $migration->processImport(array('idlist' => 2));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import with idlist returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    $this->assertEqual(count($rawnodes), 1, t('One node imported'));
    $node = reset($rawnodes);
    $this->assertEqual($node->title, 'Archeo Ruggero di Tasso Nero d\'Avola',
      t('Single specified node imported'));

    // Test itemlimit (unjoined map table)
    $result = $migration->processRollback(array('force' => TRUE));
    $migration->getSource()->setMapJoinable(FALSE);
    $result = $migration->processImport(array(
      'limit' =>
        array('value' => 1, 'unit' => 'item'),
    ));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import with itemlimit returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    $this->assertEqual(count($rawnodes), 1, t('One node imported'));

    // Test idlist (unjoined map table)
    $result = $migration->processRollback(array('force' => TRUE));
    $result = $migration->processImport(array('idlist' => 2));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Wine node import with idlist returned RESULT_COMPLETED'));
    $rawnodes = node_load_multiple(FALSE, array('type' => 'migrate_example_wine'), TRUE);
    $this->assertEqual(count($rawnodes), 1, t('One node imported'));
    $node = reset($rawnodes);
    $this->assertEqual($node->title, 'Archeo Ruggero di Tasso Nero d\'Avola',
      t('Single specified node imported'));

    // Test integer highwater marks (http://drupal.org/node/1161612)
    $result = $migration->processRollback(array('force' => TRUE));
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 100000000))
      ->condition('wineid', 1)
      ->execute();
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 200000000))
      ->condition('wineid', 2)
      ->execute();
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 300000000))
      ->condition('wineid', 3)
      ->execute();
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 400000000))
      ->condition('wineid', 4)
      ->execute();
    $result = $migration->processImport();
    // Just a quick check to make sure we got four nodes with the right changed values
    $count = db_query("SELECT COUNT(nid)
                       FROM {node} n
                       INNER JOIN {migrate_map_winewine} map ON n.nid=map.destid1
                       WHERE n.changed = map.sourceid1*100000000")->fetchField();
    $this->assertEqual($count, 4, t('Four nodes with updated changed values imported'));
    // We mark two nodes with higher updated values. If these end up being treated
    // as strings in saveHighwater(), the saved highwater mark will end up as
    // 500000000 instead of 1000000000.
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 1000000000))
      ->condition('wineid', 2)
      ->execute();
    db_update('migrate_example_wine')
      ->fields(array('last_changed' => 500000000))
      ->condition('wineid', 3)
      ->execute();
    $result = $migration->processImport();
    $newHighwater = db_select('migrate_status', 'ms')
      ->fields('ms', array('highwater'))
      ->condition('machine_name', 'WineWine')
      ->execute()
      ->fetchField();
    if (!$this->assertEqual($newHighwater, 1000000000, t('Correct highwater mark set'))) {
      $this->error(t('Unexpected highwater mark !highwater', array('!highwater' => $newHighwater)));
    }

    // Test for http://drupal.org/node/1037872 - updating with nid mapped and idlist
    $migration = Migration::getInstance('BeerTerm');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Beer term import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('BeerUser');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Beer user import returned RESULT_COMPLETED'));
    $migration = Migration::getInstance('BeerNode');
    $result = $migration->processImport();
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Beer node import returned RESULT_COMPLETED'));
    db_update('migrate_map_beernode')
      ->fields(array('needs_update' => 1))
      ->execute();
    $result = $migration->processImport(array('idlist' => 99999999));
    $this->assertEqual($result, Migration::RESULT_COMPLETED,
      t('Beer node update import returned RESULT_COMPLETED'));
    $result = db_select('migrate_message_beernode', 'msg')
      ->fields('msg', array('message'))
      ->execute();
    foreach ($result as $row) {
      $this->error($row->message);
    }

    // Test proper disableMailSystem()/restoreMailSystem() behavior.
    // @see https://www.drupal.org/node/2499861
    // This is not necessarily node-related, but it was shoved in the node test.
    $migration->saveMailSystem();
    $migration->disableMailSystem();
    $ms = variable_get('mail_system', NULL);
    $this->assertTrue(is_array($ms) && in_array('MigrateMailIgnore', $ms),
      t('Migration has disabled mail system'));
    $migration->restoreMailSystem();
    $ms = variable_get('mail_system', NULL);
    $this->assertFalse(is_array($ms) && in_array('MigrateMailIgnore', $ms),
      t('Migration::restoreMailSystem restored mail system'));
  }
}
