skipClasses[__CLASS__] = TRUE; } /** * Set up an administrative user account and testing keys. */ function setUp() { // Grab the configured endpoints from the variables because the // parent::setup() call will clear our variables. $mollom_api_endpoint = variable_get('mollom_api_endpoint', ''); $mollom_test_api_endpoint = variable_get('mollom_test_api_endpoint', ''); // Re-initialize stored session_id and watchdog messages. $this->resetResponseID(); $this->messages = array(); $modules = func_get_args(); $modules = (isset($modules[0]) ? $modules[0] : array()); // Automatically enable local testing server implementation. if (strstr($this->mollomClass, 'Local') && !in_array('mollom_test_server', $modules)) { $modules[] = 'mollom_test_server'; } // If not explicitly disabled by a test, setup with Mollom. if (empty($this->disableDefaultSetup)) { $modules[] = 'mollom'; } // Database logging is unconditionally required to assert watchdog messages. $modules[] = 'dblog'; parent::setUp($modules); // Save the Mollom client implementation to use for running the tests. variable_set('mollom_class', $this->mollomClass); // Save the flag telling the testing client implementation whether to // automatically create testing API keys. variable_set('mollom_testing_create_keys', $this->createKeys); // Set the API server endpoints. if ($mollom_api_endpoint) { variable_set('mollom_api_endpoint', $mollom_api_endpoint); } if ($mollom_test_api_endpoint) { variable_set('mollom_test_api_endpoint', $mollom_test_api_endpoint); } // Disable testing mode warnings. // drupal_set_message() starts a session, which disables page caching, and // in turn, page/form cache related tests would not behave correctly. variable_set('mollom_testing_mode_omit_warning', TRUE); // Log all messages. variable_set('mollom_log_minimum_severity', WATCHDOG_DEBUG); // D7's new default theme Bartik is bogus in various locations, which leads // to failing tests. // @todo Remove this override. variable_set('theme_default', 'garland'); // If not explicitly disabled by a test, setup and validate testing keys, // and create a default admin user. if (empty($this->disableDefaultSetup)) { $permissions = array( 'access administration pages', 'administer mollom', 'administer content types', 'administer permissions', 'administer users', 'bypass node access', ); if (module_exists('comment')) { $permissions[] = 'access comments'; $permissions[] = 'post comments'; $permissions[] = 'skip comment approval'; $permissions[] = 'administer comments'; } $this->admin_user = $this->drupalCreateUser($permissions); if ($this->createKeys) { $this->setKeys(); $this->assertValidKeys(); } } } function tearDown() { // Delete the testing site. // Not (always) possible when working with local testing server, since // getServerRecord() removes server records upon retrieval, so the site // record may no longer exist. // @todo Only remove keys after running the last test in a test case. /* if ($this->mollomClass == 'MollomDrupalTest') { $this->deleteKeys(); } */ // Capture any (remaining) watchdog messages. $this->assertMollomWatchdogMessages(); parent::tearDown(); } /** * Assert any watchdog messages based on their severity. * * This function can be (repeatedly) invoked to assert new watchdog messages. * All watchdog messages with a higher severity than WATCHDOG_NOTICE are * considered as "severe". * * @param $max_severity * (optional) A maximum watchdog severity level message constant that log * messages must have to pass the assertion. All messages with a higher * severity will fail. Defaults to WATCHDOG_NOTICE. If a severity level * higher than WATCHDOG_NOTICE is passed, then at least one severe message * is expected. * * @todo Add this to Drupal core. */ protected function assertMollomWatchdogMessages($max_severity = WATCHDOG_NOTICE) { // Ensure that all messages have been written before attempting to verify // them. Actions executed within the test class may lead to log messages, // but those get only logged when hook_exit() is triggered. // mollom.module may not be installed by a test and thus not loaded yet. drupal_load('module', 'mollom'); mollom_log_write(); module_load_include('inc', 'dblog', 'dblog.admin'); $this->messages = array(); $query = db_select('watchdog', 'w') ->fields('w') ->orderBy('w.timestamp', 'ASC'); // The comparison logic applied in this function is a bit confusing, since // the values of watchdog severity level constants defined by RFC 3164 are // negated to their actual "severity level" meaning: // WATCHDOG_EMERGENCY is 0, WATCHDOG_NOTICE is 5, WATCHDOG_DEBUG is 7. $fail_expected = ($max_severity < WATCHDOG_NOTICE); $had_severe_message = FALSE; foreach ($query->execute() as $row) { $this->messages[$row->wid] = $row; // Only messages with a maximum severity of $max_severity or less severe // messages must pass. More severe messages need to fail. See note about // severity level constant values above. $output = theme_dblog_message(array('event' => $row, 'link' => FALSE)); if ($row->severity >= $max_severity) { // Visually separate debug log messages from other messages. if ($row->severity == WATCHDOG_DEBUG) { $this->error($output, 'User notice'); } else { $this->pass(check_plain($row->type) . ': ' . $output, t('Watchdog')); } } else { $this->fail(check_plain($row->type) . ': ' . $output, t('Watchdog')); } // In case a severe message is expected, non-severe messages always pass, // since we would trigger a false positive test failure otherwise. // However, in order to actually assert the expectation, there must have // been at least one severe log message. $had_severe_message = ($had_severe_message || $row->severity < WATCHDOG_NOTICE); } // Assert that there was a severe message, in case we expected one. if ($fail_expected && !$had_severe_message) { $this->fail(t('Severe log message was found.'), t('Watchdog')); } // Delete processed watchdog messages. if (!empty($this->messages)) { $seen_ids = array_keys($this->messages); db_delete('watchdog')->condition('wid', $seen_ids)->execute(); } } /** * Assert that the Mollom session id remains the same. * * The Mollom session id is only known to one server. If we are communicating * with a different Mollom server (due to a refreshed server list or being * redirected), then we will get a new session_id. * * @param $type * The type of ID to assert; e.g., 'contentId', 'captchaId'. * @param $id * The ID of $type in the last request, as returned from Mollom. * @param $new_expected * (optional) Boolean indicating whether a new ID is expected; e.g., after * incorrectly solving a CAPTCHA. */ protected function assertResponseID($type, $id, $new_expected = FALSE) { if (!isset($this->responseIds[$type]) || $new_expected) { // Use assertTrue() instead of pass(), to test !empty(). $this->assertTrue($id, t('New %type: %id', array( '%type' => $type, '%id' => $id, ))); $this->responseIds[$type] = $id; } else { $this->assertSame($type, $id, $this->responseIds[$type]); } return $this->responseIds[$type]; } /** * Reset the statically cached Mollom session id. * * @param $type * The type of ID to reset; e.g., 'contentId', 'captchaId'. */ protected function resetResponseID($type = NULL) { if (isset($type)) { unset($this->responseIds[$type]); } else { unset($this->responseIds); } } /** * Assert a Mollom session id in a form. * * This is a wrapper around assertResponseID() allows to assert that a proper * Mollom session id is found in the form contained in the internal browser * output. The usual flow is: * - drupalGet() or drupalPost() requests or submits a form. * - drupalGet() and drupalPost() invoke assertMollomWatchdogMessages() * internally, which records all new watchdog messages. * - This function, assertResponseIDInForm(), is invoked to assert that there * is a Mollom session id and, depending on the recorded watchdog messages, * that it either equals the last known session id or the new session id is * used for future comparisons in case of a server redirect. * - The return value of this function is used to invoke assertMollomData(), * to verify that the proper session id was stored in the database. * * @param $type * The type of ID to assert; e.g., 'contentId', 'captchaId'. * @param $new_expected * (optional) Boolean indicating whether a new ID is expected; e.g., after * incorrectly solving a CAPTCHA. */ protected function assertResponseIDInForm($type, $new_expected = FALSE) { $id = $this->getFieldValueByName('mollom[' . $type . ']'); return $this->assertResponseID($type, $id, $new_expected); } /** * Instantiate a Mollom client and make it available on $this->mollom; * * @param $force * (Optional) If true, then a new class is always instantiated. */ protected function getClient($force = FALSE) { if ($force || !isset($this->mollom)) { // mollom.module may not be enabled in the parent site executing the test. drupal_load('module', 'mollom'); $this->mollom = mollom($this->mollomClass, $force); } return $this->mollom; } /** * Setup Mollom API keys for testing. * * New keys are only created if MollomWebTestCase::$createKeys or respectively * the 'mollom_testing_create_keys' variable is set to TRUE. * * @param bool $once * (optional) Whether to disable the 'mollom_testing_create_keys' variable * after the first call (and thus omit API key verifications on every page * request). Defaults to FALSE; i.e., API keys are verified repetitively. * * @see MollomWebTestCase::$createKeys * @see MollomDrupalTest::__construct() * @see MollomDrupalTest::createKeys() */ protected function setKeys($once = FALSE) { // Instantiate a Mollom client class. // Depending on MollomWebTestCase::$createKeys and ultimately the // 'mollom_testing_create_keys' variable, MollomDrupalTest::__construct() // will automatically setup testing API keys. $this->getClient(); $this->mollom->createKeys(); // Make API keys available to test methods. if (!empty($this->mollom->publicKey)) { $this->publicKey = $this->mollom->publicKey; $this->privateKey = $this->mollom->privateKey; // Multiple tests might be executed in a single request. Every test sets // up a new child site from scratch. The Mollom class with testing API // keys still exists in the test, but the configuration is gone. $this->mollom->saveKeys(); } if ($once) { variable_set('mollom_testing_create_keys', FALSE); } } /** * Calls _mollom_status() directly to verify that current API keys are valid. */ protected function assertValidKeys() { $status = _mollom_status(TRUE); $this->assertMollomWatchdogMessages(); $this->assertIdentical($status['isVerified'], TRUE, t('Mollom servers can be contacted and testing API keys are valid.')); } /** * Deletes the current testing site. */ protected function deleteKeys() { if (!empty($this->mollom->publicKey)) { $this->mollom->deleteSite($this->mollom->publicKey); } unset($this->publicKey, $this->privateKey, $this->mollom); } /** * Saves a mollom_form entity to protect a given form with Mollom. * * @param string $form_id * The form id to protect. * @param int $mode * The protection mode. Defaults to MOLLOM_MODE_ANALYSIS. * @param array $values * (optional) An associative array of properties to additionally set on the * mollom_form entity. * * @return int * The save status, as returned by mollom_form_save(). */ protected function setProtection($form_id, $mode = MOLLOM_MODE_ANALYSIS, $values = array()) { if (!$mollom_form = mollom_form_load($form_id)) { $mollom_form = mollom_form_new($form_id); } $mollom_form['mode'] = $mode; if ($values) { foreach ($values as $property => $value) { $mollom_form[$property] = $value; } } $status = mollom_form_save($mollom_form); return $status; } /** * Configure Mollom protection for a given form. * * @param $form_id * The form id to configure. * @param $mode * The Mollom protection mode for the form. * @param $fields * (optional) A list of form elements to enable for text analysis. If * omitted and the form registers individual elements, all fields are * enabled by default. * @param $edit * (optional) An array of POST data to pass through to drupalPost() when * configuring the form's protection. */ protected function setProtectionUI($form_id, $mode = MOLLOM_MODE_ANALYSIS, $fields = NULL, $edit = array()) { // Always start from overview page, also to make debugging easier. $this->drupalGet('admin/config/content/mollom'); // Determine whether the form is already protected. $exists = db_query_range('SELECT 1 FROM {mollom_form} WHERE form_id = :form_id', 0, 1, array(':form_id' => $form_id))->fetchField(); // Add a new form. if (!$exists) { $this->clickLink(t('Add form')); $add_form_edit = array( 'mollom[form_id]' => $form_id, ); $this->drupalPost(NULL, $add_form_edit, t('Next')); } // Edit an existing form. else { $this->assertLinkByHref('admin/config/content/mollom/manage/' . $form_id); $this->drupalGet('admin/config/content/mollom/manage/' . $form_id); } $edit += array( 'mollom[mode]' => $mode, ); // Process the enabled fields. $form_list = mollom_form_list(); $form_info = mollom_form_info($form_id, $form_list[$form_id]['module']); if (!empty($form_info['elements'])) { $edit += array( 'mollom[checks][spam]' => TRUE, ); } foreach (array_keys($form_info['elements']) as $field) { if (!isset($fields) || in_array($field, $fields)) { // If the user specified all fields by default or to include this // field, set its checkbox value to TRUE. $edit['mollom[enabled_fields][' . rawurlencode($field) . ']'] = TRUE; } else { // Otherwise set the field's checkbox value to FALSE. $edit['mollom[enabled_fields][' . rawurlencode($field) . ']'] = FALSE; } } $this->drupalPost(NULL, $edit, t('Save')); if (!$exists) { $this->assertText(t('The form protection has been added.')); } else { $this->assertText(t('The form protection has been updated.')); } } /** * Remove Mollom protection for a given form. * * @param $form_id * The form id to configure. */ protected function delProtection($form_id) { // Determine whether the form is protected. $exists = db_query_range('SELECT 1 FROM {mollom_form} WHERE form_id = :form_id', 0, 1, array(':form_id' => $form_id)); if ($exists) { $this->drupalGet('admin/config/content/mollom/unprotect/' . $form_id); $this->assertText(t('Mollom will no longer protect this form from spam.'), t('Unprotect confirmation form found.')); $this->drupalPost(NULL, array(), t('Confirm')); } } /** * Assert that Mollom session data was stored for a submission. * * @param $entity * The entity type to search for in {mollom}. * @param $id * The entity id to search for in {mollom}. * @param $response_type * (optional) The type of ID to assert; e.g., 'contentId', 'captchaId'. * @param $response_id * (optional) The ID of $type to assert additionally. */ protected function assertMollomData($entity, $id, $response_type = '', $response_id = NULL) { $data = mollom_data_load($entity, $id); $this->assertTrue($data->id, t('Mollom session data for %entity @id exists:
@data
', array( '%entity' => $entity, '@id' => $id, '@data' => var_export($data, TRUE), ))); if (isset($response_id)) { $this->assertSame(t('Stored @type ID', array('@type' => $response_type)), $data->$response_type, $response_id); } return $data; } /** * Assert that no Mollom session data exists for a certain entity. */ protected function assertNoMollomData($entity, $id) { $data = mollom_data_load($entity, $id); $this->assertFalse($data, t('No Mollom session data exists for %entity @id.', array('%entity' => $entity, '@id' => $id))); } /** * Assert that the CAPTCHA field is found on the current page. */ protected function assertCaptchaField() { $inputs = $this->xpath('//input[@type=:type and @name=:name]', array( ':type' => 'text', ':name' => 'mollom[captcha]', )); $labels = $this->xpath('//label[@for=:for]/span[@class=:class]', array( ':for' => 'edit-mollom-captcha', ':class' => 'form-required', )); $this->assert(!empty($inputs[0]) && !empty($labels[0]), 'Required CAPTCHA field found.'); $image = $this->xpath('//img[@alt=:alt]', array(':alt' => t("Type the characters you see in this picture."))); $this->assert(!empty($image), 'CAPTCHA image found.'); } /** * Assert that the CAPTCHA field is not found on the current page. */ protected function assertNoCaptchaField() { $this->assertNoText($this->unsure_message); $this->assertNoText($this->incorrect_message); $this->assertNoFieldByXPath('//input[@type="text"][@name="mollom[captcha]"]', '', 'CAPTCHA field not found.'); $image = $this->xpath('//img[@alt=:alt]', array(':alt' => t("Type the characters you see in this picture."))); $this->assert(empty($image), 'CAPTCHA image not found.'); } /** * Assert that the privacy policy link is found on the current page. */ protected function assertPrivacyLink() { $elements = $this->xpath('//div[contains(@class, "mollom-privacy")]'); $this->assertTrue($elements, t('Privacy policy container found.')); } /** * Assert that the privacy policy link is not found on the current page. */ protected function assertNoPrivacyLink() { $elements = $this->xpath('//div[contains(@class, "mollom-privacy")]'); $this->assertFalse($elements, t('Privacy policy container not found.')); } /** * Test submitting a form with a correct CAPTCHA value. * * @param $url * The URL of the form, or NULL to use the current page. * @param $edit * An array of form values used in drupalPost(). * @param $button * The text of the form button to click in drupalPost(). * @param $success_message * An optional message to test does appear after submission. */ protected function postCorrectCaptcha($url, array $edit = array(), $button, $success_message = '') { if (isset($url)) { $this->drupalGet($url); } $this->assertCaptchaField(); $edit['mollom[captcha]'] = 'correct'; $this->drupalPost(NULL, $edit, $button); $this->assertNoCaptchaField(); $this->assertNoText($this->incorrect_message); if ($success_message) { $this->assertText($success_message); } } /** * Test submitting a form with an incorrect CAPTCHA value. * * @param $url * The URL of the form, or NULL to use the current page. * @param $edit * An array of form values used in drupalPost(). * @param $button * The text of the form button to click in drupalPost(). * @param $success_message * An optional message to test does not appear after submission. */ protected function postIncorrectCaptcha($url, array $edit = array(), $button, $success_message = '') { if (isset($url)) { $this->drupalGet($url); } $this->assertCaptchaField(); $edit['mollom[captcha]'] = 'incorrect'; $before_url = $this->getUrl(); $this->drupalPost(NULL, $edit, $button); $this->assertCaptchaField(); $this->assertText($this->incorrect_message); if ($success_message) { $this->assertNoText($success_message); } } /** * Test submitting a form with 'spam' values. * * @param $url * The URL of the form, or NULL to use the current page. * @param $spam_fields * An array of form field names to inject spam content into. * @param $edit * An array of non-spam form values used in drupalPost(). * @param $button * The text of the form button to click in drupalPost(). * @param $success_message * An optional message to test does not appear after submission. */ protected function assertSpamSubmit($url, array $spam_fields, array $edit = array(), $button, $success_message = '') { $edit += array_fill_keys($spam_fields, 'spam'); $this->drupalPost($url, $edit, $button); $this->assertNoCaptchaField(); $this->assertText($this->spam_message); if ($success_message) { $this->assertNoText($success_message); } } /** * Test submitting a form with 'ham' values. * * @param $url * The URL of the form, or NULL to use the current page. * @param $ham_fields * An array of form field names to inject ham content into. * @param $edit * An array of non-spam form values used in drupalPost(). * @param $button * The text of the form button to click in drupalPost(). * @param $success_message * An optional message to test does appear after submission. */ protected function assertHamSubmit($url, array $ham_fields, array $edit = array(), $button, $success_message = '') { $edit += array_fill_keys($ham_fields, 'ham'); $this->drupalPost($url, $edit, $button); $this->assertNoCaptchaField($url); $this->assertNoText($this->spam_message); if ($success_message) { $this->assertText($success_message); } } /** * Test submitting a form with unsure values and resulting CAPTCHA submissions. * * @param $url * The URL of the form, or NULL to use the current page. * @param $unsure_fields * An array of form field names to inject unsure content into. * @param $edit * An array of non-spam form values used in drupalPost(). * @param $button * The text of the form button to click in drupalPost(). * @param $success_message * An optional message to test does appear after sucessful form and CAPTCHA * submission. */ protected function assertUnsureSubmit($url, array $unsure_fields, array $edit = array(), $button, $success_message = '') { $edit += array_fill_keys($unsure_fields, 'unsure'); $this->drupalPost($url, $edit, $button); $this->assertCaptchaField(); $this->assertText($this->unsure_message); if ($success_message) { $this->assertNoText($success_message); } $this->postIncorrectCaptcha(NULL, $edit, $button, $success_message); $this->postCorrectCaptcha(NULL, $edit, $button, $success_message); } /** * Asserts that the most recently sent mail contains a "Report to Mollom" link. * * Contrary to DrupalWebTestCase::assertMail(), this function removes the last * sent mail from the internally recorded stack. * * @param string $entity_type * (optional) The expected entity type contained in the report link. * Defaults to 'mollom_content'. * * @return array|false * FALSE if the link was not found, or an associative array containing: * - url: The full report link URL. * - entity: The entity type contained in the report link URL. * - id: The entity ID contained in the report link URL. * - mail: The full mail message array, as recorded by TestingMailSystem. * - external: TRUE. * The array can be passed directly as $options to drupalGet(). */ protected function assertMailMollomReportLink($entity_type = 'mollom_content') { // Grab the last sent mail. // @see DrupalWebTestCase::assertMail() $captured_emails = variable_get('drupal_test_email_collector', array()); $message = array_pop($captured_emails); variable_set('drupal_test_email_collector', $captured_emails); $found = FALSE; // Determine the report URI pattern for the passed entity type. $path = FALSE; foreach (mollom_form_list() as $form_id => $info) { if (isset($info['entity']) && $info['entity'] == $entity_type && isset($info['report path'])) { $path = $info['report path']; break; } } if ($path) { $path = strtr($path, array('%id' => '([^\s]+)')); if (preg_match('@http.+?' . $path . '@', $message['body'], $matches)) { $found = array( 'url' => $matches[0], 'entity' => $entity_type, 'id' => $matches[1], 'mail' => $message, 'external' => TRUE, ); } } elseif (preg_match('@http.+?mollom/report/([^/]+)/([^\s]+)@', $message['body'], $matches)) { $found = array( 'url' => $matches[0], 'entity' => $matches[1], 'id' => $matches[2], 'mail' => $message, 'external' => TRUE, ); } $this->assertTrue($found, t('Report to Mollom link found in e-mail: %url', array('%url' => $found['url']))); $this->assertSame('Report link entity type', $found['entity'], $entity_type); $this->assertMollomData($found['entity'], $found['id']); return $found; } /** * Asserts that the most recently sent mail does NOT contain a "Report to Mollom" link. * * Contrary to DrupalWebTestCase::assertMail(), this function removes the last * sent mail from the internally recorded stack. * * @return bool * TRUE if no link was found, FALSE otherwise. */ protected function assertNoMailMollomReportLink() { // Grab the last sent mail. // @see DrupalWebTestCase::assertMail() $captured_emails = variable_get('drupal_test_email_collector', array()); $message = array_pop($captured_emails); if (empty($message)) { $this->fail('No mail to assert.'); return; } variable_set('drupal_test_email_collector', $captured_emails); $found = preg_match('@http.+?mollom/report/([^/]+)/([^\s]+)@', $message['body'], $matches); $this->assertFalse($found, 'Report to Mollom link not found in e-mail.'); if ($found) { debug($message); } } /** * Retrieve a field value by ID. */ protected function getFieldValueByID($id) { $fields = $this->xpath($this->constructFieldXpath('id', $id)); return (string) $fields[0]['value']; } /** * Retrieve a field value by name. */ protected function getFieldValueByName($name) { $fields = $this->xpath($this->constructFieldXpath('name', $name)); return (string) $fields[0]['value']; } /** * Retrieve sent request parameter values from testing server implementation. * * @param $resource * (optional) The resource name to retrieve submitted values from. Defaults * to 'content'. * @param $retain * (optional) Whether to retain the (last) record being read. Defaults to * FALSE; i.e., the record being read is removed. * * @see MollomWebTestCase::resetServerRecords() */ protected function getServerRecord($resource = 'content', $retain = FALSE) { $function = 'mollom_test_server_' . $resource; // Ensure that we do not read obsolete/outdated data from variable_get()'s // static cache while variables might have been updated in the child site. $this->refreshVariables(); // Retrieve last recorded values. $storage = variable_get($function, array()); $return = ($retain ? end($storage) : array_shift($storage)); variable_set($function, $storage); return $return; } /** * Resets recorded XML-RPC values. * * @param $resource * (optional) The resource name to reset records of. Defaults to 'content'. * * @see MollomWebTestCase::getServerRecord() */ protected function resetServerRecords($resource = 'content') { $function = 'mollom_test_server_' . $resource; // Delete the variable. variable_del($function); } /** * Wraps drupalGet() for additional watchdog message assertion. * * @param $options * In addition to regular $options that are passed to url(): * - watchdog: (optional) Boolean whether to assert that only non-severe * watchdog messages have been logged. Defaults to TRUE. Use FALSE to * negate the watchdog message severity assertion. * * @see DrupalWebTestCase->drupalGet() * @see MollomWebTestCase->assertMollomWatchdogMessages() * @see MollomWebTestCase->assertResponseID() */ protected function drupalGet($path, array $options = array(), array $headers = array()) { $output = parent::drupalGet($path, $options, $headers); $options += array('watchdog' => WATCHDOG_NOTICE); $this->assertMollomWatchdogMessages($options['watchdog']); return $output; } /** * Wraps drupalPost() for additional watchdog message assertion. * * @param $options * In addition to regular $options that are passed to url(): * - watchdog: (optional) Boolean whether to assert that only non-severe * watchdog messages have been logged. Defaults to TRUE. Use FALSE to * negate the watchdog message severity assertion. * * @see MollomWebTestCase->assertMollomWatchdogMessages() * @see MollomWebTestCase->assertResponseID() * @see DrupalWebTestCase->drupalPost() */ protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { $output = parent::drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post); $options += array('watchdog' => WATCHDOG_NOTICE); $this->assertMollomWatchdogMessages($options['watchdog']); return $output; } /** * Asserts that two values belonging to the same variable are equal. * * Checks to see whether two values, which belong to the same variable name or * identifier, are equal and logs a readable assertion message. * * @param $name * A name or identifier to use in the assertion message. * @param $first * The first value to check. * @param $second * The second value to check. * * @return * TRUE if the assertion succeeded, FALSE otherwise. * * @see MollomWebTestCase::assertNotSame() * * @todo D8: Move into core. This improved assertEqual() did not get into D7, * since the function signature differs and it's plenty of work to manually * update all assertEqual() invocations throughout all tests. */ protected function assertSame($name, $first, $second) { $message = t("@name: @first is equal to @second.", array( '@name' => $name, '@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE), )); $this->assertEqual($first, $second, $message); } /** * Asserts that two values belonging to the same variable are not equal. * * Checks to see whether two values, which belong to the same variable name or * identifier, are not equal and logs a readable assertion message. * * @param $name * A name or identifier to use in the assertion message. * @param $first * The first value to check. * @param $second * The second value to check. * * @return * TRUE if the assertion succeeded, FALSE otherwise. * * @see MollomWebTestCase::assertSame() */ protected function assertNotSame($name, $first, $second) { $message = t("@name: '@first' is not equal to '@second'.", array( '@name' => $name, '@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE), )); $this->assertNotEqual($first, $second, $message); } /** * Enables aggressive page caching options to resemble reverse-proxies. */ protected function enablePageCache() { variable_set('cache', 1); variable_set('page_cache_maximum_age', 180); // A minimum cache lifetime causes cache_clear_all() to start a session. //variable_set('cache_lifetime', 60); } /** * Asserts a successful mollom_test_form submission. * * @param $old_mid * (optional) The existing test record id to assert. */ protected function assertTestSubmitData($old_mid = NULL) { $this->assertText('Successful form submission.'); $mid = $this->getFieldValueByName('mid'); if (isset($old_mid)) { $this->assertSame('Test record id', $mid, $old_mid); } else { $this->assertTrue($mid > 0, t('Test record id @id found.', array('@id' => $mid))); } return $mid; } /** * Get the URL for a link by link text. * Will return the first link found with this link text by default, or a later * one if an index is given. Match is case sensitive with normalized space. * The label is translated label. * * @param $label * Text between the anchor tags. * @param $index * Link position counting from zero. * @return * Url on success, or FALSE if not found. */ protected function getLink($label, $index = 0) { $urls = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label)); if (isset($urls[$index])) { return $this->getAbsoluteUrl($urls[$index]['href']); } return FALSE; } } /** * Tests testing mode functionality. */ class MollomTestingModeTestCase extends MollomWebTestCase { /** * Overrides MollomWebTestCase::$mollomClass. * * In order to test toggling of the testing mode, ensure the regular class for * production usage is used. */ protected $mollomClass = 'MollomDrupal'; /** * Prevent automated setup of testing keys. */ protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Testing mode', 'description' => 'Tests toggling of testing mode.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); // Enable testing mode warnings. variable_del('mollom_testing_mode_omit_warning'); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer mollom', )); } /** * Tests enabling and disabling of testing mode. */ function testTestingMode() { $this->drupalLogin($this->admin_user); // Protect mollom_test_form. $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS); variable_set('mollom_fallback', MOLLOM_FALLBACK_ACCEPT); // Setup production API keys and expected languages. They must be retained. $publicKey = 'the-invalid-mollom-api-key-value'; $privateKey = 'the-invalid-mollom-api-key-value'; $expectedLanguages = array('en','de'); $edit = array( 'mollom_public_key' => $publicKey, 'mollom_private_key' => $privateKey, 'mollom_languages_expected[]' => $expectedLanguages, ); $this->drupalGet('admin/config/content/mollom/settings'); $this->assertText('The Mollom API keys are not configured yet.'); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertNoText('The Mollom API keys are not configured yet.'); $this->assertText(t('The configuration options have been saved.')); $this->assertText('The configured Mollom API keys are invalid.'); $this->drupalLogout(); // Verify that spam can be posted, since testing mode is disabled and API // keys are invalid. $edit = array( 'title' => $this->randomString(), 'body' => 'spam', ); $this->drupalGet('mollom-test/form'); $this->drupalPost(NULL, $edit, 'Submit', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText('Successful form submission.'); // Enable testing mode. $this->drupalLogin($this->admin_user); $edit = array( 'mollom_testing_mode' => 1, ); $this->drupalGet('admin/config/content/mollom/settings', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText('The configured Mollom API keys are invalid.'); $this->drupalPost(NULL, $edit, t('Save configuration')); $this->assertNoText('The Mollom API keys are not configured yet.'); $this->assertNoText('The configured Mollom API keys are invalid.'); $this->assertText(t('Mollom testing mode is still enabled. !admin-message', array('!admin-message' => ''))); // Verify that expected languages were retained. foreach($expectedLanguages as $lang) { $this->assertOptionSelected('mollom_languages_expected', $lang); } $this->drupalLogout(); // Verify presence of testing mode warning. $this->drupalGet('mollom-test/form'); $this->assertText('Mollom testing mode is still enabled.'); // Verify that no spam can be posted with testing mode enabled. $edit = array( 'title' => $this->randomString(), 'body' => 'spam', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertText($this->spam_message); $this->assertNoText('Successful form submission.'); // Disable testing mode. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom/settings'); $this->assertText('Mollom testing mode is still enabled.'); $edit = array( 'mollom_testing_mode' => FALSE, ); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText(t('The configuration options have been saved.')); $this->assertText('The configured Mollom API keys are invalid.'); $this->assertNoText('Mollom testing mode is still enabled.'); // Verify that production API keys still exist. $this->assertFieldByName('mollom_public_key', $publicKey); $this->assertFieldByName('mollom_private_key', $privateKey); foreach($expectedLanguages as $lang) { $this->assertOptionSelected('mollom_languages_expected', $lang); } } } /** * Tests module installation and global status handling. */ class MollomInstallationTestCase extends MollomWebTestCase { protected $mollomClass = 'MollomDrupalTestLocal'; protected $disableDefaultSetup = TRUE; protected $createKeys = FALSE; public static function getInfo() { return array( 'name' => 'Installation and key handling', 'description' => 'Tests module installation and key error handling.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom_test')); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer site configuration', 'administer modules', 'administer permissions', )); $this->web_user = $this->drupalCreateUser(array()); } /** * Tests status handling after installation. * * We walk through a regular installation of the Mollom module instead of using * setUp() to ensure that everything works as expected. * * Note: Partial error messages tested here; hence, no t(). */ function testInstallationProcess() { $message_short_invalid = t('The configured Mollom API keys are invalid.'); $message_invalid = t('The Mollom servers could be contacted, but Mollom API keys could not be verified.'); $message_valid = t('The services are operating correctly.'); $message_missing = t('The Mollom API keys are not configured yet.'); $message_server = t('The Mollom servers could not be contacted. Please make sure that your web server can make outgoing HTTP requests.'); $message_saved = t('The configuration options have been saved.'); $admin_message = t('Visit the Mollom settings page to configure your keys.', array( '@settings-url' => url('admin/config/content/mollom/settings'), )); $this->drupalLogin($this->admin_user); // Ensure there is no requirements error by default. $this->drupalGet('admin/reports/status'); $this->clickLink('run cron manually'); // Install the Mollom module. $this->drupalPost('admin/modules', array('modules[Other][mollom][enable]' => TRUE), t('Save configuration')); $this->assertRaw($message_missing . ' ' . $admin_message, t('Post installation warning found.')); // Verify that forms can be submitted without valid Mollom module configuration. $this->drupalLogin($this->web_user); $edit = array( 'title' => 'spam', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertText('Successful form submission.'); // Assign the 'administer mollom' permission and log in a user. $this->drupalLogin($this->admin_user); $edit = array( DRUPAL_AUTHENTICATED_RID . '[administer mollom]' => TRUE, ); $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); // Verify presence of 'empty keys' error message. $this->drupalGet('admin/config/content/mollom'); $this->assertText($message_missing); $this->assertNoText($message_invalid); // Verify requirements error about missing API keys. $this->drupalGet('admin/reports/status'); $this->assertRaw($message_missing . ' ' . $admin_message, t('Requirements error found.')); // Configure invalid keys. $edit = array( 'mollom_public_key' => 'the-invalid-mollom-api-key-value', 'mollom_private_key' => 'the-invalid-mollom-api-key-value', ); $this->drupalGet('admin/config/content/mollom/settings'); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($message_saved); $this->assertNoText($this->fallback_message, t('Fallback message not found.')); // Verify presence of 'incorrect keys' error message. $this->assertText($message_short_invalid); $this->assertNoText($message_missing); //$this->assertNoText($message_server); // Verify requirements error about invalid API keys. $this->drupalGet('admin/reports/status', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($message_short_invalid); // Ensure unreachable servers. variable_set('mollom_class', 'MollomDrupalTestInvalid'); // Verify presence of 'network error' message. $this->drupalGet('admin/config/content/mollom/settings', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($message_server); $this->assertNoText($message_missing); $this->assertNoText($message_invalid); // Verify requirements error about network error. $this->drupalGet('admin/reports/status', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($message_server); $this->assertNoText($this->fallback_message, t('Fallback message not found.')); // Create a testing site on backend to have some API keys. variable_set('mollom_class', $this->mollomClass); $mollom = mollom(); $mollom->createKeys(); $response = $mollom->getSite(); $this->assertSame('publicKey', $response['publicKey'], $mollom->publicKey); // Verify that valid keys work. $this->drupalGet('admin/config/content/mollom/settings', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertFieldByName('mollom_public_key', 'the-invalid-mollom-api-key-value'); /* This portion of the test is not working currently. See issue 2391671 for full details. Once identified and resolved this needs to be put back. $edit = array( 'mollom_public_key' => $mollom->publicKey, 'mollom_private_key' => $mollom->privateKey, ); $this->drupalPost('admin/config/content/mollom/settings', $edit, t('Save configuration')); $this->assertText($message_saved); $this->assertText($message_valid); $this->assertNoText($message_missing); $this->assertNoText($message_invalid); // Verify that deleting keys throws the correct error message again. $this->drupalGet('admin/config/content/mollom/settings'); $this->assertText($message_valid); $edit = array( 'mollom_public_key' => '', 'mollom_private_key' => '', ); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($message_saved); $this->assertNoText($message_valid); $this->assertText($message_missing); $this->assertNoText($message_invalid); */ } } /** * Tests low-level REST communication with Mollom servers. */ class MollomResponseTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Server responses', 'description' => 'Tests that Mollom server responses match expectations.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); $this->setKeys(); $this->assertValidKeys(); $this->admin_user = $this->drupalCreateUser(); } /** * Tests Site API. */ function testSiteAPI() { $mollom = mollom(); $info = $mollom->getClientInformation(); // Create a new site. $site = array( 'url' => 'example.com', 'email' => 'mollom@example.com', ); $result = $mollom->createSite($site); $this->assertMollomWatchdogMessages(); $this->assertTrue(!empty($result['publicKey']), 'publicKey found.'); $this->assertTrue(!empty($result['privateKey']), 'privateKey found.'); $this->assertSame('url', $result['url'], $site['url']); $this->assertSame('email', $result['email'], $site['email']); $this->assertTrue(!isset($result['platformName']), 'platformName not found.'); $this->assertTrue(!isset($result['platformVersion']), 'platformVersion not found.'); $this->assertTrue(!isset($result['clientName']), 'clientName not found.'); $this->assertTrue(!isset($result['clientVersion']), 'clientVersion not found.'); $site = $result; $mollom->publicKey = $site['publicKey']; $mollom->privateKey = $site['privateKey']; // Verify that getSite() response equals the createSite() response. $result = $mollom->getSite(); $this->assertMollomWatchdogMessages(); $this->assertSame('publicKey', $result['publicKey'], $site['publicKey']); $this->assertSame('privateKey', $result['privateKey'], $site['privateKey']); $this->assertSame('url', $result['url'], $site['url']); $this->assertSame('email', $result['email'], $site['email']); $this->assertTrue(!isset($result['platformName']), 'platformName not found.'); $this->assertTrue(!isset($result['platformVersion']), 'platformVersion not found.'); $this->assertTrue(!isset($result['clientName']), 'clientName not found.'); $this->assertTrue(!isset($result['clientVersion']), 'clientVersion not found.'); // Test that verifying keys updates client information. $result = $mollom->verifyKeys(); $this->assertMollomWatchdogMessages(); $this->assertIdentical($result, TRUE, 'Site was updated.'); $result = $mollom->getSite(); $this->assertMollomWatchdogMessages(); $this->assertSame('publicKey', $result['publicKey'], $site['publicKey']); $this->assertSame('privateKey', $result['privateKey'], $site['privateKey']); $this->assertSame('url', $result['url'], $site['url']); $this->assertSame('email', $result['email'], $site['email']); $this->assertSame('platformName', $result['platformName'], $info['platformName']); $this->assertSame('platformVersion', $result['platformVersion'], $info['platformVersion']); $this->assertSame('clientName', $result['clientName'], $info['clientName']); $this->assertSame('clientVersion', $result['clientVersion'], $info['clientVersion']); // Verify that the site is listed. // FIXME: Site listing not supported by backend yet. /* $result = $mollom->getSites(); $this->assertMollomWatchdogMessages(); $found = FALSE; foreach ($result as $record) { if ($record['publicKey'] == $site['publicKey']) { $found = TRUE; } } $this->assertTrue($found, 'Site record was found in site list.'); */ // Verify that the site can be deleted. $result = $mollom->deleteSite($site['publicKey']); $this->assertMollomWatchdogMessages(); $this->assertIdentical($result, TRUE, 'Site was deleted.'); // Verify that the site no longer appears in site list. $mollom->publicKey = $this->publicKey; $mollom->privateKey = $this->privateKey; // FIXME: Site listing not supported by backend yet. /* $result = $mollom->getSites(); $this->assertMollomWatchdogMessages(); $found = FALSE; foreach ($result as $record) { if ($record['publicKey'] == $site['publicKey']) { $found = TRUE; } } $this->assertFalse($found, 'Deleted site no longer exists.'); */ // Verify that retrieving the deleted site yields a 404. $result = $mollom->getSite($site['publicKey']); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); $this->assertEqual($result, 404, 'Attempt to get deleted site throws 404.'); // Verify that authentication fails. $mollom->publicKey = $site['publicKey']; $mollom->privateKey = $site['privateKey']; $result = $mollom->getSite(); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); $this->assertEqual($mollom->lastResponseCode, Mollom::AUTH_ERROR, 'Attempt to authenticate with deleted site keys fails.'); // Restore keys for tearDown(). $mollom->publicKey = $this->publicKey; $mollom->privateKey = $this->privateKey; } /** * Tests mollom.checkContent(). */ function testCheckContent() { $mollom = mollom(); $data = array( 'authorName' => $this->admin_user->name, 'authorMail' => $this->admin_user->mail, 'authorId' => $this->admin_user->uid, 'authorIp' => ip_address(), ); // Ensure proper response for 'ham' submissions. // By default (i.e., omitting 'checks') we expect spam and quality checking // only. $data['postBody'] = 'ham'; $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.0); $this->assertSame('spamClassification', $result['spamClassification'], 'ham'); $this->assertTrue(!isset($result['qualityScore']), 'qualityScore not returned.'); $this->assertTrue(!isset($result['profanityScore']), 'profanityScore not returned.'); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Ensure proper response for 'spam' submissions, re-using session_id. $data['postBody'] = 'spam'; $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertSame('spamClassification', $result['spamClassification'], 'spam'); $this->assertTrue(!isset($result['qualityScore']), 'qualityScore not returned.'); $this->assertTrue(!isset($result['profanityScore']), 'profanityScore not returned.'); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Ensure proper response for 'unsure' submissions, re-using session_id. $data['postBody'] = 'unsure'; $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.5); $this->assertSame('spamClassification', $result['spamClassification'], 'unsure'); $this->assertTrue(!isset($result['qualityScore']), 'qualityScore not returned.'); $this->assertTrue(!isset($result['profanityScore']), 'profanityScore not returned.'); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Additionally enable profanity checking. $data['postBody'] = 'spam profanity'; $data['checks'] = array('spam', 'quality', 'profanity'); $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertSame('spamClassification', $result['spamClassification'], 'spam'); $this->assertSame('qualityScore', $result['qualityScore'], 0.0); $this->assertSame('profanityScore', $result['profanityScore'], 1.0); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Change the string to contain profanity only. $data['postBody'] = 'profanity'; $data['checks'] = array('spam', 'quality', 'profanity'); $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.5); $this->assertSame('spamClassification', $result['spamClassification'], 'unsure'); $this->assertSame('qualityScore', $result['qualityScore'], 0.0); $this->assertSame('profanityScore', $result['profanityScore'], 1.0); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Disable spam checking, only do profanity checking. $data['postBody'] = 'spam profanity'; $data['checks'] = array('profanity'); $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(!isset($result['spamScore']), 'spam not returned.'); $this->assertTrue(!isset($result['spamClassification']), 'spamClassification not returned.'); $this->assertTrue(!isset($result['qualityScore']), 'qualityScore not returned.'); $this->assertSame('profanityScore', $result['profanityScore'], 1.0); $data['id'] = $this->assertResponseID('contentId', $result['id']); // Pass arbitrary string to profanity checking. $data['postBody'] = $this->randomString(12); $result = $mollom->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(!isset($result['spamScore']), 'spam not returned.'); $this->assertTrue(!isset($result['spamClassification']), 'spamClassification not returned.'); $this->assertTrue(!isset($result['qualityScore']), 'qualityScore not returned.'); $this->assertSame('profanityScore', $result['profanityScore'], 0.0); $data['id'] = $this->assertResponseID('contentId', $result['id']); } /** * Tests results of mollom.checkContent() across requests for a single session. */ function testCheckContentSession() { $mollom = mollom(); $base_data = array( 'authorName' => $this->admin_user->name, 'authorMail' => $this->admin_user->mail, 'authorId' => $this->admin_user->uid, 'authorIp' => ip_address(), ); // Sequence: // - Post unsure content // - Solve CAPTCHA // - Post spam content // - Expect spamClassification 'spam' (spam always trumps) $this->resetResponseID(); $content_data = $base_data; $content_data['postBody'] = 'unsure'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.5); $this->assertSame('spamClassification', $result['spamClassification'], 'unsure'); $contentId = $this->assertResponseID('contentId', $result['id']); $content_data['id'] = &$contentId; $captcha_data = array( 'type' => 'image', 'contentId' => &$contentId, 'authorIp' => $base_data['authorIp'], ); $result = $mollom->createCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); $captchaId = $this->assertResponseID('captchaId', $result['id']); $content_data['captchaId'] = &$captchaId; $captcha_data = array( 'id' => &$captchaId, 'contentId' => &$contentId, 'authorIp' => $base_data['authorIp'], 'authorId' => $base_data['authorId'], 'solution' => 'correct', ); $result = $mollom->checkCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); $this->assertSame('solved', $result['solved'], 1); $content_data['postBody'] = 'spam'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertSame('spamClassification', $result['spamClassification'], 'spam'); $contentId = $this->assertResponseID('contentId', $result['id']); // @todo Enable following sequence after fixing Testing API. return; // Sequence: // - Post unsure content // - Solve CAPTCHA // - Post unsure content // - Expect spamClassification 'ham' // - Post ham content // - Expect spamClassification 'ham' // - Post unsure content // - Expect spamClassification 'ham' $this->resetResponseID(); $content_data = $base_data; $content_data['postBody'] = 'unsure'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.5); $this->assertSame('spamClassification', $result['spamClassification'], 'unsure'); $contentId = $this->assertResponseID('contentId', $result['id']); $content_data['id'] = &$contentId; $captcha_data = array( 'type' => 'image', 'contentId' => &$contentId, 'authorIp' => $base_data['authorIp'], ); $result = $mollom->createCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); $captchaId = $this->assertResponseID('captchaId', $result['id']); $content_data['captchaId'] = &$captchaId; $captcha_data = array( 'id' => &$captchaId, 'contentId' => &$contentId, 'authorIp' => $base_data['authorIp'], 'authorId' => $base_data['authorId'], 'solution' => 'correct', ); $result = $mollom->checkCaptcha($captcha_data); $this->assertMollomWatchdogMessages(); $this->assertSame('solved', $result['solved'], 1); $content_data['postBody'] = 'unsure'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); //$this->assertSame('spamScore', $result['spamScore'], 0.0); $this->assertSame('spamClassification', $result['spamClassification'], 'ham'); $contentId = $this->assertResponseID('contentId', $result['id']); $content_data['postBody'] = 'ham'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); //$this->assertSame('spamScore', $result['spamScore'], 0.0); $this->assertSame('spamClassification', $result['spamClassification'], 'ham'); $contentId = $this->assertResponseID('contentId', $result['id']); $content_data['postBody'] = 'unsure'; $result = $mollom->checkContent($content_data); $this->assertMollomWatchdogMessages(); //$this->assertSame('spamScore', $result['spamScore'], 0.0); $this->assertSame('spamClassification', $result['spamClassification'], 'ham'); $contentId = $this->assertResponseID('contentId', $result['id']); } /** * Tests the language detection functionality at the API level. */ function testCheckContentLanguage() { // Note that Mollom supports more languages than those tested. // Development server checks for the "lang-{language_code}" in content. $tests = array( 'lang-en With 2.7 million residents, it is the most populous city in both the U.S. state of Illinois and the American Midwest.' => array( 'en', ), 'lang-en lang-de With 2.7 million residents, it is the most populous city in both the U.S. state of Illinois and the American Midwest. Chicago ist seit der Mitte des 19. Jahrhunderts eine wichtige Handelsstadt in den Vereinigten Staaten.' => array( 'en', 'de', ), '!!!!!!!!!!!!!!!!!!!!!!!!!!!' => array( 'zxx', ), ); $mollom = mollom(); foreach ($tests as $string => $expected) { $result = $mollom->checkContent(array( 'checks' => 'language', 'postBody' => $string, )); // Parse result values. foreach ($result['languages'] as $item => $language) { $this->assertTrue(in_array($language['languageCode'], $expected), 'Found returned language code ' . $language['languageCode'] . ' in expected languages.'); } } } /** * Tests mollom.getImageCaptcha(). */ function testGetImageCaptcha() { $mollom = mollom(); // Ensure we get no SSL URL by default. $data = array( 'type' => 'image', 'authorIp' => ip_address(), ); $result = $mollom->createCaptcha($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(strpos($result['url'], 'http://') === 0, t('CAPTCHA URL uses HTTP protocol.')); // Ensure we get a SSL URL when passing the 'ssl' parameter. $data['ssl'] = TRUE; $result = $mollom->createCaptcha($data); $this->assertMollomWatchdogMessages(); $this->assertTrue(strpos($result['url'], 'https://') === 0, t('CAPTCHA URL uses HTTPS protocol.')); } /** * Tests mollom.checkCaptcha(). */ function testCheckCaptcha() { $mollom = mollom(); // Ensure we can send an 'author_id'. // Verifying no severe watchdog messages is sufficient, as unsupported // parameters would trigger a XML-RPC error. $uid = rand(); $data = array( 'type' => 'image', 'authorIp' => ip_address(), 'authorId' => $uid, ); $result = $mollom->createCaptcha($data); $this->assertMollomWatchdogMessages(); $data['id'] = $this->assertResponseID('captchaId', $result['id']); $data += array( 'solution' => 'correct', ); $result = $mollom->checkCaptcha($data); $this->assertMollomWatchdogMessages(); } } /** * Tests low-level communication with local fake Mollom server. */ class MollomResponseLocalTestCase extends MollomResponseTestCase { // Re-route Mollom communication to this testing site. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Server responses (local)', 'description' => 'Tests that local fake Mollom server responses match expectations.', 'group' => 'Mollom', ); } } /** * Tests low-level communication with configured server. */ class MollomResponseConfiguredTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Server responses (configured)', 'description' => 'Tests that a configured endpoint behaves the same as specified endpoints.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom_test_server', 'mollom_test', 'mollom')); } function testConfiguredUrl() { // Set the configured URL to be the same as the test server endpoing. $test_url = $this->getAbsoluteURL('mollom-test/rest'); $stripped = preg_replace('/^[a-z]+:\\/\\//i', '', $test_url); variable_set('mollom_api_endpoint', $stripped); // Clear out Mollom local variable so that it can be instantiated with the // correct endpoint. $this->getClient(TRUE); // The DrupalTest class should now use the local test server. $this->setKeys(); $this->assertValidKeys(); // Check the watchdog from the key assertion is using the correct server. // If the keys passed assertion, then they were created on the right server // too. foreach ($this->messages as $row) { $output = theme_dblog_message(array('event' => $row, 'link' => FALSE)); $this->assertTrue(strpos($output, 'Request: POST ' . $test_url . '/v1/site/') !== FALSE, 'Keys verified from configured endpoint.'); } } } class MollomAccessTestCase extends MollomWebTestCase { protected $mollomClass = 'MollomDrupalTestLocal'; protected $createKeys = FALSE; public static function getInfo() { return array( 'name' => 'Access checking', 'description' => 'Confirm that there is a working key pair and that this status is correctly indicated on the module settings page for appropriate users.', 'group' => 'Mollom', ); } /** * Configure an invalid key pair and ensure error message. */ function testKeyPairs() { // No error message or watchdog messages should be thrown with default // testing keys. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom/settings'); // Try to setup completely invalid keys. $edit = array( 'mollom_public_key' => 'foo', 'mollom_private_key' => 'bar', ); $this->drupalPost(NULL, $edit, t('Save configuration')); $this->assertText(t('!title must be 32 characters. Ensure you copied the key correctly.', array( '!title' => t('Public key'), ))); $this->assertText(t('!title must be 32 characters. Ensure you copied the key correctly.', array( '!title' => t('Private key'), ))); $this->assertNoText(t('The configuration options have been saved.')); $this->assertNoText('The configured Mollom API keys are invalid.'); // Set up invalid test keys and check that an error message is shown. $edit = array( 'mollom_public_key' => 'the-invalid-mollom-api-key-value', 'mollom_private_key' => 'the-invalid-mollom-api-key-value', ); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText(t('The configuration options have been saved.')); $this->assertText('The configured Mollom API keys are invalid.'); } /** * Make sure that the Mollom settings page works for users with the * 'administer mollom' permission but not those without * it. */ function testAdminAccessRights() { // Check access for a user that only has access to the 'administer // site configuration' permission. This user should have access to // the Mollom settings page. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom'); $this->assertResponse(200); // Check access for a user that has everything except the 'administer // mollom' permission. This user should not have access to the Mollom // settings page. $this->web_user = $this->drupalCreateUser(array_diff(module_invoke_all('perm'), array('administer mollom'))); $this->drupalLogin($this->web_user); $this->drupalGet('admin/config/content/mollom', array('watchdog' => WATCHDOG_WARNING)); $this->assertResponse(403); } } class MollomBypassAccessTestCase extends MollomWebTestCase { // Requires entity-level access permissions. protected $profile = 'standard'; public static function getInfo() { return array( 'name' => 'Bypass access', 'description' => 'Tests that users having higher privileges can bypass Mollom protection.', 'group' => 'Mollom', ); } /** * Tests 'bypass access' property of registered forms. */ function testBypassAccess() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form'); $this->drupalLogout(); $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'node body'))), 'type' => 'article')); // Create a regular user and post a comment. $this->web_user = $this->drupalCreateUser(array('edit own comments')); $this->drupalLogin($this->web_user); $edit = array( 'subject' => 'ham', 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); $this->drupalPost(NULL, array(), t('Save')); $this->assertText('node body'); $this->assertText($edit['comment_body[und][0][value]']); // Ensure a user having one of the permissions to bypass access can post // spam without triggering the spam protection. $this->drupalLogin($this->admin_user); $this->drupalGet('node/' . $node->nid); $this->clickLink('edit'); $edit = array( 'subject' => 'spam', 'comment_body[und][0][value]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Preview')); $this->assertNoText($this->spam_message); $this->drupalPost(NULL, array(), t('Save')); $this->assertNoText($this->spam_message); $this->assertText('node body'); $this->assertText($edit['comment_body[und][0][value]']); // Log in back the regular user and try to edit the comment containing spam. $this->drupalLogin($this->web_user); $this->drupalGet('node/' . $node->nid); $this->clickLink('edit'); $this->drupalPost(NULL, array(), t('Preview')); $this->assertText($this->spam_message); $this->drupalPost(NULL, array(), t('Save')); $this->assertText($this->spam_message); $this->assertNoText('node body'); } } class MollomFallbackModeTestCase extends MollomWebTestCase { /** * Disable automated creation and recovery of testing API keys. * * This test attempts to save an invalid server list in order to test the * fallback mode on a protected form. With $createKeys enabled, the Mollom * client class would verify its keys upon every instantiation, and thus, * discover the bogus server list, and empty the server list. Thus, the * intentionally invalid server list would never be in effect. * * We disable the automatic creation of keys and only set them up once. * * @see setUp() */ protected $createKeys = FALSE; public static function getInfo() { return array( 'name' => 'Fallback mode', 'description' => 'Tests expected fallback behavior when Mollom servers are not available.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('node', 'comment')); // Setup valid testing API keys. $this->setKeys(); $this->assertValidKeys(); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); } /** * Tests that form submissions are blocked when Mollom servers are unreachable. */ function testBlock() { $this->setProtection('user_pass', MOLLOM_MODE_CAPTCHA); $this->setProtection('comment_node_article_form'); $node = $this->drupalCreateNode(array('type' => 'article')); // Set the fallback strategy to 'blocking mode'. variable_set('mollom_fallback', MOLLOM_FALLBACK_BLOCK); // Make all requests to Mollom fail. variable_set('mollom_class', 'MollomDrupalTestInvalid'); // Check the CAPTCHA-only protected form. $this->drupalGet('user/password', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($this->fallback_message); $this->assertNoCaptchaField(); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); // Verify that the form cannot be submitted. $edit = array( 'name' => $this->admin_user->name, ); $this->drupalPost(NULL, $edit, t('E-mail new password'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertNoText(t('Further instructions have been sent to your e-mail address.')); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); // Check the text analysis protected form. $this->web_user = $this->drupalCreateUser(array('access content', 'access comments', 'post comments', 'skip comment approval')); $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $node->nid); $this->assertNoText($this->fallback_message); $this->assertText('privacy policy'); // Verify that the form cannot be submitted. $edit = array( 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost(NULL, $edit, t('Preview'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText($this->fallback_message); $this->assertNoCaptchaField(); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); } /** * Tests that form submissions are accepted when Mollom servers are unreachable. */ function testAccept() { $this->setProtection('user_pass', MOLLOM_MODE_CAPTCHA); $this->setProtection('comment_node_article_form'); $node = $this->drupalCreateNode(array('type' => 'article')); // Set the fallback strategy to 'accept mode'. variable_set('mollom_fallback', MOLLOM_FALLBACK_ACCEPT); // Make all requests to Mollom fail. variable_set('mollom_class', 'MollomDrupalTestInvalid'); // Check the CAPTCHA-only protected form. $this->drupalGet('user/password', array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertNoText($this->fallback_message); $this->assertNoCaptchaField(); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); // Verify that the form can be submitted. $edit = array( 'name' => $this->admin_user->name, ); $this->drupalPost(NULL, $edit, t('E-mail new password'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertText(t('Further instructions have been sent to your e-mail address.')); $this->assertNoText($this->fallback_message); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); // Check the text analysis protected form. $this->web_user = $this->drupalCreateUser(array('access content', 'access comments', 'post comments', 'skip comment approval')); $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $node->nid); $this->assertNoText($this->fallback_message); $this->assertText('privacy policy'); // Verify that the form can be submitted. $edit = array( 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost(NULL, $edit, t('Preview'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertNoText($this->fallback_message); $this->assertNoCaptchaField(); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); $this->drupalPost(NULL, array(), t('Save'), array('watchdog' => WATCHDOG_EMERGENCY)); $this->assertNoText($this->fallback_message); $this->assertNoCaptchaField(); $this->assertNoText('CAPTCHA'); $this->assertNoText('word verification'); } /** * Tests that form submissions are accepted when only last request attempt to Mollom servers succeeds. */ function testFailover() { $this->setProtection('user_pass', MOLLOM_MODE_CAPTCHA); // Set the fallback strategy to 'blocking mode', so that if the failover // mechanism does not work, we would expect to get a warning. variable_set('mollom_fallback', MOLLOM_FALLBACK_BLOCK); // Make all requests to Mollom fail. variable_set('mollom_class', 'MollomDrupalTestInvalid'); // Enable pseudo server fail-over. // @see MollomDrupalTestInvalid::handleRequest() variable_set('mollom_testing_server_failover', TRUE); // Validate that the request password form has a CAPTCHA text field and // that a user is not blocked from submitting it. $this->drupalGet('user/password'); $this->assertCaptchaField(); $this->assertNoText($this->fallback_message); $this->postCorrectCaptcha('user/password', array('name' => $this->admin_user->name), t('E-mail new password')); $this->assertText(t('Further instructions have been sent to your e-mail address.')); } } /** * Tests blacklist functionality. * * The blacklists are stored on the server. These tests can fail when * different people run the tests at the same time because all tests share * the same blacklist. You can configure a custom key to avoid this. */ class MollomBlacklistTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Blacklist', 'description' => 'Tests URL and text blacklist functionality.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom')); $this->setKeys(); } /** * Test the blacklist functionality at the API level without using a web interface. */ function testBlacklistAPI() { $mollom = mollom(); // Remove any stale blacklist entries from test runs that did not finish. $blacklist = $mollom->getBlacklist(); foreach ($blacklist as $entry) { if (REQUEST_TIME - strtotime($entry['created']) > 86400) { $mollom->deleteBlacklistEntry($entry['id']); } } $this->assertMollomWatchdogMessages(); // Blacklist a URL. $domain = drupal_strtolower($this->randomName()) . '.com'; $entry = $mollom->saveBlacklistEntry(array( 'value' => $domain, 'context' => 'allFields', 'reason' => 'spam', 'match' => 'contains', )); $this->assertMollomWatchdogMessages(); $this->assertTrue($entry['id'], t('The URL was blacklisted.')); // Check whether posts containing the blacklisted URL are properly blocked. $result = mollom()->checkContent(array( 'postBody' => "When the exact URL is present, the post should get blocked: http://{$domain}", )); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Exact URL match was blocked.')); $result = mollom()->checkContent(array( 'postBody' => "When the URL is expanded in the back, the post should get blocked: http://{$domain}/oh-my", )); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Partial URL match was blocked.')); $result = mollom()->checkContent(array( 'postBody' => "When the URL is expanded in the front, the post should get blocked: http://www.{$domain}", )); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('URL with www-prefix was blocked.')); $result = mollom()->checkContent(array( 'postBody' => "When the URL has a different schema, the post should get blocked: ftp://www.{$domain}", )); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('URL with different schema was blocked.')); $result = $mollom->deleteBlacklistEntry($entry['id']); $this->assertMollomWatchdogMessages(); $this->assertIdentical($result, TRUE, t('The blacklisted URL was removed.')); // Blacklist a word. // @todo As of now, only non-numeric, lower-case text seems to be supported. $term = drupal_strtolower(preg_replace('/[^a-zA-Z]/', '', $this->randomName())); $entry = $mollom->saveBlacklistEntry(array( 'value' => $term, 'context' => 'allFields', 'reason' => 'spam', 'match' => 'contains', )); $this->assertMollomWatchdogMessages(); $this->assertTrue($entry['id'], t('The text was blacklisted.')); // Check whether posts containing the blacklisted word are properly blocked. $data = array( 'postBody' => $term, ); $result = mollom()->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Identical match was blocked.')); $data = array( 'postBody' => "When the term is present, the post should get blocked: " . $term, ); $result = mollom()->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Exact match was blocked.')); $data = array( 'postBody' => "When match is 'contains', the word can be surrounded by other text: abc" . $term . "def", ); $result = mollom()->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Partial match was blocked.')); // Update the blacklist entry to match the term only exactly. $entry = $mollom->saveBlacklistEntry(array( 'id' => $entry['id'], 'value' => $term, 'context' => 'allFields', 'reason' => 'spam', 'match' => 'exact', )); $this->assertMollomWatchdogMessages(); $this->assertTrue($entry['id'], t('The blacklist entry was updated.')); $data = array( 'postBody' => "When match is 'exact', it has to be exact: " . $term, ); $result = mollom()->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 1.0); $this->assertEqual($result['spamClassification'], 'spam', t('Exact match was blocked.')); $data = array( 'postBody' => "When match is 'exact', it has to be exact: abc{$term}def", ); $result = mollom()->checkContent($data); $this->assertMollomWatchdogMessages(); $this->assertSame('spamScore', $result['spamScore'], 0.5); $this->assertEqual($result['spamClassification'], 'unsure', t('Partial match was not blocked.')); $result = $mollom->deleteBlacklistEntry($entry['id']); $this->assertMollomWatchdogMessages(); $this->assertIdentical($result, TRUE, t('The blacklisted text was removed.')); // Try to remove a non-existing entry. // @todo Ensure that the ID does not exist. $result = $mollom->deleteBlacklistEntry(999); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); $this->assertNotIdentical($result, TRUE, t('Error response for a non-existing blacklist text found.')); $this->assertSame('Response code', $mollom->lastResponseCode, 404); } /** * Test the blacklist administration interface. * * We don't need to check whether the blacklisting actually works * (i.e. blocks posts) because that is tested in testTextBlacklistAPI() and * testURLBlacklistAPI(). */ function testBlacklistUI() { // Log in as an administrator and access the blacklist administration page. $this->admin_user = $this->drupalCreateUser(array( 'administer mollom', 'access administration pages', )); $this->drupalLogin($this->admin_user); // Add a word to the spam blacklist. $this->drupalGet('admin/config/content/mollom/blacklist'); $text = $this->randomName(); $edit = array( 'entry[value]' => $text, 'entry[context]' => 'allFields', 'entry[match]' => 'contains', ); $this->drupalPost(NULL, $edit, t('Add')); $text = drupal_strtolower($text); $this->assertText(t('The entry was added to the blacklist.')); $this->assertText($text); // Remove the word from the spam blacklist. $links = $this->xpath('//td[contains(., :text)]/following-sibling::td/a', array(':text' => $text)); $delete_url = (string) $links[0]['href']; $this->assertLinkByHref($delete_url); $delete_url = $GLOBALS['base_root'] . $delete_url; $this->drupalGet($delete_url); $this->drupalPost(NULL, array(), t('Delete')); $this->assertEqual($this->getUrl(), url('admin/config/content/mollom/blacklist', array('absolute' => TRUE)), t('Correct page redirection.')); $this->assertNoText($text, 'Text blacklist removed.'); // Add a word to the profanity blacklist. $this->drupalGet('admin/config/content/mollom/blacklist/profanity'); $text = $this->randomName(); $edit = array( 'entry[value]' => $text, 'entry[context]' => 'allFields', 'entry[match]' => 'contains', ); $this->drupalPost(NULL, $edit, t('Add')); $this->assertText(t('The entry was added to the blacklist.')); $text = drupal_strtolower($text); $this->assertText($text); // Remove the word from the profanity blacklist. $links = $this->xpath('//td[contains(., :text)]/following-sibling::td/a', array(':text' => $text)); $delete_url = (string) $links[0]['href']; $this->assertLinkByHref($delete_url); $delete_url = $GLOBALS['base_root'] . $delete_url; $this->drupalGet($delete_url); $this->drupalPost(NULL, array(), t('Delete')); $this->assertEqual($this->getUrl(), url('admin/config/content/mollom/blacklist/profanity', array('absolute' => TRUE)), t('Correct page redirection.')); $this->assertNoText($text, 'Text blacklist removed.'); } } /** * Tests Mollom form profanity filtering functionality. */ class MollomProfanityTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Profanity checking', 'description' => 'Tests form protection with text analysis checking for profanity.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom_test')); } /** * Tests text analysis profanity checking. */ function testProfanity() { // Protect Mollom test form but do not enable profanity checking. $this->drupalLogin($this->admin_user); $edit_config = array( 'mollom[checks][spam]' => TRUE, 'mollom[checks][profanity]' => FALSE, ); $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, $edit_config); $this->drupalLogout(); // Assert that the profanity filter is disabled. $edit = array( 'title' => 'Joomla lover unite!', 'body' => 'This is a post just for unsure Joomla lovers. If you love Joomla, this is the profanity for you!', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); $this->assertNoText($this->profanity_message); // Enable profanity checking, disable spam checking. $this->drupalLogin($this->admin_user); $edit_config = array( 'mollom[checks][spam]' => FALSE, 'mollom[checks][profanity]' => TRUE, ); $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, $edit_config); $this->drupalLogout(); // Verify that the profanity filter now blocks this content. $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertNoText('Successful form submission.'); $this->assertText($this->profanity_message); // Verify that we are able to post after removing profanity, as the error // message suggests. $edit['body'] = 'This is a post just for unsure Joomla lovers.'; $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertText('Successful form submission.'); $this->assertNoText($this->profanity_message); } /** * Tests text analysis with both profanity and spam checking. */ function testProfanitySpam() { // Enable spam and profanity checking for the article node comment form. $this->drupalLogin($this->admin_user); $edit_config = array( 'mollom[checks][profanity]' => TRUE, 'mollom[checks][spam]' => TRUE, ); $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, $edit_config); $this->drupalLogout(); // Sequence: Post profanity (ham), remove profanity (still ham), and expect // that to be accepted. $edit = array( 'title' => $this->randomName(), ); $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $this->assertPrivacyLink(); $edit['body'] = 'profanity ham'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertText($this->profanity_message); $this->assertNoText('Successful form submission.'); $contentId = $this->assertResponseIDInForm('contentId'); $edit['body'] = 'not profane ham'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoText($this->profanity_message); $this->assertText('Successful form submission.'); $mid = $this->getFieldValueByName('mid'); $this->assertMollomData('mollom_test', $mid, 'contentId', $contentId); // Sequence: Post unsure spam (not profanity), post profanity along with // correct CAPTCHA, and expect that to be discarded. $this->resetResponseID(); $this->web_user = $this->drupalCreateUser(array()); $this->drupalLogin($this->web_user); $edit = array( 'title' => $this->randomName(), ); $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $this->assertPrivacyLink(); $edit['body'] = 'unsure'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertCaptchaField(); $this->assertNoText($this->profanity_message); $this->assertNoText('Successful form submission.'); $contentId = $this->assertResponseIDInForm('contentId'); $captchaId = $this->assertResponseIDInForm('captchaId'); $edit['body'] = 'unsure profanity'; $this->postCorrectCaptcha(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertText($this->profanity_message); $this->assertNoText('Successful form submission.'); } } /** * Tests Mollom form configuration functionality. */ class MollomFormConfigurationTestCase extends MollomWebTestCase { // Re-route Mollom communication to this testing site. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Form administration', 'description' => 'Verify that forms can be properly protected and unprotected.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom_test')); $this->drupalLogin($this->admin_user); } /** * Tests configuration of form fields for textual analysis. */ function testFormFieldsConfiguration() { $form_info = mollom_form_info('mollom_test_form', 'mollom_test'); // Protect Mollom test form. $this->drupalGet('admin/config/content/mollom/add'); $edit = array( 'mollom[form_id]' => 'mollom_test_form', ); $this->drupalPost(NULL, $edit, t('Next')); $this->assertText('Mollom test form'); $edit = array( 'mollom[mode]' => MOLLOM_MODE_ANALYSIS, 'mollom[checks][spam]' => TRUE, 'mollom[enabled_fields][title]' => TRUE, 'mollom[enabled_fields][body]' => TRUE, 'mollom[enabled_fields][exclude]' => FALSE, 'mollom[enabled_fields][' . rawurlencode('parent][child') . ']' => TRUE, 'mollom[enabled_fields][field]' => TRUE, ); $this->drupalPost(NULL, $edit, t('Save')); // Verify that mollom_test_form form was protected. $this->assertText(t('The form protection has been added.')); $this->assertText('Mollom test form'); $mollom_form = mollom_form_load('mollom_test_form'); $this->assertTrue($mollom_form, t('Form configuration exists.')); // Verify that field configuration was properly stored. $this->drupalGet('admin/config/content/mollom/manage/mollom_test_form'); foreach ($edit as $name => $value) { // Skip any inputs that are not the fields for analysis checkboxes. if (strpos($name, '[enabled_fields]') === FALSE) { continue; } // assertFieldByName() does not work for checkboxes. // @see assertFieldChecked() $elements = $this->xpath('//input[@name=:name]', array(':name' => $name)); if (isset($elements[0])) { if ($value) { $this->assertTrue(!empty($elements[0]['checked']), t('Field @name is checked', array('@name' => $name))); } else { $this->assertTrue(empty($elements[0]['checked']), t('Field @name is not checked', array('@name' => $name))); } } else { $this->fail(t('Field @name not found.', array('@name' => $name))); } } // Remove the title field from those that were enabled. $mollom_form['enabled_fields'] = array('body', 'exclude', 'parent][child', 'field'); mollom_form_save($mollom_form); // Try a submit of the form. $this->drupalLogout(); $edit = array( 'title' => 'unsure', 'body' => 'unsure', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertText($this->unsure_message); $data = $this->getServerRecord(); $this->assertTrue(empty($data['postTitle']), 'Post title was not passed to Mollom.'); // Add the title back. $this->drupalLogin($this->admin_user); $mollom_form['enabled_fields'][] = 'title'; // Add a field to the stored configuration that existed previously. $mollom_form['enabled_fields'][] = 'orphan_field'; mollom_form_save($mollom_form); // Verify that field configuration contains only available elements. $this->drupalGet('admin/config/content/mollom/manage/mollom_test_form'); $fields = $this->xpath('//input[starts-with(@name, "mollom[enabled_fields]")]'); $elements = array(); foreach ($fields as $field) { $elements[] = substr(substr(rawurldecode($field['name']), 0, -1), 23); } $this->assertEqual($elements, array_keys($form_info['elements']), t('Field list only contains available form elements.')); // Try a simple submit of the form. $this->drupalLogout(); $edit = array( 'title' => 'unsure', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertNoText('Successful form submission.'); $this->assertText($this->unsure_message); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); // Try to submit values for top-level fields. $edit = array( 'title' => 'spam', 'body' => 'spam', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertNoText('Successful form submission.'); $this->assertNoText($this->unsure_message); $this->assertText($this->spam_message); // Try to submit values for nested field. $edit = array( 'title' => $this->randomString(), 'parent[child]' => 'spam', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertNoText('Successful form submission.'); $this->assertNoText($this->unsure_message); $this->assertText($this->spam_message); // Try to submit values for nested field and multiple value field. // Start with ham values for simple, nested, and first multiple field. $edit = array( 'title' => 'ham', 'parent[child]' => 'ham', 'field[new]' => 'ham', ); $this->drupalPost('mollom-test/form', $edit, 'Add'); // Verify that the form was rebuilt. $this->assertNoText('Successful form submission.'); $this->assertNoText($this->unsure_message); $this->assertNoText($this->spam_message); // Add another value for multiple field. $edit = array( 'field[new]' => 'ham', ); $this->drupalPost(NULL, $edit, 'Add'); // Verify that the form was rebuilt. $this->assertNoText('Successful form submission.'); $this->assertNoText($this->unsure_message); $this->assertNoText($this->spam_message); // Now replace all ham values with random values, add a spam value to the // multiple field and submit the form. $edit = array( 'title' => $this->randomString(), 'parent[child]' => $this->randomString(), 'field[0]' => $this->randomString(), 'field[1]' => $this->randomString(), 'field[new]' => 'spam', ); $this->drupalPost(NULL, $edit, 'Submit'); // Verify that the form was not submitted and cannot be submitted. $this->assertNoText('Successful form submission.'); $this->assertText($this->spam_message); // Verify that we can remove the form protection. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom'); $this->assertText('Mollom test form'); $this->drupalPost('admin/config/content/mollom/unprotect/mollom_test_form', array(), t('Confirm')); $this->assertUrl('admin/config/content/mollom'); $this->assertNoText('Mollom test form'); $this->assertText(t('The form protection has been removed.')); $mollom_form = mollom_form_load('mollom_test_form'); $this->assertFalse($mollom_form, t('Form protection not found.')); // Verify that the form is no longer protected. $this->drupalLogout(); $edit = array( 'title' => 'unsure', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertText('Successful form submission.'); $this->assertNoText($this->unsure_message); $this->assertNoCaptchaField(); } /** * Tests default configuration, protecting, and unprotecting forms. */ function testFormAdministration() { $form_info = mollom_form_list(); foreach ($form_info as $form_id => $info) { $form_info[$form_id] += mollom_form_info($form_id, $info['module']); } // Verify that user registration form is not protected. $this->drupalGet('admin/config/content/mollom'); $this->assertNoText($form_info['user_register_form']['title']); $this->assertFalse(mollom_form_load('user_register_form'), t('Form configuration does not exist.')); // Re-protect user registration form. $this->drupalGet('admin/config/content/mollom/add'); $this->assertNoText(t('All available forms are protected already.')); $edit = array( 'mollom[form_id]' => 'user_register_form', ); $this->drupalPost(NULL, $edit, t('Next')); $this->assertText($form_info['user_register_form']['title']); $this->assertNoText(t('Text fields to analyze')); $this->drupalPost(NULL, array(), t('Save')); // Verify that user registration form was protected. $this->assertText(t('The form protection has been added.')); $this->assertText($form_info['user_register_form']['title']); $this->assertTrue(mollom_form_load('user_register_form'), t('Form configuration exists.')); // Retrieve a list of all permissions to verify them below. $all_permissions = array(); foreach (module_implements('permission') as $module) { if ($module_permissions = module_invoke($module, 'permission')) { foreach ($module_permissions as &$info) { $info += array('module' => $module); } $all_permissions += $module_permissions; } } // Iterate over all unconfigured forms and protect them. foreach ($form_info as $form_id => $info) { if (!mollom_form_load($form_id)) { $edit = array( 'mollom[form_id]' => $form_id, ); $this->drupalPost('admin/config/content/mollom/add', $edit, t('Next')); $this->assertText($info['title']); // Verify that forms specifying elements have all possible elements // preselected for textual analysis. $edit = array(); if (!empty($info['elements'])) { $edit['mollom[checks][spam]'] = TRUE; foreach ($info['elements'] as $field => $label) { $field = rawurlencode($field); $this->assertFieldByName("mollom[enabled_fields][$field]", TRUE); } } // Verify that CAPTCHA-only forms contain no configurable fields. else { $this->assertNoText(t('Analyze text for')); $this->assertNoText(t('Text fields to analyze')); } // Verify that bypass permissions are output. $this->assertRaw($all_permissions['bypass mollom protection']['title']); foreach ($info['bypass access'] as $permission) { $this->assertRaw($all_permissions[$permission]['title']); } $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('The form protection has been added.')); } } // Verify that trying to add a form redirects to the overview. $this->drupalGet('admin/config/content/mollom/add'); $this->assertText(t('All available forms are protected already.')); $this->assertText(t('Operations')); } /** * Tests invalid (stale) form configurations. */ function testInvalidForms() { $forms = array( 'nonexisting' => 'nonexisting_form', 'user' => 'user_nonexisting_form', 'node' => 'nonexisting_node_form', 'comment' => 'comment_node_nonexisting_form', ); $mode = 0; foreach ($forms as $module => $form_id) { $mollom_form = mollom_form_info($form_id, $module, array()); $mollom_form['mode'] = $mode++; mollom_form_save($mollom_form); } // Just visiting the form administration page is sufficient; it will throw // fatal errors, warnings, and notices. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom'); // Ensure that unprotecting the forms does not throw any notices either. foreach ($forms as $form_id) { $this->assertNoLinkByHref('admin/config/content/mollom/manage/' . $form_id); $this->assertLinkByHref('admin/config/content/mollom/unprotect/' . $form_id); $this->drupalPost('admin/config/content/mollom/unprotect/' . $form_id, array(), t('Confirm')); $this->assertNoLinkByHref('admin/config/content/mollom/unprotect/' . $form_id); } // Confirm deletion. $count = db_query('SELECT 1 FROM {mollom_form}')->fetchField(); $this->assertFalse($count, 'No forms found.'); } /** * Tests programmatically, conditionally disabling Mollom. */ function testFormAlter() { // Enable CAPTCHA-only protection for request user password form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('user_pass', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Verify regular form protection. $this->drupalGet('user/password'); $this->assertCaptchaField(); // Conditionally disable protection and verify again. variable_set('mollom_test.disable_mollom', TRUE); $this->drupalGet('user/password'); $this->assertNoCaptchaField(); } } /** * Tests protection of User module forms. */ class MollomUserFormsTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'User integration', 'description' => 'Tests protection of User module forms.', 'group' => 'Mollom', ); } /** * Make sure that the request password form is protected correctly. */ function testUserPasswordCaptcha() { $this->drupalLogin($this->admin_user); // Verify that the protection mode defaults to CAPTCHA. $this->drupalGet('admin/config/content/mollom/add/user_pass'); $this->assertFieldByName('mollom[mode]', MOLLOM_MODE_CAPTCHA); $this->setProtectionUI('user_pass', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Create a new user. $this->web_user = $this->drupalCreateUser(array()); $this->drupalGet('user/password'); // Try to reset the user's password by specifying an invalid CAPTCHA. $edit = array('name' => $this->web_user->name); $this->postIncorrectCaptcha('user/password', $edit, t('E-mail new password')); $this->postCorrectCaptcha(NULL, array(), t('E-mail new password')); $this->assertNoMailMollomReportLink(); // Try to reset the user's password by specifying a valid CAPTCHA. $this->postCorrectCaptcha('user/password', $edit, t('E-mail new password')); $this->assertText(t('Further instructions have been sent to your e-mail address.')); $this->assertNoMailMollomReportLink(); } /** * Make sure that the user registration form is protected correctly. */ function testUserRegisterCaptcha() { $this->drupalLogin($this->admin_user); // Verify that the protection mode defaults to CAPTCHA. $this->drupalGet('admin/config/content/mollom/add/user_register_form'); $this->assertFieldByName('mollom[mode]', MOLLOM_MODE_CAPTCHA); $this->setProtectionUI('user_register_form', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Retrieve initial count of registered users. $count_initial = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); // Validate that the user registration form has a CAPTCHA text field. $this->drupalGet('user/register'); $this->assertCaptchaField(); // Try to register with an invalid CAPTCHA. Make sure the user did not // successfully register. $name = $this->randomName(); $edit = array( 'name' => $name, 'mail' => $name . '@example.com', ); $this->postIncorrectCaptcha('user/register', $edit, t('Create new account')); $this->assertFalse(user_load_by_name($name), t('The user who attempted to register cannot be found in the database when the CAPTCHA is invalid.')); // Verify that user count is still the same. $count_new = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); $this->assertEqual($count_initial, $count_new, t('No new user record has been created.')); // Try to register with a valid CAPTCHA. Make sure the user was able // to successfully register. $this->postCorrectCaptcha('user/register', $edit, t('Create new account')); $this->assertText(t('Your account is currently pending approval by the site administrator.')); $account = user_load_by_name($edit['name']); $this->assertTrue($account, 'New user found after solving CAPTCHA.'); $this->assertEqual($account->status, 0, 'New user account is pending approval.'); $data = $this->assertMollomData('user', $account->uid); $this->assertSame('$data->moderate', $data->moderate, 0); // First mail sent is the site administrator approval. $link = $this->assertMailMollomReportLink('user'); // Second mail goes to the user who registered. $this->assertNoMailMollomReportLink(); // Verify that the user account is deleted after reporting it as spam. $this->drupalLogin($this->admin_user); $this->drupalGet($link['url'], $link); $edit = array( 'user_cancel_method' => 'user_cancel_delete', 'mollom[feedback]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Cancel account')); $account = user_load($account->uid, TRUE); $this->assertFalse($account, 'Reported user account not found.'); } /** * Tests text analysis protection with CAPTCHA for user registration form. */ function testUserRegisterAnalysisCaptcha() { // Allow registration by site visitors without administrator approval. variable_set('user_register', USER_REGISTER_VISITORS); $this->drupalLogin($this->admin_user); $this->setProtectionUI('user_register_form', MOLLOM_MODE_ANALYSIS); $this->drupalLogout(); // Retrieve initial count of registered users. $count_initial = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); // Verify that a spam user registration is blocked. $this->drupalGet('user/register'); $this->assertNoCaptchaField(); $edit = array( 'name' => 'spam', 'mail' => 'spam@example.com', ); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertNoCaptchaField(); $this->assertText($this->spam_message); $count_new = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); $this->assertEqual($count_initial, $count_new, 'Existing user count found.'); $this->assertFalse(user_load_by_name($edit['name']), 'New user not found.'); // Verify that a unsure registration triggers a CAPTCHA. $this->drupalGet('user/register'); $this->assertNoCaptchaField(); $edit = array( 'name' => 'unsure', 'mail' => 'unsure@example.com', ); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertCaptchaField(); // Verify that user count is still the same. $count_new = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); $this->assertEqual($count_initial, $count_new, 'Existing user count found.'); // Verify that solving the CAPTCHA registers the user. $this->postCorrectCaptcha(NULL, array(), t('Create new account')); $account = user_load_by_name($edit['name']); $this->assertTrue($account, 'New user found after solving CAPTCHA.'); $this->assertEqual($account->status, 1, 'New user account is active.'); $data = $this->assertMollomData('user', $account->uid); $this->assertSame('$data->moderate', $data->moderate, 0); } /** * Tests text analysis protection without CAPTCHA for user registration form. */ function testUserRegisterAnalysisModerate() { // Allow registration by site visitors without administrator approval. variable_set('user_register', USER_REGISTER_VISITORS); $this->drupalLogin($this->admin_user); $this->setProtectionUI('user_register_form', MOLLOM_MODE_ANALYSIS, array(), array( 'mollom[unsure]' => 'moderate', )); $this->drupalLogout(); // Retrieve initial count of registered users. $count_initial = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); // Verify that a spam user registration is blocked. $this->drupalGet('user/register'); $this->assertNoCaptchaField(); $edit = array( 'name' => 'spam', 'mail' => 'spam@example.com', ); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertNoCaptchaField(); $this->assertText($this->spam_message); $count_new = db_query("SELECT COUNT(uid) FROM {users}")->fetchField(); $this->assertEqual($count_initial, $count_new, 'Existing user count found.'); $this->assertFalse(user_load_by_name($edit['name']), 'New user not found.'); // Verify that a unsure registration triggers no CAPTCHA and requires approval. $this->drupalGet('user/register'); $this->assertNoCaptchaField(); $edit = array( 'name' => 'unsure', 'mail' => 'unsure@example.com', ); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertNoCaptchaField(); $this->assertText(t('Your account is currently pending approval by the site administrator.')); $account = user_load_by_name($edit['name']); $this->assertTrue($account, 'New user found after solving CAPTCHA.'); $this->assertEqual($account->status, 0, 'New user account is pending approval.'); $data = $this->assertMollomData('user', $account->uid); $this->assertSame('$data->moderate', $data->moderate, 1); // First mail sent is the site administrator approval. $link = $this->assertMailMollomReportLink('user'); // Second mail goes to the user who registered. $this->assertNoMailMollomReportLink(); // Verify that the user account is deleted after reporting it as spam. $this->drupalLogin($this->admin_user); $this->drupalGet($link['url'], $link); $edit = array( 'user_cancel_method' => 'user_cancel_delete', 'mollom[feedback]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Cancel account')); $account = user_load($account->uid, TRUE); $this->assertFalse($account, 'Reported user account not found.'); } } /** * Tests Profile module integration. */ class MollomProfileFormsTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Profile integration', 'description' => 'Tests Profile module integration.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('profile')); } /** * Tests Profile module integration with user registration form. */ function testProfileRegistration() { $this->drupalLogin($this->admin_user); // Allow registration by site visitors without administrator approval. variable_set('user_register', USER_REGISTER_VISITORS); // Add the three supported profile field types. $fields = array(); foreach (array('textfield', 'textarea', 'url', 'list') as $type) { $name = 'profile_' . drupal_strtolower($this->randomName()); $title = $this->randomString(); $edit = array( 'category' => 'Registration', 'title' => $title, 'name' => $name, 'register' => 1, ); $fields[$name] = $edit + array( 'type' => $type, ); $this->drupalPost('admin/config/people/profile/add/' . $type, $edit, t('Save field')); } // Enable text analysis protection for user registration form. $this->setProtectionUI('user_register_form', MOLLOM_MODE_ANALYSIS); $this->drupalLogout(); // Test each supported field separately. foreach ($fields as $key => $field) { $this->drupalGet('user/register'); $this->assertNoCaptchaField(); // @todo REST Testing API does not analyze postBody yet. $name = $this->randomName(); $name = 'unsure'; $edit = array( 'name' => $name, 'mail' => $name . '@example.com', $key => $field['type'] != 'url' ? 'unsure' : 'http://example.com/unsure', ); $this->drupalPost(NULL, $edit, t('Create new account')); $this->assertCaptchaField(); } $this->postCorrectCaptcha(NULL, array(), t('Create new account')); $this->assertText(t('A welcome message with further instructions has been sent to your e-mail address.')); $this->assertTrue(user_load_by_name($name), t('New user was found in database.')); } } /** * Tests Node module integration. */ class MollomNodeFormTestCase extends MollomWebTestCase { private $node; public static function getInfo() { return array( 'name' => 'Node integration', 'description' => 'Tests node form protection.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); // @todo 'view own unpublished content' permission required to prevent a // bogus access denied watchdog caused by a bug in Drupal core. // @see http://drupal.org/node/1429442 $this->web_user = $this->drupalCreateUser(array('create article content', 'view own unpublished content')); } /** * Tests saving of Mollom data for protected node forms. * * node_form() uses a button-level form submit handler, which invokes * form-level submit handlers before a new node entity has been stored. * Therefore, the submitted form values do not contain a 'nid' yet, so Mollom * session data cannot be stored for the new node. */ function testData() { // Enable Mollom CAPTCHA protection for Article nodes. $this->drupalLogin($this->admin_user); $this->setProtectionUI('article_node_form', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Login and submit a node. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/article'); $captchaId = $this->assertResponseIDInForm('captchaId'); $edit = array( 'title' => 'spam', 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, t('Save')); $this->node = $this->drupalGetNodeByTitle($edit['title']); $this->assertUrl('node/' . $this->node->nid); $this->assertMollomData('node', $this->node->nid, 'captchaId', $captchaId); } /** * Tests retaining of node form submissions containing profanity. */ function testRetain() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('article_node_form', MOLLOM_MODE_ANALYSIS, NULL, array( 'mollom[checks][profanity]' => TRUE, 'mollom[discard]' => 0, )); $this->drupalLogout(); // Login and submit a node. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/article'); $edit = array( 'title' => 'profanity', 'body[und][0][value]' => 'ham profanity', ); $this->drupalPost(NULL, $edit, t('Save')); $this->node = $this->drupalGetNodeByTitle($edit['title']); $this->assertEqual($this->node->status, 0, t('Node containing profanity was retained as unpublished.')); $this->assertUrl('node/' . $this->node->nid); $this->assertMollomData('node', $this->node->nid); } /** * Tests appearance of feedback options on node delete forms. */ function testFeedback() { // Create a second node type, which is not protected. $this->drupalCreateContentType(array('type' => 'unprotected', 'name' => 'Unprotected')); user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array( 'create unprotected content', 'delete own unprotected content', 'delete own article content', )); // Protect the article node type. $this->drupalLogin($this->admin_user); $this->setProtectionUI('article_node_form', MOLLOM_MODE_ANALYSIS); $this->drupalLogout(); // Login and submit a protected article node. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/article'); $edit = array( 'title' => 'protected ham', 'body[und][0][value]' => 'ham', ); $this->drupalPost(NULL, $edit, t('Save')); $this->node = $this->drupalGetNodeByTitle($edit['title']); $this->assertUrl('node/' . $this->node->nid); $this->assertMollomData('node', $this->node->nid); // Verify that no feedback options appear on the delete confirmation form // for the node author. $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertResponse(200); $this->assertNoText(t('Report as…')); // Verify that feedback options appear for the admin user. $this->drupalLogin($this->admin_user); $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertResponse(200); $this->assertText(t('Report as…')); // Login and submit an unprotected node. $this->drupalLogin($this->web_user); $this->drupalGet('node/add/unprotected'); $edit = array( 'title' => 'unprotected spam', 'body[und][0][value]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Save')); $this->node = $this->drupalGetNodeByTitle($edit['title']); $this->assertUrl('node/' . $this->node->nid); $this->assertNoMollomData('node', $this->node->nid); // Verify that no feedback options appear on the delete confirmation form // for the node author. $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertResponse(200); $this->assertNoText(t('Report as…')); // Verify that no feedback options appear for the admin user. $this->drupalLogin($this->admin_user); $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertResponse(200); $this->assertNoText(t('Report as…')); } } class MollomCommentFormTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Comment integration', 'description' => 'Check that the comment submission form can be protected.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('comment')); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); $this->web_user = $this->drupalCreateUser(array('create article content', 'access comments', 'post comments', 'skip comment approval')); $this->node = $this->drupalCreateNode(array('type' => 'article', 'uid' => $this->web_user->uid)); variable_set('comment_preview_article', DRUPAL_OPTIONAL); } /** * Make sure that the comment submission form can be unprotected. */ function testUnprotectedCommentForm() { // Request the comment reply form. There should be no CAPTCHA. $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertNoCaptchaField(); $this->assertNoPrivacyLink(); // Preview a comment that is 'spam' and make sure there is still no CAPTCHA. $this->drupalPost(NULL, array('comment_body[und][0][value]' => 'spam'), t('Preview')); $this->assertNoCaptchaField(); $this->assertNoPrivacyLink(); // Save the comment and make sure it appears. $this->drupalPost(NULL, array(), t('Save')); $this->assertRaw('

spam

', t('A comment that is known to be spam appears on the screen after it is submitted.')); } /** * Make sure that the comment submission form can be protected by captcha only. */ function testCaptchaProtectedCommentForm() { // Enable Mollom CAPTCHA protection for comments. $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Request the comment reply form. There should be a CAPTCHA form. $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertCaptchaField(); $this->assertResponseIDInForm('captchaId'); $this->assertNoPrivacyLink(); // Try to submit an incorrect answer for the CAPTCHA, without value for // required field. $this->postIncorrectCaptcha(NULL, array(), t('Preview')); $this->assertText(t('Comment field is required.')); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertNoPrivacyLink(); // Try to submit a correct answer for the CAPTCHA, still without required // field value. $this->postCorrectCaptcha(NULL, array(), t('Preview')); $this->assertText(t('Comment field is required.')); $captchaId = $this->assertResponseIDInForm('captchaId', TRUE); $this->assertNoPrivacyLink(); // Finally, we should be able to submit a comment. $this->drupalPost(NULL, array('comment_body[und][0][value]' => 'spam'), t('Save')); $this->assertText(t('Your comment has been posted.')); $this->assertRaw('

spam

', t('Spam comment could be posted with correct CAPTCHA.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => 'spam'))->fetchField(); $this->assertMollomData('comment', $cid, 'captchaId', $captchaId); // Verify we can solve the CAPTCHA directly. $this->resetResponseID(); $value = 'some more spam'; $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertCaptchaField(); $captchaId = $this->assertResponseIDInForm('captchaId'); $edit = array( 'comment_body[und][0][value]' => $value, 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertText(t('Your comment has been posted.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $value))->fetchField(); $this->assertMollomData('comment', $cid, 'captchaId', $captchaId); } /** * Make sure that the comment submission form can be fully protected. */ function testTextAnalysisProtectedCommentForm() { // Enable Mollom text-classification for comments. $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form'); $this->drupalLogout(); // Request the comment reply form. Initially, there should be no CAPTCHA. $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/'. $this->node->nid); $this->assertNoCaptchaField(); $this->assertPrivacyLink(); // Try to save a comment that is 'unsure' and make sure there is a CAPTCHA. $edit = array( 'comment_body[und][0][value]' => 'unsure', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertCaptchaField(); $contentId = $this->assertResponseIDInForm('contentId'); $this->assertPrivacyLink(); // Try to submit the form by solving the CAPTCHA incorrectly. At this point, // the submission should be blocked and a new CAPTCHA generated, but only if // the comment is still neither ham or spam. $this->postIncorrectCaptcha(NULL, array(), t('Save')); $this->assertCaptchaField(); $captchaId = $this->assertResponseIDInForm('captchaId'); $this->assertPrivacyLink(); // Correctly solving the CAPTCHA should accept the form submission. $this->postCorrectCaptcha(NULL, array(), t('Save')); $this->assertRaw('

