You are here

cdn.test in CDN 7.2

Test CDN.

File

tests/cdn.test
View source
<?php

/**
 * @file
 * Test CDN.
 */
class CDNUnitTestCase extends DrupalUnitTestCase {
  function setUp() {
    parent::setUp();

    // Alter $_SERVER to include some relatively rarely set HTTP headers.
    $alt_server = array(
      'HTTP_ACCEPT_ENCODING',
      'HTTPS' => 'off',
      'HTTP_X_FORWARDED_PROTO' => 'http',
      'HTTP_USER_AGENT' => $this
        ->randomName(),
    );
    $alt_server = array_merge($alt_server, $_SERVER);
    $_SERVER = $alt_server;
    $this
      ->setRequestProtocol('http');

    // Enable the private:// stream wrapper.
    $this
      ->variableSet('file_private_path', conf_path() . '/files/private');

    // Pretend the CDN module is enabled; this ensures invocations of its own
    // hook implementations will work as expected.
    $cdn_module_file = drupal_get_path('module', 'cdn') . '/cdn.module';
    $module_list['system']['filename'] = 'modules/system/system.module';
    $module_list['cdn']['filename'] = $cdn_module_file;
    module_list(TRUE, FALSE, FALSE, $module_list);
    $implementations =& drupal_static('module_implements');
    $implementations = array();
    $this
      ->loadFile('cdn.constants.inc');
    $this
      ->loadFile('cdn.module');

    // Override $conf to be able to use variable_set() and variable_get() in
    // DrupalUnitTestCase. At the same time, make sure we can restore the
    // original values.
    global $conf;
    $this->originalConfig = $conf;
    $this
      ->variableSetDefaults();
  }
  function tearDown() {

    // Restore the original values that are used by variable_get().
    global $conf;
    $conf = $this->originalConfig;
    parent::tearDown();
  }
  function loadFile($file) {
    $cdn_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'cdn');
    require_once "{$cdn_path}/{$file}";
  }

  /**
   * Mock function for variable_set().
   */
  function variableSet($name, $value) {
    global $conf;
    $conf[$name] = $value;
  }

  /**
   * Set the default variable values for the CDN module.
   */
  function variableSetDefaults() {
    global $conf;
    $this->defaultConfig = array(
      CDN_STATUS_VARIABLE => CDN_ENABLED,
      CDN_MODE_VARIABLE => FALSE,
      CDN_HTTPS_SUPPORT_VARIABLE => FALSE,
      CDN_BASIC_MAPPING_VARIABLE => '',
      CDN_BASIC_MAPPING_HTTPS_VARIABLE => '',
      CDN_BASIC_FARFUTURE_VARIABLE => FALSE,
      CDN_SEO_REDIRECT_VARIABLE => TRUE,
    );
    $conf = array_merge($conf, $this->defaultConfig);
  }

  /**
   * Set the protocol of the current "request".
   *
   * @param $protocol
   *   'http' or 'https'.
   */
  function setRequestProtocol($protocol) {
    if ($protocol == 'http') {
      $_SERVER['HTTPS'] = 'off';
      $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
    }
    elseif ($protocol == 'https') {
      $_SERVER['HTTPS'] = 'on';
      $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
    }
  }

  /**
   * Set the User-Agent of the current "request".
   *
   * @param $ua
   *   A User-Agent, which can be almost any string.
   */
  function setUserAgent($ua) {
    $_SERVER['HTTP_USER_AGENT'] = $ua;
  }

  /**
   * Configure HTTPS-related settings.
   *
   * @param $supported
   *   Boolean that indicates whether HTTPS is supported by the current CDN
   *   or not.
   * @param $mapping
   *   The CDN mapping to use when the CDN supports HTTPS and the current
   *   request is happening over HTTPS.
   */
  function configureHTTPS($supported, $mapping = '') {
    $this
      ->variableSet(CDN_HTTPS_SUPPORT_VARIABLE, $supported);
    $this
      ->variableSet(CDN_BASIC_MAPPING_HTTPS_VARIABLE, $mapping);
  }

  /**
   * Given a file URI, get the expanded file path.
   *
   * @param $uri
   *   @see file_stream_wrapper_get_instance_by_uri()
   * @return
   *  A Drupal root-relative path.
   */
  function getExpandedFilePath($uri) {
    $wrapper = file_stream_wrapper_get_instance_by_uri($uri);
    return str_replace($GLOBALS['base_url'] . '/', '', $wrapper
      ->getExternalUrl());
  }

  /**
   * Given a file URI, get its path, create the file and ensure it exists.
   *
   * @param $uri
   *   @see getExpandedFilePath()
   */
  function touchFile($uri) {
    $path = $this
      ->getExpandedFilePath($uri);
    $this
      ->assertTrue(touch(rawurldecode($path)), 'Test file created.');
    return $path;
  }

}
class CDNWebTestCase extends DrupalWebTestCase {
  function setUp() {
    parent::setUp('cdn');
  }
  function loadFile($file) {
    $cdn_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'cdn');
    require_once "{$cdn_path}/{$file}";
  }

}
class CDNGeneralTestCase extends CDNUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'General',
      'description' => 'Verify general functionality.',
      'group' => 'CDN',
    );
  }
  function testHTTPSDetection() {

    // HTTPS + HTTP_X_FORWARDED_PROTO permutations.
    $_SERVER['HTTPS'] = 'off';
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
    $this
      ->assertEqual(FALSE, cdn_request_is_https(), 'HTTP request detected.');
    $_SERVER['HTTPS'] = 'off';
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');
    $_SERVER['HTTPS'] = 'on';
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'https';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');
    $_SERVER['HTTPS'] = 'on';
    $_SERVER['HTTP_X_FORWARDED_PROTO'] = 'http';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');

    // HTTPS + HTTP_X_FORWARDED_PROTOCOL permutations.
    unset($_SERVER['HTTP_X_FORWARDED_PROTO']);
    $_SERVER['HTTPS'] = 'off';
    $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'http';
    $this
      ->assertEqual(FALSE, cdn_request_is_https(), 'HTTP request detected.');
    $_SERVER['HTTPS'] = 'off';
    $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'https';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');
    $_SERVER['HTTPS'] = 'on';
    $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'https';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');
    $_SERVER['HTTPS'] = 'on';
    $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] = 'http';
    $this
      ->assertEqual(TRUE, cdn_request_is_https(), 'HTTPS request detected.');
  }

}
class CDNOriginPullTestCase extends CDNUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Origin Pull mode',
      'description' => 'Verify Origin Pull mode-related functionality.',
      'group' => 'CDN',
    );
  }
  function setUp() {
    parent::setUp();
    $this
      ->loadFile('cdn.basic.inc');
    $this
      ->loadFile('cdn.basic.farfuture.inc');
    $this
      ->variableSet(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  }

  /**
   * Assert a CDN mapping (and optionally set a mapping).
   *
   * @param $mapping
   *   The mapping to set; if FALSE, no new mapping will be set.
   * @param $parsed_reference
   *   The reference the parsed mapping will be compared to.
   * @param $domains_reference
   *   The reference the domains (as returned by cdn_get_domains()) will be
   *   compared to.
   */
  function assertMapping($mapping, $parsed_reference, $domains_reference) {
    if ($mapping !== FALSE) {
      $this
        ->variableSet(CDN_BASIC_MAPPING_VARIABLE, $mapping);
    }
    $this
      ->assertEqual($parsed_reference, _cdn_basic_parse_raw_mapping(cdn_basic_get_mapping()), 'CDN mapping parsed correctly.');
    $domains = cdn_get_domains();
    sort($domains);
    $this
      ->assertEqual($domains_reference, $domains, 'CDN domains parsed correctly.');
  }
  function testMapping() {
    $this
      ->setRequestProtocol('http');
    $this
      ->assertEqual('', cdn_basic_get_mapping(), 'The default CDN mapping is empty.');

    // Ensure the parsing of the raw mapping works correctly.
    $this
      ->assertMapping('', array(), array());
    $this
      ->assertMapping('http://cdn-a.com', array(
      '*' => array(
        'http://cdn-a.com',
      ),
    ), array(
      'cdn-a.com',
    ));
    $this
      ->assertMapping('http://cdn-a.com/', array(
      '*' => array(
        'http://cdn-a.com',
      ),
    ), array(
      'cdn-a.com',
    ));
    $this
      ->assertMapping('//cdn-a.com', array(
      '*' => array(
        '//cdn-a.com',
      ),
    ), array(
      'cdn-a.com',
    ));
    $this
      ->assertMapping('//cdn-a.com/', array(
      '*' => array(
        '//cdn-a.com',
      ),
    ), array(
      'cdn-a.com',
    ));
    $parsed_mapping = array(
      'css' => array(
        'http://cdn-a.com',
      ),
      'jpg' => array(
        'http://cdn-a.com',
      ),
      'jpeg' => array(
        'http://cdn-a.com',
      ),
      'png' => array(
        'http://cdn-a.com',
      ),
      'zip' => array(
        'http://cdn-b.com',
      ),
      '*' => array(
        'http://cdn-c.com',
      ),
    );
    $domains = array(
      'cdn-a.com',
      'cdn-b.com',
      'cdn-c.com',
    );
    $this
      ->assertMapping("http://cdn-a.com|.css .jpg .jpeg .png\nhttp://cdn-b.com|.zip\nhttp://cdn-c.com", $parsed_mapping, $domains);
    $parsed_mapping = array(
      'css' => array(
        'http://cdn-a.com',
        'http://cdn-d.com',
      ),
      'jpg' => array(
        'http://cdn-a.com',
        'http://cdn-d.com',
      ),
      'jpeg' => array(
        'http://cdn-a.com',
        'http://cdn-d.com',
      ),
      'png' => array(
        'http://cdn-a.com',
        'http://cdn-d.com',
        'http://cdn-b.com',
      ),
      '*' => array(
        'http://cdn-c.com',
      ),
    );
    $domains = array(
      'cdn-a.com',
      'cdn-b.com',
      'cdn-c.com',
      'cdn-d.com',
    );
    $this
      ->assertMapping("http://cdn-a.com http://cdn-d.com|.css .jpg .jpeg .png\nhttp://cdn-b.com|.png\nhttp://cdn-c.com", $parsed_mapping, $domains);

    // When a HTTPS request is performed and the CDN is not marked to support
    // HTTPS, then it should fall back to the default CDN mapping.
    $this
      ->setRequestProtocol('https');
    $this
      ->assertMapping(FALSE, $parsed_mapping, $domains);

    // When a HTTPS request is performed and the CDN *is* marked to support
    // HTTPS, then it should still fall back to the default CDN mapping. (When
    // file URLs are actually altered, it will then replace `http://` with
    // `https://` -- this will be tested in a different test.)
    $this
      ->configureHTTPS(TRUE);
    $this
      ->assertMapping(FALSE, $parsed_mapping, $domains);

    // When a HTTPS request is performed *and* the CDN is marked to support
    // HTTPS *and* there's a HTTPS-specific CDN mapping, that mapping should
    // be used instead!
    $this
      ->configureHTTPS(TRUE, "https://cdn-a.com|.css .jpg .jpeg .png\nhttps://cdn-b.com|.zip\nhttps://cdn-c.com");
    $this
      ->assertMapping(FALSE, array(
      'css' => array(
        'https://cdn-a.com',
      ),
      'jpg' => array(
        'https://cdn-a.com',
      ),
      'jpeg' => array(
        'https://cdn-a.com',
      ),
      'png' => array(
        'https://cdn-a.com',
      ),
      'zip' => array(
        'https://cdn-b.com',
      ),
      '*' => array(
        'https://cdn-c.com',
      ),
    ), array(
      'cdn-a.com',
      'cdn-b.com',
      'cdn-c.com',
    ));

    // Ensure the default CDN mapping is used whenever a HTTP request occurs
    // and the CDN is marked to suppport HTTPS and there's a HTTPS-specific
    // CDN mapping.
    $this
      ->configureHTTPS(FALSE);
    $this
      ->assertMapping(FALSE, $parsed_mapping, $domains);
  }
  function testFileUrlAlterHook() {

    // Provide a very basic CDN mapping.
    $this
      ->variableSet(CDN_BASIC_MAPPING_VARIABLE, 'http://cdn-a.com');
    $filename = 'újjáépítésérol — 100% in B&W.jpg';
    $uri = "public://{$filename}";
    $this
      ->touchFile($uri);
    cdn_file_url_alter($uri);
    $expected = 'http://cdn-a.com' . base_path() . variable_get('file_public_path', conf_path() . '/files') . '/' . drupal_encode_path($filename);
    $this
      ->assertIdentical($uri, $expected, 'cdn_file_url_alter() works correctly.');

    // Test that private:// file URLs are not altered.
    $uri = "private://{$filename}";
    $expected = $uri;
    cdn_file_url_alter($uri);
    $this
      ->assertIdentical($uri, $expected, 'cdn_file_url_alter() excludes private:// files.');
  }

}
class CDNOriginPullFarFutureTestCase extends CDNUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Origin Pull mode — Far Future expiration',
      'description' => 'Verify Origin Pull mode\'s Far Future expiration functionality.',
      'group' => 'CDN',
    );
  }
  function setUp() {
    parent::setUp();
    $this
      ->loadFile('cdn.basic.inc');
    $this
      ->loadFile('cdn.basic.farfuture.inc');
    $this
      ->variableSet(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
    $this
      ->variableSet(CDN_BASIC_FARFUTURE_VARIABLE, TRUE);
  }

  /**
   * Assert a UFI mapping (and optionally set a mapping).
   *
   * @param $mapping
   *   The mapping to set; if FALSE, no new mapping will be set.
   * @param $parsed_reference
   *   The reference the parsed mapping will be compared to.
   * @param $message
   */
  function assertUFIMapping($mapping, $parsed_reference, $message = NULL) {
    if (is_null($message)) {
      $message = 'UFI mapping parsed correctly.';
    }
    if ($mapping !== FALSE) {
      $this
        ->variableSet(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, $mapping);
    }
    $parsed = _cdn_basic_farfuture_parse_raw_mapping(variable_get(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT));
    $this
      ->assertEqual($parsed_reference, $parsed, $message);
  }

  /**
   * Assert a UFI method. Must be run after a UFI mapping is asserted (and
   * set) by assertUFIMapping().
   *
   * @param $path
   *   The path to get a UFI for.
   * @param $ufi_method_reference
   *   The reference the resulting UFI method will be compared to.
   */
  function assertUFIMethod($path, $ufi_method_reference) {
    $ufi_mapping = _cdn_basic_farfuture_parse_raw_mapping(variable_get(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT));
    $this
      ->assertEqual($ufi_method_reference, cdn_basic_farfuture_get_ufi_method($path, $ufi_mapping), 'Correct UFI method applied.');
  }
  function testUFIMapping() {
    $default = CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_DEFAULT;
    $parsed_mapping = _cdn_basic_farfuture_parse_raw_mapping($default);
    $this
      ->assertUFIMapping(FALSE, $parsed_mapping, 'The default UFI mapping is set to sensible defaults.');

    // Growing complexity for a single-directory UFI.
    $this
      ->assertUFIMapping("sites/*|mtime", array(
      'sites/*' => array(
        '*' => array(
          'ufi method' => 'mtime',
          'specificity' => 20,
        ),
      ),
    ), 'Simple single-directory UFI mapping (step 1).');
    $this
      ->assertUFIMethod('sites/foo', 'mtime');
    $this
      ->assertUFIMapping("sites/*|mtime\nsites/*|.woff .ttf|md5_hash", array(
      'sites/*' => array(
        '*' => array(
          'ufi method' => 'mtime',
          'specificity' => 20,
        ),
        'woff' => array(
          'ufi method' => 'md5_hash',
          'specificity' => 21,
        ),
        'ttf' => array(
          'ufi method' => 'md5_hash',
          'specificity' => 21,
        ),
      ),
    ), 'Simple single-directory UFI mapping (step 2).');
    $this
      ->assertUFIMethod('sites/foo', 'mtime');
    $this
      ->assertUFIMethod('sites/foobambamb.woff', 'md5_hash');
    $this
      ->assertUFIMethod('sites/foo/bar/baz.ttf', 'md5_hash');
    $this
      ->assertUFIMapping("sites/*|mtime\nsites/*|.woff .ttf|md5_hash\nsites/*|.mov .mp4|perpetual", array(
      'sites/*' => array(
        '*' => array(
          'ufi method' => 'mtime',
          'specificity' => 20,
        ),
        'woff' => array(
          'ufi method' => 'md5_hash',
          'specificity' => 21,
        ),
        'ttf' => array(
          'ufi method' => 'md5_hash',
          'specificity' => 21,
        ),
        'mov' => array(
          'ufi method' => 'perpetual',
          'specificity' => 21,
        ),
        'mp4' => array(
          'ufi method' => 'perpetual',
          'specificity' => 21,
        ),
      ),
    ), 'Simple single-directory UFI mapping (step 2).');
    $this
      ->assertUFIMethod('sites/foo', 'mtime');
    $this
      ->assertUFIMethod('sites/foobambamb.woff', 'md5_hash');
    $this
      ->assertUFIMethod('sites/foo/bar/baz.ttf', 'md5_hash');
    $this
      ->assertUFIMethod('sites/movies/foo.mov', 'perpetual');
    $this
      ->assertUFIMethod('sites/movies/bar.mp4', 'perpetual');
  }
  function testFileUrlAlterHook() {

    // We don't want to test the UFI functionality here.
    $this
      ->variableSet(CDN_BASIC_FARFUTURE_UNIQUE_IDENTIFIER_MAPPING_VARIABLE, '*|perpetual');

    // Provide a very basic CDN mapping.
    $this
      ->variableSet(CDN_BASIC_MAPPING_VARIABLE, 'http://cdn-a.com');
    $filename = 'újjáépítésérol — 100% in B&W.jpg';
    $uri = "public://{$filename}";
    $this
      ->touchFile($uri);
    cdn_file_url_alter($uri);
    $path_info = pathinfo($filename);
    $expected = implode('/', array(
      'http://cdn-a.com' . base_path() . 'cdn/farfuture',
      drupal_hmac_base64('perpetual:forever' . $path_info['filename'], drupal_get_private_key() . drupal_get_hash_salt()),
      'perpetual:forever',
      variable_get('file_public_path', conf_path() . '/files'),
      drupal_encode_path($filename),
    ));
    $this
      ->assertIdentical($uri, $expected, 'cdn_file_url_alter() works correctly.');
  }

}
class CDNImageTestCase extends CDNUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Image and Download Link HTML altering',
      'description' => 'Verify that image and anchor link URLs inside HTML are rewritten correctly.',
      'group' => 'CDN',
    );
  }
  function setUp() {
    parent::setUp();
    $this
      ->loadFile('cdn.fallback.inc');
  }
  function testImage() {
    $cdn = 'http://cdn-a.com';
    $this
      ->variableSet(CDN_BASIC_MAPPING_VARIABLE, $cdn);
    $this
      ->variableSet(CDN_MODE_VARIABLE, CDN_MODE_BASIC);

    // Image altering type 1: "just image", i.e. "<img />".
    $template = function ($url) {
      return '<img alt="Foo!" src="' . $url . '" title="Bar!"/>';
    };

    // Simplest case possible.
    $img_url = base_path() . 'foo/bar/image.png';
    $html = $template($img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $img_url), $html, 'Image HTML correctly altered.');

    // Query strings should not be stripped
    $img_url = base_path() . 'foo/bar/image.png?foobar';
    $html = $template($img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $img_url), $html, 'Image HTML correctly altered (query string not stripped).');

    // In particular: not the query string used to generate image styles.
    $img_url = base_path() . 'foo/bar/image.png?itok=1234abcd';
    $html = $template($img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $img_url), $html, 'Image HTML correctly altered (image style query string not stripped).');

    // Edge case: a script generating an image is not (yet) supported.
    $img_url = base_path() . 'foo/bar/showimage?formula=12345.png';
    $html = $template($img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $img_url), $html, 'Image HTML correctly altered (query string not stripped).');

    // Image altering type 2: "linked image", i.e. "<a><img /></a>"..
    $template = function ($a_url, $img_url) {
      return '<a rel="gallery" href="' . $a_url . '" class="gallery-image"><img alt="Foo!" src="' . $img_url . '" title="Bar!" /></a>';
    };

    // Simplest case possible: a linked image linking to the same image.
    $img_base_url = base_path() . 'foo/bar/image';
    $a_url = $img_url = $img_base_url . '.png';
    $html = $template($a_url, $img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $a_url, $cdn . $img_url), $html, 'Linked image HTML correctly altered.');

    // Slightly more complex: a linked image linking to a derivative image.
    $img_url = $img_base_url . '-thumbnail.png?itok=5678wxyz';
    $html = $template($a_url, $img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $a_url, $cdn . $img_url), $html, 'Linked image HTML correctly altered.');

    // Slightly more complex: a linked derivative image linking to another
    // derivative image.
    $a_url = $img_base_url . '-large.png?itok=9012klmn';
    $img_url = $img_base_url . '-thumbnail.png?itok=5678wxyz';
    $html = $template($a_url, $img_url);
    cdn_html_alter_image_urls($html);
    $this
      ->assertIdentical($template($cdn . $a_url, $cdn . $img_url), $html, 'Linked image HTML correctly altered.');

    // Ensure that images linking to content (i.e. not a bigger version of the
    // image) don't get their anchors modified
    $a_url = base_path() . 'node';
    $html = $template($a_url, $img_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($a_url, $cdn . $img_url), $html, 'Linked image HTML correctly altered (anchor unmodified).');

    // Same, but now for a link with a query string.
    $a_url = base_path() . 'node?foobar';
    $html = $template($a_url, $img_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($a_url, $cdn . $img_url), $html, 'Linked image HTML correctly altered (anchor unmodified, even with query strings).');
  }
  function testOtherContentLink() {
    $cdn = 'http://cdn-a.com';
    $this
      ->variableSet(CDN_BASIC_MAPPING_VARIABLE, "{$cdn}|.png .pdf");
    $this
      ->variableSet(CDN_MODE_VARIABLE, CDN_MODE_BASIC);

    // Image altering type 1: "just image", i.e. "<img />".
    $template = function ($url) {
      return '<a href="' . $url . '">Download this!</a>';
    };

    // Simplest case possible.
    $a_url = base_path() . 'foo/bar/image.png';
    $html = $template($a_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($cdn . $a_url), $html, 'Other content link correctly altered.');

    // Query strings should not be stripped.
    $a_url = base_path() . 'foo/bar/image.pdf?foobar';
    $html = $template($a_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($cdn . $a_url), $html, 'Other content link correctly altered (query string not stripped).');

    // Edge case: a script generating an image is not (yet) supported.
    $a_url = base_path() . 'foo/bar/image.gif';
    $html = $template($a_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($a_url), $html, 'Other content link correctly NOT altered (extension does not map).');

    // Edge case: a script generating an image is not (yet) supported.
    $a_url = base_path() . 'foo/bar/showimage?formula=12345.png';
    $html = $template($a_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($a_url), $html, 'Other content link correctly NOT altered (extension not pulled from query string).');
  }
  function testOtherContentLinkWrappingImage() {
    $cdn = 'http://cdn-a.com';
    $this
      ->variableSet(CDN_BASIC_MAPPING_VARIABLE, "{$cdn}|.png .jpeg");
    $this
      ->variableSet(CDN_MODE_VARIABLE, CDN_MODE_BASIC);

    // Image altering type 2: "linked image", i.e. "<a><img /></a>"..
    $template = function ($a_url, $img_url) {
      return '<a href="' . $a_url . '"><img src="' . $img_url . '" /></a>';
    };

    // Test that images linking to a URL with an extension that matches a
    // mapped extension, even if the extension is different than the image,
    // are modified.
    $a_url = base_path() . 'node.jpeg';
    $img_url = base_path() . 'foo/bar/image.png';
    $html = $template($a_url, $img_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($cdn . $a_url, $cdn . $img_url), $html, 'Other content link wrapping image correctly altered.');
    $a_url = base_path() . 'node.gif';
    $html = $template($a_url, $img_url);
    $html = cdn_post_render_html_alter($html);
    $this
      ->assertIdentical($template($a_url, $cdn . $img_url), $html, 'Other content link wrapping correctly NOT altered (extension does not map).');
  }

}
class CDNCssUrlTestCase extends CDNWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'CSS URLs',
      'description' => 'Verify URLs in CSS files are rewritten properly.',
      'group' => 'CDN',
    );
  }
  function setUp() {
    parent::setUp();
    $this
      ->loadFile('cdn.basic.css.inc');
    variable_set(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
    variable_set(CDN_BASIC_FARFUTURE_VARIABLE, TRUE);
  }

  // Makes sure the URL components are preserved.
  function testCssUrl() {
    global $base_url;
    $css_info = array(
      array(
        'type' => 'file',
        'data' => drupal_get_path('module', 'cdn') . '/tests/css/orig/url.css',
      ),
    );
    try {

      // Make sure the URL in the supplied CSS file matches what we expect.
      // trim() is needed to avoid issues with differences in newlines.
      $this
        ->assertEqual(trim(file_get_contents(_cdn_build_css_cache($css_info))), '.home{background:url(' . $base_url . '/' . drupal_get_path('module', 'cdn') . '/tests/css/orig/geo-md-webfont.eot?#iefix);}');
    } catch (Exception $exc) {
      $this
        ->fail($exc);
    }
  }

}
class CDNSEOTestCase extends CDNUnitTestCase {
  public static function getInfo() {
    return array(
      'name' => 'SEO',
      'description' => 'Verify SEO duplicate content prevention.',
      'group' => 'CDN',
    );
  }
  function testSEO() {

    // Test with and without SEO duplicate content prevention. Each case is tested
    // with three sorts of paths:
    // - page path (without any extension and with all of the three default
    //   "allowed" extensions, meaning that they c)
    // - file path
    // - generated file path
    // SEO duplicate content prevention disabled.
    $this
      ->variableSet(CDN_SEO_REDIRECT_VARIABLE, FALSE);
    $this
      ->setUserAgent('Amazon CloudFront');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.htm.'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.htm'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.php'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('misc/jquery.js'), FALSE, 'Disabled SEO duplicate content prevention is respected for file paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('sites/default/files/styles/thumbnail/foobar.png'), FALSE, 'Disabled SEO duplicate content prevention is respected for generated file paths.');
    $this
      ->setUserAgent($this
      ->randomName());
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.html'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.htm'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.php'), FALSE, 'Disabled SEO duplicate content prevention is respected for page paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('misc/jquery.js'), FALSE, 'Disabled SEO duplicate content prevention is respected for file paths.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('sites/default/files/styles/thumbnail/foobar.png'), FALSE, 'Disabled SEO duplicate content prevention is respected for generated file paths.');