' . $edit['comment_body[und][0][value]'] . '

', t('A comment that may contain spam was found.')); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => $edit['comment_body[und][0][value]']))->fetchField(); $this->assertMollomData('comment', $cid, 'contentId', $contentId); // Try to save a new 'spam' comment; it should be discarded, with no CAPTCHA // appearing on the page. $this->resetResponseID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); $this->assertSpamSubmit(NULL, array('comment_body[und][0][value]'), array(), t('Save')); $contentId = $this->assertResponseIDInForm('contentId'); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Try to save again; it should be discarded, with no CAPTCHA. $this->assertSpamSubmit(NULL, array('comment_body[und][0][value]'), array(), t('Save')); $contentId = $this->assertResponseIDInForm('contentId'); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Save a new 'ham' comment. $this->resetResponseID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); $this->assertHamSubmit(NULL, array('comment_body[und][0][value]'), array(), t('Save')); $this->assertRaw('

ham

', t('A comment that is known to be ham appears on the screen after it is submitted.')); $this->assertCommentCount($this->node->nid, $original_number_of_comments + 1); $cid = db_query('SELECT cid FROM {comment} WHERE subject = :subject ORDER BY created DESC', array(':subject' => 'ham'))->fetchField(); $this->assertMollomData('comment', $cid); } /** * Return the number of comments for a node of the given node ID. We * can't use comment_num_all() here, because that is statically cached * and therefore will not work correctly with the SimpleTest browser. */ private function getCommentCount($nid) { return db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array(':nid' => $nid))->fetchField(); } /** * Test that the number of comments for a node matches an expected value. * * @param $nid * A node ID * @param $expected * An integer with the expected number of comments for the node. * @param $message * An optional string with the message to be used in the assertion. */ protected function assertCommentCount($nid, $expected, $message = '') { $actual = $this->getCommentCount($nid); if (!$message) { $message = t('Node @nid has @actual comment(s), expected @expected.', array('@nid' => $nid, '@actual' => $actual, '@expected' => $expected)); } $this->assertEqual($actual, $expected, $message); } } /** * Tests Mollom actions. */ class MollomActionsTestCase extends MollomWebTestCase { // Keep track of nodes and comments for actions. protected $nodes = array(); protected $comments = array(); // Re-route Mollom communication to this testing site. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Actions integration', 'description' => 'Tests actions provided for comments and nodes.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('comment')); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); $this->web_user = $this->drupalCreateUser(array('create article content', 'access comments', 'post comments', 'skip comment approval')); $this->drupalLogin($this->admin_user); $this->setProtectionUI('article_node_form'); $this->setProtectionUI('comment_node_article_form'); $this->drupalLogout(); // Login and submit a node. $this->nodes = array(); $this->comments = array(); $this->drupalLogin($this->web_user); for ($i = 0; $i < 2; $i++) { // Create a test node. $edit = array( 'title' => 'ham node ' . $i, ); $this->drupalPost('node/add/article', $edit, t('Save')); $node = $this->drupalGetNodeByTitle($edit['title']); $this->drupalGet('comment/reply/' . $node->nid); $edit = array( 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost(NULL, $edit, t('Save')); $this->comments[] = db_query('SELECT * FROM {comment} WHERE subject = :comment AND nid = :nid', array(':comment' => $edit['comment_body[und][0][value]'], ':nid' => $node->nid))->fetchObject(); $this->nodes[] = $node; } } /** * Test that calling the mollom action function triggers the unpublish of * comments and marking as spam. */ function testCommentActions() { // Load the comment entity objects. $cids = array(); $contentIds = array(); foreach($this->comments as $comment) { $cids[] = $comment->cid; $data = mollom_data_load('comment', $comment->cid); $contentIds[] = $data->contentId; } $comment_entities = entity_load('comment', $cids); // Verify that all comments are currently published. $published = db_query('SELECT * FROM {comment} WHERE cid IN (:cids) AND status = 1', array(':cids' => $cids))->fetchAll(); $this->assertEqual(count($published), count($comment_entities)); // Call the unpublish action. mollom_action_unpublish_comment($comment_entities); // Verify that all comments are now unpublished. $published = db_query('SELECT * FROM {comment} WHERE cid IN (:cids) AND status = 1', array(':cids' => $cids))->fetchAll(); $this->assertEqual(count($published), 0); // Verify that all comments have been marked as spam. foreach ($cids as $cid) { $server = $this->getServerRecord('feedback'); $this->assertTrue(in_array($server['contentId'], $contentIds)); $this->assertEqual($server['source'], 'mollom_action_unpublish_comment'); $this->assertEqual($server['reason'], 'spam'); $this->assertEqual($server['type'], 'moderate'); } } /** * Test that calling the mollom action function triggers the unpublish of * nodes and marking as spam. */ function testNodeActions() { // Load the comment entity objects. $nids = array(); $contentIds = array(); foreach($this->nodes as $node) { $nids[] = $node->nid; $data = mollom_data_load('node', $node->nid); $contentIds[] = $data->contentId; } $node_entities = entity_load('node', $nids); // Verify that all nodes are currently published. $published = db_query('SELECT * FROM {node} WHERE nid IN (:nids) AND status = 1', array(':nids' => $nids))->fetchAll(); $this->assertEqual(count($published), count($node_entities)); // Call the unpublish action. mollom_action_unpublish_node($node_entities); // Verify that all comments are now unpublished. $published = db_query('SELECT * FROM {node} WHERE nid IN (:nids) AND status = 1', array(':nids' => $nids))->fetchAll(); $this->assertEqual(count($published), 0); // Verify that all nodes have been marked as spam. foreach ($nids as $nid) { $server = $this->getServerRecord('feedback'); $this->assertTrue(in_array($server['contentId'], $contentIds)); $this->assertEqual($server['source'], 'mollom_action_unpublish_node'); $this->assertEqual($server['reason'], 'spam'); $this->assertEqual($server['type'], 'moderate'); } } } /** * Tests protection of Contact module forms. */ class MollomContactFormTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Contact integration', 'description' => 'Tests protection of Contact module forms.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('contact', 'mollom')); $this->setKeys(); $this->assertValidKeys(); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer mollom', )); $this->web_user = $this->drupalCreateUser(array('access site-wide contact form', 'access user profiles', 'access user contact forms')); } /** * Make sure that the user contact form is protected correctly. */ function testProtectContactUserForm() { // Enable Mollom for the contact form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('contact_personal_form'); $this->drupalLogout(); $this->drupalLogin($this->web_user); $url = 'user/' . $this->admin_user->uid . '/contact'; $button = t('Send message'); $success = t('Your message has been sent.'); // Submit a 'spam' message. This should be blocked. $this->assertSpamSubmit($url, array('subject', 'message'), array(), $button); $this->assertNoText($success); // Submit a 'ham' message. This should be accepted. $this->assertHamSubmit($url, array('subject', 'message'), array(), $button); $this->assertText($success); $this->assertMailMollomReportLink(); // Submit an 'unsure' message. This should be accepted only after the // CAPTCHA has been solved. $this->assertUnsureSubmit($url, array('subject', 'message'), array(), $button, $success); $this->assertText($success); $this->assertMailMollomReportLink(); } /** * Make sure that the site-wide contact form is protected correctly. */ function testProtectContactSiteForm() { // Enable Mollom for the contact form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('contact_site_form'); $this->drupalLogout(); // Add some fields to the contact form so that it is active. // Empty 'reply' so as to not have to fiddle with auto-reply messages. $this->drupalLogin($this->web_user); db_insert('contact') ->fields(array( 'category' => 'test category', 'recipients' => $this->web_user->mail, 'reply' => '', )) ->execute(); $url = 'contact'; $button = t('Send message'); $success = t('Your message has been sent.'); // Submit a 'spam' message. This should be blocked. $this->assertSpamSubmit($url, array('subject', 'message'), array(), $button); $this->assertNoText($success); // Submit a 'ham' message. This should be accepted. $this->assertHamSubmit($url, array('subject', 'message'), array(), $button); $this->assertText($success); $this->assertMailMollomReportLink(); // Submit an 'unsure' message. This should be accepted only after the // CAPTCHA has been solved. $this->assertUnsureSubmit($url, array('subject', 'message'), array(), $button, $success); $this->assertText($success); $found = $this->assertMailMollomReportLink(); // Report the mail to Mollom. $this->drupalGet($found['url']); $edit = array( 'mollom[feedback]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Delete')); $this->assertText(t('The content was successfully reported as inappropriate.')); } } class MollomResellerTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Reseller functionality', 'description' => 'Check that the reseller APIs are working properly.', 'group' => 'Mollom', ); } /** * Make sure that resellers can create a new site. */ function testKeyManagement() { return; // Create 3 test sites: for ($i = 1; $i <= 3; $i++) { $keys[] = mollom()->createSite(array( 'url' => 'http://example.com/site-'. $i, 'mail' => 'mail@example.com', 'status' => 0, 'testing' => 1, )); } // Assert that there were no XML-RPC errors or watchdog messages. $this->assertMollomWatchdogMessages(); $sites = mollom('mollom.listSites'); foreach ($sites as $site) { // Retrieve the site information: $details = mollom()->getSite(array('client_key' => $site)); $this->assertEqual($details['mail'], 'mail@example.com', t('The original information is correctly retrieved from Mollom.')); $this->assertEqual($details['status'], 0, t('The original information is correctly retrieved from Mollom.')); $this->assertEqual($details['testing'], 1, t('The original information is correctly retrieved from Mollom.')); // Perform a safety check to avoid that the tests would delete // valid sites in case someone messed up their Mollom settings! if ($details['mail'] == 'mail@example.com' || $details['mail'] == 'root@example.com') { // Update the information on the site and verify that it was updated. mollom()->updateSite(array('client_key' => $site, 'mail' => 'root@example.com')); $details = mollom()->getSite(array('client_key' => $site)); $this->assertEqual($details['mail'], 'root@example.com', t('The updated information is correctly retrieved from Mollom.')); // Verify that the existing information did not change (partial updates). $this->assertEqual($details['status'], 0, t('The original information is correctly retrieved from Mollom.')); $this->assertEqual($details['testing'], 1, t('The original information is correctly retrieved from Mollom.')); // Delete the test site: mollom()->deleteSite(array('client_key' => $site)); } else { $this->fail(t('We tried to delete a non-test site.')); } } // Assert that there were no XML-RPC errors or watchdog messages. $this->assertMollomWatchdogMessages(); // Retrieve information about a non-existing site: $details = mollom()->getSite(array('client_key' => 'bogus')); $this->assertEqual(xmlrpc_errno(), TRUE, t('Retrieving information from a non-existing site returned an XML-RPC error.')); $this->assertMollomWatchdogMessages(WATCHDOG_EMERGENCY); // Verify that all sites have been deleted: $sites = mollom('mollom.listSites'); $this->assertEqual(count($sites), 0, t('All Mollom sites have been deleted.')); } } /** * Tests form value processing. */ class MollomDataTestCase extends MollomWebTestCase { // Re-route Mollom communication to this testing site. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Data processing', 'description' => 'Verify that form registry information is properly transformed into data that is sent to Mollom servers.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom_test')); } /** * Test mollom_form_get_values(). */ function testFormGetValues() { global $user; // Load the context node. $node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1)); // Form registry information. $form_info = array( 'elements' => array( 'subject' => 'Subject', 'message' => 'Message', 'parent][child' => 'Some nested element', 'field_checked' => 'Field to check', 'field_unchecked' => 'Field to ignore', ), 'mapping' => array( 'post_title' => 'subject', 'author_name' => 'name', 'author_mail' => 'mail', 'author_openid' => 'author][openid', 'context_id' => 'nid', ), ); // Fields configured via Mollom admin UI based on $form_info['elements']. $fields = array( 'subject', 'message', 'parent][child', 'field_checked', ); // Verify submitted form values for an anonymous/arbitrary user. $user = drupal_anonymous_user(); $values = array( 'subject' => 'Foo', 'message' => 'Bar', 'parent' => array( 'child' => 'Beer', ), 'author' => array( 'openid' => 'Vodka', ), 'field_checked' => array( 'und' => array( 0 => array('value' => ''), 1 => array('summary' => 'Check summary', 'value' => 'Check first', 'whatever' => 'whatever'), 2 => array('value' => 'Check second'), 3 => array('nested_empty' => array()), 4 => array('nested_weird' => array('')), ), ), 'field_unchecked' => array( 'und' => array( 0 => array('value' => 'Ignore first'), 1 => array('value' => 'Ignore second'), ), ), 'name' => 'Drupaler', 'mail' => 'drupaler@example.com', 'nid' => $node->nid, ); $form_state = array('values' => $values, 'buttons' => array()); $form_state['mollom']['context created callback'] = 'node_mollom_context_created'; $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $data += array_fill_keys(array('postTitle', 'postBody', 'authorId', 'authorCreated', 'authorName', 'authorMail', 'authorUrl', 'authorOpenid', 'authorIp', 'contextCreated'), NULL); $this->assertSame('postTitle', $data['postTitle'], $values['subject']); $body = array( $values['message'], $values['parent']['child'], $values['field_checked']['und'][1]['summary'], $values['field_checked']['und'][1]['value'], $values['field_checked']['und'][2]['value'], ); $this->assertSame('postBody', $data['postBody'], implode("\n", $body)); $this->assertSame('authorId', $data['authorId'], NULL); $this->assertSame('authorCreated', $data['authorCreated'], NULL); $this->assertSame('authorName', $data['authorName'], $values['name']); $this->assertSame('authorMail', $data['authorMail'], $values['mail']); $this->assertSame('authorUrl', $data['authorUrl'], NULL); $this->assertSame('authorOpenid', $data['authorOpenid'], $values['author']['openid']); $this->assertSame('authorIp', $data['authorIp'], ip_address()); $this->assertSame('contextCreated', $data['contextCreated'], $node->created); // Verify submitted form values for an anonymous user whose name happens to // match a registered user. $values = array( 'subject' => 'Foo', 'message' => 'Bar', 'parent' => array( 'child' => 'Beer', ), 'author' => array( 'openid' => 'Vodka', ), 'field_checked' => array( 0 => array('value' => 'Check first'), 1 => array('value' => 'Check second'), ), 'field_unchecked' => array( 0 => array('value' => 'Ignore first'), 1 => array('value' => 'Ignore second'), ), 'name' => $this->admin_user->name, 'nid' => $node->nid, ); $form_state = array('values' => $values, 'buttons' => array()); $form_state['mollom']['context created callback'] = 'node_mollom_context_created'; $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $data += array_fill_keys(array('postTitle', 'postBody', 'authorId', 'authorCreated', 'authorName', 'authorMail', 'authorUrl', 'authorOpenid', 'authorIp', 'contextCreated'), NULL); $this->assertSame('postTitle', $data['postTitle'], $values['subject']); $body = array( $values['message'], $values['parent']['child'], $values['field_checked'][0]['value'], $values['field_checked'][1]['value'], ); $this->assertSame('postBody', $data['postBody'], implode("\n", $body)); $this->assertSame('authorId', $data['authorId'], NULL); $this->assertSame('authorCreated', $data['authorCreated'], NULL); $this->assertSame('authorName', $data['authorName'], $values['name']); $this->assertSame('authorMail', $data['authorMail'], NULL); $this->assertSame('authorUrl', $data['authorUrl'], NULL); $this->assertSame('authorOpenid', $data['authorOpenid'], $values['author']['openid']); $this->assertSame('authorIp', $data['authorIp'], ip_address()); $this->assertSame('contextCreated', $data['contextCreated'], $node->created); // Verify submitted form values for a registered user. $user = $this->admin_user; $values = array( 'subject' => 'Foo', 'message' => 'Bar', 'name' => $this->admin_user->name, 'nid' => $node->nid, ); $form_state = array('values' => $values, 'buttons' => array()); $form_state['mollom']['context created callback'] = 'node_mollom_context_created'; $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $data += array_fill_keys(array('postTitle', 'postBody', 'authorId', 'authorCreated', 'authorName', 'authorMail', 'authorUrl', 'authorOpenid', 'authorIp', 'contextCreated'), NULL); $this->assertSame('postTitle', $data['postTitle'], $values['subject']); $this->assertSame('postBody', $data['postBody'], $values['message']); $this->assertSame('authorId', $data['authorId'], $this->admin_user->uid); $this->assertSame('authorCreated', $data['authorCreated'], $this->admin_user->created); $this->assertSame('authorName', $data['authorName'], $this->admin_user->name); $this->assertSame('authorMail', $data['authorMail'], $this->admin_user->mail); $this->assertSame('authorUrl', $data['authorUrl'], NULL); $this->assertSame('authorOpenid', $data['authorOpenid'], NULL); $this->assertSame('authorIp', $data['authorIp'], ip_address()); $this->assertSame('contextCreated', $data['contextCreated'], $node->created); // Verify that invalid UTF-8 is detected. $values = array( 'subject' => "Foo \xC0 bar", ); $form_state = array('values' => $values, 'buttons' => array()); $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $this->assertFalse($data, 'Invalid UTF-8 detected.'); $this->assertMollomWatchdogMessages(); // Verify that invalid UTF-8 is detected in the CAPTCHA solution element. $values = array( 'mollom' => array( 'captcha' => "Foo \xC0 bar", ), ); $form_state = array('values' => $values, 'buttons' => array()); $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $this->assertFalse($data, 'Invalid UTF-8 detected in CAPTCHA solution.'); $this->assertMollomWatchdogMessages(); // Verify that invalid XML characters are detected. $values = array( 'subject' => "Foo \x11 bar", ); $form_state = array('values' => $values, 'buttons' => array()); $data = mollom_form_get_values($form_state, $fields, $form_info['mapping']); $this->assertFalse($data, 'Invalid XML characters detected.'); $this->assertMollomWatchdogMessages(); } /** * Test that form button values are not contained in postBody sent to Mollom. */ function testFormButtonValues() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form'); $this->drupalLogout(); // Verify that neither the "Submit" nor the "Add" button value is contained // in the post body. $edit = array( 'title' => 'ham', 'body' => 'ham', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $data = $this->getServerRecord(); $this->assertFalse(preg_match('@Submit|Add@', $data['postBody']), 'Button values not found in post body.'); } /** * Test submitted post and author information for textual analysis. */ function testAnalysis() { // Create Article node type. $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); // Enable comments, make comment preview optional, and grant permissions. module_enable(array('comment')); variable_set('comment_preview_article', DRUPAL_OPTIONAL); user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access comments', 'post comments')); user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access comments', 'post comments', 'skip comment approval')); // Add 'administer comments' permission to $admin_user. $rid = current(array_diff(array_keys($this->admin_user->roles), array(DRUPAL_AUTHENTICATED_RID))); user_role_grant_permissions($rid, array('administer comments')); // Rebuild menu router, permissions, etc. // @todo Remove in D8. $this->resetAll(); $this->drupalLogin($this->admin_user); $this->setProtection('comment_node_article_form'); // Create a node we can comment on. $node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); $this->drupalGet(''); $this->assertText($node->title); $this->drupalLogout(); // Log in regular user and post a comment. $this->web_user = $this->drupalCreateUser(); $this->drupalLogin($this->web_user); $this->drupalGet(''); $this->clickLink(t('Add new comment')); $edit = array( 'subject' => $this->randomString(), 'comment_body[und][0][value]' => 'unsure', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertText($this->unsure_message); // Verify that submitted data equals post data. $data = $this->getServerRecord(); debug($data); $this->assertSame('postTitle', $data['postTitle'], $edit['subject']); $this->assertSame('postBody', $data['postBody'], $edit['comment_body[und][0][value]']); $this->assertSame('authorName', $data['authorName'], $this->web_user->name); $this->assertSame('authorMail', $data['authorMail'], $this->web_user->mail); $this->assertSame('authorId', $data['authorId'], $this->web_user->uid); $this->assertSame('strictness', $data['strictness'], 'normal'); $this->PostCorrectCaptcha(NULL, array(), t('Save')); $comment = db_query('SELECT * FROM {comment} WHERE subject = :subject', array(':subject' => $edit['subject']))->fetchObject(); $this->assertTrue($comment, t('Comment exists in database.')); // Verify that submitted data equals post data. $data = $this->getServerRecord('captcha'); $this->assertSame('authorId', $data['authorId'], $this->web_user->uid); // Allow anonymous users to post comments without approval. user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('skip comment approval')); // Allow anonymous users to post contact information. variable_set('comment_anonymous_article', COMMENT_ANONYMOUS_MAY_CONTACT); // Log out and post a comment as anonymous user. $this->resetServerRecords(); $this->drupalLogout(); $this->drupalGet('node/' . $node->nid); $this->clickLink(t('Add new comment')); // Ensure we have some potentially escaped characters in the values. $edit = array( 'name' => $this->randomString(6) . ' & ' . $this->randomString(8), 'mail' => 'mollom@example.com', 'homepage' => 'http://www.mollom.com', 'subject' => '"' . $this->randomString() . '"', 'comment_body[und][0][value]' => 'unsure', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertText($this->unsure_message); // Verify that submitted data equals post data. $data = $this->getServerRecord(); $this->assertSame('postTitle', $data['postTitle'], $edit['subject']); $this->assertSame('postBody', $data['postBody'], $edit['comment_body[und][0][value]']); $this->assertSame('authorName', $data['authorName'], $edit['name']); $this->assertSame('authorMail', $data['authorMail'], $edit['mail']); $this->assertSame('authorUrl', $data['authorUrl'], $edit['homepage']); $this->assertFalse(isset($data['authorId']), t('authorId: Undefined.')); $this->PostCorrectCaptcha(NULL, array(), t('Save')); $comment = db_query('SELECT * FROM {comment} WHERE subject = :subject', array(':subject' => $edit['subject']))->fetchObject(); $this->assertTrue($comment, t('Comment exists in database.')); // Verify that submitted data equals post data. $data = $this->getServerRecord('captcha'); $this->assertFalse(isset($data['authorId']), t('authorId: Undefined.')); // Log in admin user and edit comment containing spam. $this->resetServerRecords(); $this->drupalLogin($this->admin_user); $this->drupalGet('comment/' . $comment->cid . '/edit'); // Post without modification. $this->drupalPost(NULL, array(), t('Save')); // Verify that no data was submitted to Mollom. $data = $this->getServerRecord(); $this->assertFalse($data, t('Administrative form submission was not validated by Mollom.')); } /** * Tests that protected forms contain form behavior analysis and its value is recorded. */ function testFormBehaviorAnalysis() { // Enable protection for mollom_test_form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form'); $this->drupalLogout(); // Test form behavior analysis when enabled. variable_set('mollom_fba_enabled', 1); // Verify that the tracking image is output. $this->drupalGet('mollom-test/form'); $this->assertFieldByName('mollom[fba]', ''); // Generate a CAPTCHA. $edit = array( 'title' => 'unsure', 'body' => 'unsure', 'mollom[fba]' => 'FBA-VALUE', ); $this->drupalPost(NULL, $edit, 'Submit'); // Verify that the tracking image value was sent to mollom.checkContent. $data = $this->getServerRecord(); $this->assertSame('trackingImageId', $data['trackingImageId'], $edit['mollom[fba]']); // Complete the verification. $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); // Verify that the tracking image value was not sent to mollom.checkContent. $data = $this->getServerRecord(); $this->assertTrue(empty($data['trackingImageId'])); // Verify that a blank tracking image is sent as -1. $this->drupalGet('mollom-test/form'); $edit = array( 'title' => 'ham', 'body' => 'ham', 'mollom[fba]' => '', ); $this->drupalPost(NULL, $edit, 'Submit'); // Verify that the tracking image empty indicator was sent to mollom.checkContent. $data = $this->getServerRecord(); $this->assertSame('trackingImageId', $data['trackingImageId'], '-1'); // Disable form behavior analysis. variable_set('mollom_fba_enabled', 0); // Verify that the tracking image is not output. $this->drupalGet('mollom-test/form'); $this->assertNoFieldByName('mollom[fba]'); // Post the content. $edit = array( 'title' => 'ham', 'body' => 'ham', ); $this->drupalPost(NULL, $edit, 'Submit'); // Verify that there is no tracking image value sent to mollom.checkContent. $data = $this->getServerRecord(); $this->assertTrue(empty($data['trackingImageId'])); } /** * Tests that protected forms contain a hidden honeypot field and its value is recorded. */ function testHoneypot() { // Enable protection for mollom_test_form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form'); $this->drupalLogout(); // Verify that the hidden honeypot field is output. $this->drupalGet('mollom-test/form'); $elements = $this->xpath('//div[contains(@style, :style)]/descendant::input[@name = :name]', array( ':style' => 'display: none', ':name' => 'mollom[homepage]', )); $this->assertEqual(count($elements), 1, t('Hidden honeypot field found.')); // Verify that a honeypot value is sent to mollom.checkContent. $edit = array( 'title' => 'unsure', 'body' => 'unsure', 'mollom[homepage]' => 'HONEYPOT-VALUE', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertCaptchaField(); $data = $this->getServerRecord(); $this->assertSame('honeypot', $data['honeypot'], $edit['mollom[homepage]']); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); $data = $this->getServerRecord(); $this->assertSame('honeypot', $data['honeypot'], $edit['mollom[homepage]']); $data = $this->getServerRecord('captcha'); $this->assertSame('honeypot', $data['honeypot'], $edit['mollom[homepage]']); // Change form protection to CAPTCHA only. $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); $this->resetServerRecords(); // Verify that the hidden honeypot field is output. $this->drupalGet('mollom-test/form'); $elements = $this->xpath('//div[contains(@style, :style)]/descendant::input[@name = :name]', array( ':style' => 'display: none', ':name' => 'mollom[homepage]', )); $this->assertEqual(count($elements), 1, t('Hidden honeypot field found.')); // Verify that a honeypot value is sent to mollom.checkContent. // postCorrectCaptcha() cannot be used for mollom_test_form, since the form // is re-displayed again after a successful form submission. $edit = array( 'title' => $this->randomString(), 'mollom[captcha]' => 'correct', 'mollom[homepage]' => 'HONEYPOT-VALUE', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertText('Successful form submission.'); $data = $this->getServerRecord('captcha'); $this->assertSame('honeypot', $data['honeypot'], $edit['mollom[homepage]']); } /** * Tests automated 'post_id' mapping and session data storage. * * This is an atomic test to verify that a simple 'post_id' mapping defined * via hook_mollom_form_info() is sufficient for basic integration with * Mollom (without reporting). */ function testPostIdMapping() { // Enable protection for mollom_test_form. $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form'); $this->drupalLogout(); // Submit a mollom_test thingy. $edit = array( 'title' => 'ham', 'body' => $this->randomString(), ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $this->assertText('Successful form submission.'); $mid = $this->getFieldValueByName('mid'); $this->assertTrue($mid > 0, t('Submission was stored.')); $data = $this->assertMollomData('mollom_test', $mid); // Ensure we were redirected to the form for the stored entry. $this->assertFieldByName('body', $edit['body'], t('Existing body value found.')); $new_mid = $this->getFieldValueByName('mid'); $this->assertEqual($new_mid, $mid, t('Existing entity id found.')); // Verify that session data was stored. $this->assertSame('entity', $data->entity, 'mollom_test'); $this->assertSame('id', $data->id, $mid); $this->assertSame('form_id', $data->form_id, 'mollom_test_form'); $count = db_query("SELECT COUNT(1) FROM {mollom}")->fetchField(); $this->assertEqual($count, 1, t('Data was stored in {mollom}.')); // Update the stored entry. $edit['title'] = 'unsure'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertCaptchaField(); $contentId = $this->assertResponseIDInForm('contentId'); $captchaId = $this->assertResponseIDInForm('captchaId'); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); $new_data = $this->assertMollomData('mollom_test', $mid); // Verify that only session data was updated. $this->assertSame('entity', $data->entity, $new_data->entity); $this->assertSame('id', $data->id, $new_data->id); $this->assertNotSame('contentId', $data->contentId, $new_data->contentId); $this->assertNotSame('captchaId', $data->captchaId, $new_data->captchaId); $this->assertSame('form_id', $data->form_id, $new_data->form_id); $this->assertSame('qualityScore', $data->qualityScore, $new_data->qualityScore); $count = db_query("SELECT COUNT(1) FROM {mollom}")->fetchField(); $this->assertEqual($count, 1, t('Stored data in {mollom} was updated.')); } /** * Tests data sent for Mollom::verifyKeys(). */ function testVerifyKeys() { $this->drupalLogin($this->admin_user); $this->drupalGet('admin/config/content/mollom/settings'); // Verify that we additionally sent version data. $data = $this->getServerRecord('site'); $info = mollom()->getClientInformation(); $this->assertTrue(!empty($info['platformName']), t('Version information found.')); $this->assertSame('platformName', $data['platformName'], $info['platformName']); $this->assertSame('platformVersion', $data['platformVersion'], $info['platformVersion']); $this->assertSame('clientName', $data['clientName'], $info['clientName']); $this->assertSame('clientVersion', $data['clientVersion'], $info['clientVersion']); } } /** * Verify that Mollom data can be created, read, updated, and deleted. */ class MollomDataCRUDTestCase extends MollomWebTestCase { protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Data CRUD', 'description' => 'Verify that Mollom data can be created, read, updated, and deleted.', 'group' => 'Mollom', ); } /** * Verify that Mollom data can be updated. * * Also verifies that the combined primary/unique database schema index is * properly accounted for; i.e., two entities having the same ID but different * types must not considered the same. */ function testUpdate() { // Create a first data record. $data1 = (object) array( 'entity' => 'type1', 'id' => 123, 'form_id' => 'type1_form', 'contentId' => 1, ); mollom_data_save($data1); $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); // Create a second data record; same ID, different entity type. $data2 = (object) array( 'entity' => 'type2', 'id' => 123, 'form_id' => 'type2_form', 'contentId' => 2, ); mollom_data_save($data2); $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); // Update the first data record. $data1->contentId = 3; mollom_data_save($data1); // Verify that both records are correct. $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); } /** * Verify that Mollom data can be deleted. */ function testDelete() { // Create a data record. $data1 = (object) array( 'entity' => 'type1', 'id' => 123, 'form_id' => 'type1_form', 'contentId' => 1, ); mollom_data_save($data1); // Create a second data record; same ID, different entity type. $data2 = (object) array( 'entity' => 'type2', 'id' => 123, 'form_id' => 'type2_form', 'contentId' => 2, ); mollom_data_save($data2); // Verify that both records exist. $this->assertMollomData($data1->entity, $data1->id, 'contentId', $data1->contentId); $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); // Delete the first data record. mollom_data_delete($data1->entity, $data1->id); // Verify that only the second record remained and was not changed. $this->assertNoMollomData($data1->entity, $data1->id); $this->assertMollomData($data2->entity, $data2->id, 'contentId', $data2->contentId); } } /** * Tests text analysis functionality. */ class MollomAnalysisTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; // Re-route Mollom communication to this testing site. // @todo Remove this when Testing API is fixed. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'Text analysis', 'description' => 'Tests text analysis functionality.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); $this->setKeys(TRUE); $this->assertValidKeys(); $this->setProtection('mollom_test_form', MOLLOM_MODE_ANALYSIS); } function testUnsure() { $methods = get_class_methods($this); foreach ($methods as $method) { if (substr($method, 0, 7) === 'subtest') { //debug($method); $this->$method(); } } } /** * Tests basic unsure submission flow. */ function subtestUnsureCorrect() { $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $edit = array( 'title' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId', TRUE); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertCaptchaField(); $edit = array( 'title' => 'unsure unsure', 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertTestSubmitData(); } /** * Tests unsure post with repetitive incorrect CAPTCHA solution. * * @todo CAPTCHA ID should stay the same. * @see http://drupal.org/node/1959904 */ function subtestUnsureIncorrect() { $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $edit = array( 'title' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId', TRUE); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertCaptchaField(); $edit = array(); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); // $this->assertResponseIDInForm('captchaId'); $this->assertCaptchaField(); $edit = array( 'mollom[captcha]' => 'incorrect', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); // $this->assertResponseIDInForm('captchaId'); $this->assertCaptchaField(); $edit = array( 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertTestSubmitData(); } /** * Tests unsure post with other validation errors. */ function subtestUnsureValidation() { // The 'title' field is required. Omit its value to verify that the CAPTCHA // can be solved, repetitive form validations do not show a CAPTCHA again, // and the post can finally be submitted by providing a title. $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $edit = array( 'body' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId', TRUE); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertCaptchaField(); $edit = array( 'body' => 'unsure unsure', 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); $edit = array( 'body' => 'unsure unsure unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); $edit = array( 'title' => 'unsure', 'body' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertTestSubmitData(); } /** * Tests unsure posts turning into ham. */ function subtestUnsureHam() { $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); // Posting unsure as title would submit the form, so post as body instead. $edit = array( 'body' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId', TRUE); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertCaptchaField(); // Turn the post into ham. $edit = array( 'body' => 'ham', 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); // Turn the post back into unsure. $edit = array( 'body' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); $edit = array( 'title' => 'irrelevant', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertTestSubmitData(); } /** * Tests unsure posts turning into spam. * * @todo CAPTCHA ID should stay the same. * @see http://drupal.org/node/1959904 */ function subtestUnsureSpam() { $this->drupalGet('mollom-test/form'); $this->assertNoCaptchaField(); $edit = array( 'body' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId', TRUE); $this->assertResponseIDInForm('captchaId', TRUE); $this->assertCaptchaField(); $edit = array( 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); // Turn the post into spam. $edit = array( 'title' => 'spam', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertResponseIDInForm('contentId'); $this->assertNoCaptchaField(); $this->assertText($this->spam_message); $this->assertNoText('Successful form submission.'); // Turn the post back into unsure. $edit = array( 'title' => 'unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertTestSubmitData(); } } /** * Tests text analysis with page cache. */ class MollomAnalysisPageCacheTestCase extends MollomAnalysisTestCase { public static function getInfo() { return array( 'name' => 'Text analysis (page cache)', 'description' => 'Tests text analysis with page cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); } } /** * Tests text analysis with page and form cache. */ class MollomAnalysisPageFormCacheTestCase extends MollomAnalysisTestCase { public static function getInfo() { return array( 'name' => 'Text analysis (page + form cache)', 'description' => 'Tests text analysis with page and form cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); variable_set('mollom_test.form.cache', TRUE); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); } } /** * Tests text analysis as authenticated user. */ class MollomAnalysisAuthenticatedTestCase extends MollomAnalysisTestCase { public static function getInfo() { return array( 'name' => 'Text analysis (authenticated)', 'description' => 'Tests text analysis as authenticated user.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->web_user = $this->drupalCreateUser(array()); $this->drupalLogin($this->web_user); } } /** * Tests text analysis as authenticated user with enabled page and form cache. */ class MollomAnalysisAuthenticatedPageFormCacheTestCase extends MollomAnalysisTestCase { public static function getInfo() { return array( 'name' => 'Text analysis (authenticated + page + form cache)', 'description' => 'Tests text analysis as authenticated user with enabled page and form cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); variable_set('mollom_test.form.cache', TRUE); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->web_user = $this->drupalCreateUser(array()); $this->drupalLogin($this->web_user); } } /** * Tests text analysis functionality. */ class MollomAnalysisOptionsTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Text analysis options', 'description' => 'Tests text analysis binary mode, retaining unsure/spam.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); $this->setKeys(); $this->assertValidKeys(); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer mollom', )); } /** * Tests binary unsure mode. */ function testUnsureBinary() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, array( 'mollom[unsure]' => 'binary', )); $this->drupalLogout(); // Verify that an unsure post is ham. // Note: The actual binary mode of Mollom's production API is more granular. $edit = array( 'title' => $this->randomString(), 'body' => 'unsure', ); $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); $mid = $this->assertTestSubmitData(); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 1, t('Published test post found.')); $this->assertSame('spamScore', $data->spamScore, 0); $this->assertSame('spamClassification', $data->spamClassification, 'ham'); $this->assertSame('moderate', $data->moderate, 0); // Verify that a ham post is accepted. $edit = array( 'title' => $this->randomString(), 'body' => 'ham', ); $this->assertHamSubmit('mollom-test/form', array(), $edit, 'Submit'); $mid = $this->assertTestSubmitData(); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 1, t('Published test post found.')); $this->assertSame('spamScore', $data->spamScore, 0); $this->assertSame('spamClassification', $data->spamClassification, 'ham'); $this->assertSame('moderate', $data->moderate, 0); // Verify that a spam post is blocked. $edit = array( 'title' => $this->randomString(), 'body' => 'spam', ); $this->assertSpamSubmit('mollom-test/form', array(), $edit, 'Submit'); } /** * Tests retaining unsure posts and moderating them. */ function testRetainUnsure() { $this->drupalLogin($this->admin_user); // Verify that mollom_basic_elements_test_form cannot be configured to put // posts into moderation queue. $this->setProtectionUI('mollom_basic_elements_test_form'); $this->drupalGet('admin/config/content/mollom/manage/mollom_basic_elements_test_form'); $this->assertNoFieldByName('mollom[unsure]'); // Configure mollom_test_form to retain unsure posts. $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, array( 'mollom[unsure]' => 'moderate', )); $this->drupalLogout(); // Verify that an unsure post gets unpublished. $edit = array( 'title' => $this->randomString(), 'body' => 'unsure', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $mid = $this->assertTestSubmitData(); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 0, t('Unpublished test post found.')); $this->assertSame('spamScore', $data->spamScore, 0.5); $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); $this->assertSame('moderate', $data->moderate, 1); // Verify that editing the post does neither change the session data, nor // the publishing status. $edit = array( 'title' => $this->randomString(), 'body' => 'unsure unsure', ); $this->drupalPost(NULL, $edit, 'Submit'); $mid = $this->assertTestSubmitData($mid); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 0, t('Unpublished test post found.')); $this->assertSame('spamScore', $data->spamScore, 0.5); $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); $this->assertSame('moderate', $data->moderate, 1); // Verify that publishing the post changes the session data accordingly. $this->drupalLogin($this->admin_user); $edit = array( 'status' => TRUE, ); $this->drupalPost('mollom-test/form/' . $mid, $edit, 'Submit'); $mid = $this->assertTestSubmitData($mid); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 1, t('Published test post found.')); $this->assertSame('spamScore', $data->spamScore, 0.5); $this->assertSame('spamClassification', $data->spamClassification, 'unsure'); $this->assertSame('moderate', $data->moderate, 0); } /** * Tests retaining spam posts and moderating them. */ function testRetainSpam() { $this->drupalLogin($this->admin_user); // Verify that mollom_basic_test_form cannot be configured to put posts into // moderation queue. $this->setProtectionUI('mollom_basic_elements_test_form'); $this->drupalGet('admin/config/content/mollom/manage/mollom_basic_elements_test_form'); $this->assertNoFieldByName('mollom[discard]'); // Configure mollom_test_form to accept bad posts. $this->setProtectionUI('mollom_test_form', MOLLOM_MODE_ANALYSIS, NULL, array( 'mollom[checks][profanity]' => TRUE, 'mollom[discard]' => 0, )); $this->drupalLogout(); // Verify that we are able to post spam and the post is unpublished. $edit = array( 'title' => $this->randomString(), 'body' => 'spam profanity', ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); $mid = $this->assertTestSubmitData(); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 0, t('Unpublished test post found.')); $this->assertSame('spamScore', $data->spamScore, 1.0); $this->assertSame('spamClassification', $data->spamClassification, 'spam'); $this->assertSame('profanityScore', $data->profanityScore, 1); $this->assertSame('moderate', $data->moderate, 1); // Verify that editing the post does neither change the session data, nor // the publishing status. $edit = array( 'title' => $this->randomString(), 'body' => 'spam profanity spam profanity', ); $this->drupalPost(NULL, $edit, 'Submit'); $mid = $this->assertTestSubmitData($mid); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 0, t('Unpublished test post found.')); $this->assertSame('spamScore', $data->spamScore, 1.0); $this->assertSame('spamClassification', $data->spamClassification, 'spam'); $this->assertSame('profanityScore', $data->profanityScore, 1); $this->assertSame('moderate', $data->moderate, 1); // Verify that publishing the post changes the session data accordingly. $this->drupalLogin($this->admin_user); $edit = array( 'status' => TRUE, ); $this->drupalPost('mollom-test/form/' . $mid, $edit, 'Submit'); $mid = $this->assertTestSubmitData($mid); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 1, t('Published test post found.')); $this->assertSame('spamScore', $data->spamScore, 1.0); $this->assertSame('spamClassification', $data->spamClassification, 'spam'); $this->assertSame('profanityScore', $data->profanityScore, 1); $this->assertSame('moderate', $data->moderate, 0); // Verify that neither ham or unsure spam posts, nor non-profane posts are // marked for moderation. $this->drupalLogout(); $expectations = array( 'ham' => array('spamScore' => 0.0, 'spamClassification' => 'ham', 'profanityScore' => 0), 'unsure' => array('spamScore' => 0.0, 'spamClassification' => 'unsure', 'profanityScore' => 0), $this->randomString() => array('spamScore' => 0.0, 'spamClassification' => 'unsure', 'profanityScore' => 0), ); foreach ($expectations as $body => $expected) { $edit = array( 'title' => $this->randomString(), 'body' => $body, ); $this->drupalPost('mollom-test/form', $edit, 'Submit'); if ($expected['spamClassification'] == 'unsure') { $this->postCorrectCaptcha(NULL, array(), 'Submit'); $expected['spamClassification'] = 'ham'; } $mid = $this->assertTestSubmitData(); $data = $this->assertMollomData('mollom_test', $mid); $record = mollom_test_load($mid); $this->assertEqual($record->status, 1, t('Published test post %body found.', array('%body' => $body))); $this->assertSame('spamScore', $data->spamScore, $expected['spamScore']); $this->assertSame('spamClassification', $data->spamClassification, $expected['spamClassification']); $this->assertSame('profanityScore', $data->profanityScore, $expected['profanityScore']); $this->assertSame('moderate', $data->moderate, 0); } } } /** * Tests basic text analysis functionality with enabled caching. */ class MollomAnalysisPageCachingTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; public static function getInfo() { return array( 'name' => 'Text analysis with caching', 'description' => 'Tests basic text analysis functionality with enabled caching.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); $this->setKeys(); $this->assertValidKeys(); $this->enablePageCache(); } /** * Tests text analysis. */ function testAnalysis() { $this->setProtection('mollom_test_form'); // Prime the form + page cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertUnsureSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertSpamSubmit(NULL, array('title'), array(), 'Submit'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->assertHamSubmit(NULL, array('title'), array(), 'Submit'); } /** * Tests text analysis with additionally enabled Form API cache. */ function testAnalysisFormCache() { variable_set('mollom_test.form.cache', TRUE); $this->testAnalysis(); } } /** * Tests CAPTCHA functionality. */ class MollomCaptchaTestCase extends MollomWebTestCase { protected $disableDefaultSetup = TRUE; // Re-route Mollom communication to this testing site. protected $mollomClass = 'MollomDrupalTestLocal'; public static function getInfo() { return array( 'name' => 'CAPTCHA', 'description' => 'Tests CAPTCHA functionality.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('mollom', 'mollom_test')); $this->setKeys(TRUE); $this->assertValidKeys(); $this->setProtection('mollom_test_form', MOLLOM_MODE_CAPTCHA); } function testCAPTCHA() { $methods = get_class_methods($this); foreach ($methods as $method) { if (substr($method, 0, 7) === 'subtest') { //debug($method); $this->$method(); } } } /** * Tests #required validation of CAPTCHA form element. */ function subtestCAPTCHARequired() { $this->drupalGet('mollom-test/form'); // Verify that CAPTCHA cannot be left empty. $this->assertCaptchaField(); $this->drupalPost(NULL, array(), 'Submit'); $this->assertText($this->incorrect_message); $this->assertNoText('Successful form submission.'); // Verify it again on subsequent POST. $this->assertCaptchaField(); $this->drupalPost(NULL, array(), 'Submit'); $this->assertText($this->incorrect_message); $this->assertNoText('Successful form submission.'); // Verify that incorrect solution still leaves the field required. $edit = array( 'title' => $this->randomString(), ); $this->postIncorrectCaptcha(NULL, $edit, 'Submit', 'Successful form submission.'); // Verify correct solution, but trigger other validation errors. $edit = array( 'title' => '', 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertNoText('Successful form submission.'); // Lastly, confirm we're able to submit. $edit = array( 'title' => $this->randomString(), ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoText($this->incorrect_message); $this->assertTestSubmitData(); } /** * Tests incorrect solution of CAPTCHA form element. */ function subtestCAPTCHAIncorrect() { $this->drupalGet('mollom-test/form'); // Verify that incorrect solution still leaves the field required. $edit = array( 'title' => $this->randomString(), ); $this->postIncorrectCaptcha(NULL, $edit, 'Submit', 'Successful form submission.'); // Lastly, verify correct solution. $edit = array( 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertTestSubmitData(); } /** * Tests correct solution of CAPTCHA. */ function subtestCAPTCHACorrect() { $this->drupalGet('mollom-test/form'); $edit = array( 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertNoText('Successful form submission.'); $edit = array( 'body' => $this->randomString(), ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertNoCaptchaField(); $this->assertNoText('Successful form submission.'); $edit = array( 'title' => $this->randomString(), ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertTestSubmitData(); } /** * Tests correct solution of CAPTCHA in a single pass. */ function subtestCAPTCHACorrectSinglePass() { $this->drupalGet('mollom-test/form'); // Verify that CAPTCHA can be solved in one shot. $edit = array( 'title' => $this->randomString(), 'mollom[captcha]' => 'correct', ); $this->drupalPost(NULL, $edit, 'Submit'); $this->assertTestSubmitData(); } /** * Tests the CAPTCHA type switch callback. */ function subtestCAPTCHASwitchCallback() { // Verify that the CAPTCHA can be switched on a CAPTCHA-only protected form. // (without having a contentId) $this->drupalGet('mollom-test/form'); $form_build_id = $this->getFieldValueByName('form_build_id'); // @see drupalPost(), drupalGet() $path = url('mollom/captcha/audio/' . $form_build_id, array('absolute' => TRUE)); $out = $this->curlExec(array( CURLOPT_URL => $path, CURLOPT_POST => TRUE, )); // Ensure that any changes to variables in the other thread are picked up. $this->refreshVariables(); $this->verbose('POST request to: ' . $path . '
Ending URL: ' . $this->getUrl() . '
' . $out); $this->assertResponse(200); $this->assertText('mollom-captcha-player.swf'); $response = drupal_json_decode($out); $this->assertTrue($response['captchaId']); } /** * Tests the CAPTCHA audio enable/disable functionality. */ function subTestCAPTCHAAudioEnable() { // Default should be enabled audio. $this->drupalGet('mollom-test/form'); $this->assertLink(t('Switch to audio verification')); // Verify that CAPTCHA cannot be switched when audio is disabled. variable_set('mollom_audio_captcha_enabled', 0); $this->drupalGet('mollom-test/form'); $this->assertNoLink(t('Switch to audio verification')); } } /** * Tests CAPTCHA with page cache. */ class MollomCaptchaPageCacheTestCase extends MollomCaptchaTestCase { public static function getInfo() { return array( 'name' => 'CAPTCHA (page cache)', 'description' => 'Tests CAPTCHA with page cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 2'); } } /** * Tests CAPTCHA with page and form cache. */ class MollomCaptchaPageFormCacheTestCase extends MollomCaptchaTestCase { public static function getInfo() { return array( 'name' => 'CAPTCHA (page + form cache)', 'description' => 'Tests CAPTCHA with page and form cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); variable_set('mollom_test.form.cache', TRUE); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 2'); } } /** * Tests CAPTCHA as authenticated user. */ class MollomCaptchaAuthenticatedTestCase extends MollomCaptchaTestCase { public static function getInfo() { return array( 'name' => 'CAPTCHA (authenticated)', 'description' => 'Tests CAPTCHA as authenticated user.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->web_user = $this->drupalCreateUser(array()); $this->drupalLogin($this->web_user); } } /** * Tests CAPTCHA as authenticated user with enabled page and form cache. */ class MollomCaptchaAuthenticatedPageFormCacheTestCase extends MollomCaptchaTestCase { public static function getInfo() { return array( 'name' => 'CAPTCHA (authenticated + page + form cache)', 'description' => 'Tests CAPTCHA as authenticated user with enabled page and form cache.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->enablePageCache(); variable_set('mollom_test.form.cache', TRUE); // Prime the page + form cache. $this->drupalGet('mollom-test/form'); $this->assertText('Views: 1'); $this->drupalGet('mollom-test/form'); $this->assertText('Views: 2'); $this->web_user = $this->drupalCreateUser(array()); $this->drupalLogin($this->web_user); } } /** * Tests report to Mollom functionality. */ class MollomReportTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Reporting functionality', 'description' => 'Verify that session data is properly stored and content can be reported to Mollom.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('comment')); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); variable_set('comment_preview_article', DRUPAL_OPTIONAL); $this->web_user = $this->drupalCreateUser(array('create article content', 'access comments', 'post comments', 'skip comment approval')); } /** * Tests reporting comments. */ function testReportComment() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form'); $this->drupalLogout(); $this->node = $this->drupalCreateNode(array('type' => 'article')); // Post a comment. $this->drupalLogin($this->web_user); $edit = array( 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save')); $this->comment = db_query('SELECT * FROM {comment} WHERE subject = :comment AND nid = :nid', array(':comment' => $edit['comment_body[und][0][value]'], ':nid' => $this->node->nid))->fetchObject(); $this->assertTrue($this->comment, t('Comment was found in the database.')); $this->assertMollomData('comment', $this->comment->cid); // Log in comment administrator and verify that we can report to Mollom. $this->drupalLogin($this->admin_user); $this->drupalGet('node/' . $this->node->nid); $this->assertText($edit['comment_body[und][0][value]'], t('Comment found.')); $this->clickLink('delete'); $edit = array( 'mollom[feedback]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Delete')); $this->assertText(t('The comment and all its replies have been deleted.')); $this->assertText(t('The content was successfully reported as inappropriate.')); // Verify that the comment and Mollom session data has been deleted. $this->assertFalse(comment_load($this->comment->cid), t('Comment was deleted.')); $this->assertNoMollomData('comment', $this->comment->cid); } /** * Tests mass-reporting comments. */ function testMassReportComments() { $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form'); $this->drupalLogout(); $this->node = $this->drupalCreateNode(array('type' => 'article')); variable_set('comment_preview_article', DRUPAL_OPTIONAL); // Post 3 comments. $this->drupalLogin($this->web_user); $this->comments = array(); foreach (range(1, 3) as $num) { $edit = array( 'subject' => $this->randomName(), 'comment_body[und][0][value]' => 'ham', ); $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save')); $this->comments[$num] = db_query('SELECT * FROM {comment} WHERE subject = :subject AND nid = :nid', array(':subject' => $edit['subject'], ':nid' => $this->node->nid))->fetchObject(); $this->assertTrue($this->comments[$num], t('Comment was found in the database.')); $this->assertMollomData('comment', $this->comments[$num]->cid); } // Log in comment administrator and verify that we can mass-report all // comments to Mollom. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/content/comment'); $edit = array( 'operation' => 'delete', ); foreach ($this->comments as $comment) { $this->assertText($comment->subject, t('Comment found.')); $edit["comments[{$comment->cid}]"] = TRUE; } $this->drupalPost(NULL, $edit, t('Update')); foreach ($this->comments as $comment) { $this->assertText($comment->subject, t('Comment found.')); } $edit = array( 'mollom[feedback]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Delete comments')); $this->assertText(t('Deleted @count comments.', array('@count' => count($this->comments)))); $this->assertText(t('The posts were successfully reported as inappropriate.')); // Verify that the comments and Mollom session data has been deleted. foreach ($this->comments as $comment) { $this->assertFalse(comment_load($comment->cid), t('Comment was deleted.')); $this->assertNoMollomData('comment', $comment->cid); } } } /** * Tests moderating users. */ class MollomModerateUserTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Moderate users', 'description' => 'Tests moderating user accounts.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(); $this->drupalLogin($this->admin_user); $this->setProtectionUI('user_register_form', MOLLOM_MODE_CAPTCHA); $this->drupalLogout(); // Allow visitors to register. variable_set('user_register', USER_REGISTER_VISITORS); // Disable e-mail verification. variable_set('user_email_verification', 0); // Set default user account cancellation method. variable_set('user_cancel_method', 'user_cancel_delete'); } /** * Tests moderating users. */ function testModerateUser() { $account = $this->registerUser(); $this->assertMollomData('user', $account->uid); // Log in administrator and verify that we can report to Mollom. $this->drupalLogin($this->admin_user); $edit = array( 'user_cancel_method' => 'user_cancel_delete', 'mollom[feedback]' => 'spam', ); $this->drupalPost('user/' . $account->uid . '/cancel', $edit, t('Cancel account')); // @todo errrrr, "content"? ;) $this->assertText(t('The content was successfully reported as inappropriate.')); // Verify that Mollom session data has been deleted. $this->assertNoMollomData('user', $account->uid); } /** * Tests cancelling own user account. */ function testCancelOwnAccount() { // Allow users to cancel their own accounts. user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('cancel account')); $account = $this->registerUser(); $this->assertMollomData('user', $account->uid); // Verify that no feedback options appear on the account cancellation form. $this->drupalGet('user/' . $account->uid . '/cancel'); $this->assertNoText(t('Report as…')); // Cancel own account. $this->drupalPost(NULL, array(), t('Cancel account')); $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.')); $this->assertNoText(t('The content was successfully reported as inappropriate.')); // Confirm account cancellation request. $this->drupalGet($this->getCancelUrl()); // Verify that Mollom session data has been deleted. $this->assertNoMollomData('user', $account->uid); } /** * Registers a new user through the UI. * * @return int * The user ID of the new user account. */ function registerUser() { $password = $this->randomName(); $edit = array( 'name' => $this->randomName(), 'pass[pass1]' => $password, 'pass[pass2]' => $password, 'mail' => 'spam@example.com', ); $this->postCorrectCaptcha('user/register', $edit, t('Create new account')); // Determine new uid. $uid = db_query('SELECT uid FROM {users} WHERE name = :name', array(':name' => $edit['name']))->fetchField(); $account = (object) array( 'uid' => $uid, 'name' => $edit['name'], 'pass_raw' => $password, 'mail' => $edit['mail'], ); // If the user is logged in directly after registering, update the logged in // user on DrupalWebTestCase, so drupalLogin() continues to work. if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS) { $this->loggedInUser = $account; } return $account; } /** * Parses the user account cancellation URL out of the last sent mail. */ function getCancelUrl() { $mails = $this->drupalGetMails(); $mail = end($mails); preg_match('@[^\s]+user/\d+/cancel/confirm/[^\s]+@', $mail['body'], $matches); return $matches[0]; } } /** * Tests flag as inappropriate feature. */ class MollomFlagAsInappropriateTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Flag as Inappropriate', 'description' => 'Tests flag as inappropriate feature.', 'group' => 'Mollom', ); } function setUp() { parent::setUp(array('comment', 'mollom')); $this->setKeys(); $this->assertValidKeys(); $this->admin_user = $this->drupalCreateUser(array( 'administer mollom', 'administer comments', 'access comments', )); $this->web_user = $this->drupalCreateUser(array( 'access comments', 'post comments', 'skip comment approval', )); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); variable_set('comment_preview_article', DRUPAL_OPTIONAL); $this->drupalLogin($this->admin_user); $this->setProtectionUI('comment_node_article_form'); $this->node = $this->drupalCreateNode(array('type' => 'article')); } /** * Test access to flag as inappropriate link based on permissions. */ function testFlagAsInappropriate() { user_role_revoke_permissions(DRUPAL_AUTHENTICATED_RID, array('report to mollom')); // Create a comment. $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $this->node->nid); $this->drupalPost(NULL, array('comment_body[und][0][value]' => 'ham'), t('Save')); // Verify that the comment display has no report link. $this->assertNoLink(t('Report')); // Add permissions for flag as inappropriate and verify that report link shows. user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('report to mollom')); // Verify that the report link is now available. $this->drupalGet('node/' . $this->node->nid); $this->assertLink(t('report')); $report_link = $this->getLink(t('report')); if ($report_link) { $parsed = drupal_parse_url($report_link); $parts = explode('/', $parsed['path']); $source = array_pop($parts); $id = array_pop($parts); $entity = array_pop($parts); } // Follow the link to report the content. $reasons_shown = array('spam', 'profanity', 'unwanted'); $this->clickLink(t('report')); $this->assertText(t('Why are you reporting this content?')); foreach($reasons_shown as $reason) { $this->assertField('reason', $reason); } $this->assertNoField('feedback_type'); $reason = $reasons_shown[array_rand($reasons_shown)]; // Verify that the flag count was updated locally. $original = $this->getFlagCount($entity, $id, $reason); $this->drupalPost(NULL, array('reason' => $reason), t('Submit report')); $this->assertText(t('Thank you for your feedback.')); $this->assertFlagCount($entity, $id, $reason, $original+1); } /** * Return the number of flags for a given entity of a specific type. * * @param $entity * The type of entity to check. * @param $id * The id of the entity to check. * @param $reason * The type of reason to retrieve, spam, profanity, unwanted, quality. */ private function getFlagCount($entity, $id, $reason) { $data = $this->assertMollomData($entity, $id); $prop = "flags_$reason"; if (isset($data->$prop)) { return $data->$prop; } else { return -1; } } /** * Test that the number of flags of a particular reason on an entity match * an expected value. * * @param $entity * The type of entity to check. * @param $id * The id of the entity to check. * @param $reason * The type of reason to retrieve, spam, profanity, unwanted, quality. * @param $expected * An integer with the expected number of flags. * @param $message * An optional string with the message to be used in the assertion. */ protected function assertFlagCount($entity, $id, $reason, $expected, $message = '') { $actual = $this->getFlagCount($entity, $id, $reason); if (!$message) { $message = t('Entity @entity @id has @actual flags for @reason, expected @expected.', array( '@entity' => $entity, '@id' => $id, '@actual' => $actual, '@reason' => $reason, '@expected' => $expected, )); } $this->assertEqual($actual, $expected, $message); } }