    // SEO page request duplicate content prevention enabled.
    $this
      ->variableSet(CDN_SEO_REDIRECT_VARIABLE, TRUE);
    $this
      ->setUserAgent('The Amazon CloudFront User Agent!');

    // Note that this is a superstring of the provided CDN user agent "Amazon CloudFront"!
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1'), url('node/1', array(
      'absolute' => TRUE,
    )), 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.html'), url('node/1.html', array(
      'absolute' => TRUE,
    )), 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.htm'), url('node/1.htm', array(
      'absolute' => TRUE,
    )), 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.php'), url('node/1.php', array(
      'absolute' => TRUE,
    )), 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('misc/jquery.js'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a file path: the response is not a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('sites/default/files/styles/thumbnail/foobar.png'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a CDN UA requests a generated file path: the response is not a redirect.');
    $this
      ->setUserAgent($this
      ->randomName());
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.htm'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.html'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('node/1.php'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a page path: the response is a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('misc/jquery.js'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a file path: the response is not a redirect.');
    $this
      ->assertIdentical(_cdn_seo_should_redirect('sites/default/files/styles/thumbnail/foobar.png'), FALSE, 'Enabled SEO duplicate content prevention works correctly when a non-CDN UA requests a generated file path: the response is not a redirect.');
  }

}