resetSessionID(); $this->messages = array(); // If not explicitly disabled by a test, setup with Mollom and default admin // user. if (empty($this->disableDefaultSetup)) { // Call parent::setUp() allowing Mollom test cases to pass further modules. $modules = func_get_args(); $modules[] = 'mollom'; $modules[] = 'dblog'; call_user_func_array(array($this, 'parent::setUp'), $modules); $this->admin_user = $this->drupalCreateUser(array( 'administer mollom', 'access administration pages', 'administer content types', 'administer comments', 'administer permissions', )); } else { $modules = func_get_args(); $modules[] = 'dblog'; call_user_func_array(array($this, 'parent::setUp'), $modules); } // If not explicitly disabled by a test, setup and validate testing keys. if (empty($this->disableDefaultSetup)) { $this->setKeys(); $this->assertValidKeys(); } } function tearDown() { // 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 fails. * * @param $no_fail_expected * (optional) Boolean whether a failing watchdog message is expected. * Defaults to TRUE (no fail expected). If FALSE is passed, the logic for * assertion mesages is flipped. * * @todo Add this to D7 core. */ protected function assertMollomWatchdogMessages($no_fail_expected = TRUE) { module_load_include('inc', 'dblog', 'dblog.admin'); $this->messages = array(); $result = db_query("SELECT * FROM {watchdog} WHERE type = 'mollom' ORDER BY timestamp ASC"); while ($row = db_fetch_object($result)) { if ($no_fail_expected ? $row->severity >= WATCHDOG_NOTICE : $row->severity < WATCHDOG_NOTICE) { $this->pass(_dblog_format_message($row), t('Watchdog')); } else { $this->fail(_dblog_format_message($row), t('Watchdog')); } $this->messages[$row->wid] = $row; } // Delete processed watchdog messages. if (!empty($this->messages)) { $seen_ids = array_keys($this->messages); db_query("DELETE FROM {watchdog} WHERE wid IN (" . db_placeholders($seen_ids) . ")", $seen_ids); } } /** * 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 $session_id * A Mollom session_id of the last request, as contained in the XML-RPC * response. */ protected function assertSessionID($session_id) { // Check whether watchdog messages indicate a refresh or redirect. foreach ($this->messages as $message) { if ($message->message == 'Refreshed servers: %servers' || $message->message == 'Server %server redirected to: %next.') { $this->resetSessionID(); } } if (!isset($this->session_id)) { // Use assertTrue() instead of pass(), to test !empty(). $this->assertTrue($session_id, t('New session_id: %session_id', array('%session_id' => $session_id))); $this->session_id = $session_id; } else { $this->_assertEqual('session_id', $session_id, $this->session_id); } return $this->session_id; } /** * Reset the statically cached Mollom session id. */ protected function resetSessionID() { $this->session_id = NULL; } /** * Assert a Mollom session id in a form. * * This is a wrapper around assertSessionID() 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, assertSessionIDInForm(), 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 assertData(), to * verify that the proper session id was stored in the database. */ protected function assertSessionIDInForm() { // The session id found in the form element value is prefixed with the UNIX // timestamp denoting the time it was generated/output. The form element // #process callback mollom_process_mollom_session_id() uses this timestamp // to additionally validate its age. list($timestamp, $session_id) = explode('-', $this->getFieldValueByName('mollom[session_id]')); return $this->assertSessionID($session_id); } /** * Assign the Mollom API keys to internal variables and reset the server list. * * @param $public * The public Mollom API key. * @param $private * The private Mollom API key. * @param $reseller * A boolean that is TRUE if the keys are for a reseller account, or FALSE * otherwise. */ protected function setKeys($public = MOLLOM_TEST_PUBLIC_KEY, $private = MOLLOM_TEST_PRIVATE_KEY, $reseller = MOLLOM_TEST_RESELLER_KEY) { // Save internal properties. $this->public_key = $public; $this->private_key = $private; $this->is_reseller = $reseller; // Set the module key settings. variable_set('mollom_public_key', $public); variable_set('mollom_private_key', $private); // Delete any previously set Mollom servers to make sure we are using // the default ones. variable_del('mollom_servers'); } /** * Call the mollom.verifyKey function directly and check that the current * keys are valid. */ protected function assertValidKeys() { $status = _mollom_status(TRUE); $this->assertMollomWatchdogMessages(); $this->assertIdentical($status, TRUE, t('Mollom servers can be contacted and testing API keys are valid.')); } /** * Configure Mollom protection for a given form. * * @param $form_id * The form id to configure. * @param $fields * (optional) A list of form elements to enable for text analysis. */ protected function setProtection($form_id, $fields = NULL) { // Determine whether the form is already protected. $exists = db_result(db_query_range("SELECT 1 FROM {mollom_form} WHERE form_id = '%s'", $form_id, 0, 1)); // Add a new form. if (!$exists) { $this->drupalGet('admin/settings/mollom'); $this->clickLink(t('Add form')); $edit = array( 'mollom[form_id]' => $form_id, ); $this->drupalPost(NULL, $edit, t('Next')); } // Edit an existing form. else { $this->drupalGet('admin/settings/mollom/manage/' . $form_id); } $edit = array(); // Explicitly enable the passed fields, if $fields were passed. if (isset($fields)) { foreach ($fields as $field) { $edit['mollom[enabled_fields][' . rawurlencode($field) . ']'] = TRUE; } } $form_info = mollom_get_form_info($form_id); foreach (array_keys($form_info['elements']) as $field) { // Due to SimpleTest's form handling of checkboxes, we need to disable all // remaining checkboxes manually. if (isset($fields)) { if (!isset($edit[$field])) { $edit['mollom[enabled_fields][' . rawurlencode($field) . ']'] = FALSE; } } // If no $fields were passed, enable all elements exposed by the // implementation. else { $edit['mollom[enabled_fields][' . rawurlencode($field) . ']'] = TRUE; } } $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_result(db_query_range("SELECT 1 FROM {mollom_form} WHERE form_id = '%s'", $form_id, 0, 1)); if ($exists) { $this->drupalGet('admin/settings/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 $session_id * (optional) The Mollom session id to assert additionally. */ protected function assertData($entity, $id, $session_id = NULL) { $data = mollom_data_load($entity, $id); $this->assertTrue($data->session, t('Mollom session data for %entity @id exists:
@data
', array('%entity' => $entity, '@id' => $id, '@data' => var_export($data, TRUE)))); if (isset($session_id)) { $this->_assertEqual(t('Stored session id'), $data->session, $session_id); } return $data; } /** * Assert that no Mollom session data exists for a certain entity. */ protected function assertNoData($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() { $this->assertFieldByXPath('//input[@type="text"][@name="mollom[captcha]"]', '', 'CAPTCHA field found.'); } /** * Assert that the CAPTCHA field is not found on the current page. */ protected function assertNoCaptchaField() { $this->assertNoFieldByXPath('//input[@type="text"][@name="mollom[captcha]"]', '', 'CAPTCHA field 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 = '') { $edit['mollom[captcha]'] = 'correct'; $this->drupalPost($url, $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 = '') { $edit['mollom[captcha]'] = 'incorrect'; $before_url = $this->getUrl(); $this->drupalPost($url, $edit, $button); if ($this->getUrl() == $before_url) { $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($url); $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'); $before_url = $this->getUrl(); $this->drupalPost($url, $edit, $button); if ($this->getUrl() == $before_url) { $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); } /** * 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 submitted XML-RPC values from testing server implementation. * * @see mollom_test.module */ protected function getServerRecord() { $storage = variable_get('mollom_test_check_content', array()); $return = array_shift($storage); variable_set('mollom_test_check_content', $storage); return $return; } /** * 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->assertSessionID() */ protected function drupalGet($path, array $options = array(), array $headers = array()) { $output = parent::drupalGet($path, $options, $headers); $options += array('watchdog' => TRUE); $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->assertSessionID() * @see DrupalWebTestCase->drupalPost() */ protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array()) { $output = parent::drupalPost($path, $edit, $submit, $options, $headers); $options += array('watchdog' => TRUE); $this->assertMollomWatchdogMessages($options['watchdog']); return $output; } /** * Helper function for assertEqual(). * * Check to see if two values are equal. And provide a meaningful response. * * @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. */ protected function _assertEqual($name, $first, $second) { $message = strtr("@name: '@first' is equal to '@second'.", array( '@name' => $name, '@first' => $first, '@second' => $second, )); $this->assertEqual($first, $second, $message); } /** * Helper function for assertNotEqual(). * * Check to see if two values are equal. And provide a meaningful response. * * @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. */ protected function _assertNotEqual($name, $first, $second) { $message = strtr("@name: '@first' is not equal to '@second'.", array( '@name' => $name, '@first' => $first, '@second' => $second, )); $this->assertNotEqual($first, $second, $message); } } /** * Tests module installation and global status handling. */ class MollomStatusTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Status handling', 'description' => 'Tests module installation and global status handling.', 'group' => 'Mollom', ); } function setUp() { // Re-initialize stored session_id and watchdog messages. $this->resetSessionID(); $this->messages = array(); $this->disableDefaultSetup = TRUE; parent::setUp('comment'); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', 'administer site configuration', 'administer permissions', )); $this->web_user = $this->drupalCreateUser(array( 'access comments', 'post comments', 'post comments without approval', )); } /** * Tests status handling after installation. */ function testStatusInstallation() { // Ensure there is no requirements error by default. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/reports/status'); $this->clickLink('run cron manually'); // Install the module. $this->drupalPost('admin/build/modules', array('status[mollom]' => TRUE), t('Save configuration')); // Verify that forms can be submitted without valid module configuration. $node = $this->drupalCreateNode(array('type' => 'story', 'promoted' => TRUE)); $this->drupalLogin($this->web_user); $this->drupalGet('comment/reply/' . $node->nid); $edit = array( 'comment' => $this->randomName(), ); $this->drupalPost(NULL, $edit, t('Preview')); $this->drupalPost(NULL, array(), t('Save')); // @todo Replace with status message assertion in D7. $this->assertRaw('

' . $edit['comment'] . '

', t('Comment found.')); // Verify requirements error about missing API keys. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/reports/status'); // @todo Should a user without Mollom administration access see a // requirement error containing a link to the settings page? $this->assertRaw(t('Mollom API keys are not configured yet.', array('@settings-url' => url('admin/settings/mollom/settings'))), t('Requirements error found.')); // Grant access to Mollom settings. $edit = array( DRUPAL_AUTHENTICATED_RID . '[administer mollom]' => TRUE, ); $this->drupalPost('admin/user/permissions', $edit, t('Save permissions')); // Configure invalid keys. $edit = array( 'mollom_public_key' => 'foo', 'mollom_private_key' => 'bar', ); $this->drupalPost('admin/settings/mollom/settings', $edit, t('Save configuration'), array('watchdog' => FALSE)); $this->assertText(t('The configuration options have been saved.')); $this->assertNoText($this->fallback_message, t('Fallback message not found.')); $this->assertNoText(t('We tried to contact the Mollom servers but we encountered a network error. Please make sure that your web server can make outgoing HTTP requests.'), t('Network error message not found.')); $this->assertRaw(t('We contacted the Mollom servers to verify your keys: your keys do not exist or are no longer valid. Please visit the Manage sites page on the Mollom website again: @mollom-user.', array('@mollom-user' => 'http://mollom.com/user')), t('Invalid keys error message found.')); // Verify requirements error about invalid API keys. $this->drupalGet('admin/reports/status', array('watchdog' => FALSE)); $this->assertRaw(t('The configured Mollom API keys are invalid.', array('@settings-url' => url('admin/settings/mollom/settings'))), t('Requirements error found.')); // Replace server list with unreachable servers. variable_set('mollom_servers', array('http://fake-host')); // Verify requirements error about network error. // Go directly to the status report, since the server list will be reset. $this->drupalGet('admin/reports/status', array('watchdog' => FALSE)); $this->assertText(t('The Mollom servers could not be contacted. Please make sure that your web server can make outgoing HTTP requests.'), t('Requirements error found.')); $this->assertNoText($this->fallback_message, t('Fallback message not found.')); } } /** * Tests low-level XML-RPC communication with Mollom servers. */ class MollomResponseTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Server responses', 'description' => 'Tests that Mollom server responses match expectations.', 'group' => 'Mollom', ); } /** * Tests mollom.checkContent(). */ function testCheckContent() { $data = array( 'author_name' => $this->admin_user->name, 'author_mail' => $this->admin_user->mail, 'author_id' => $this->admin_user->uid, 'author_ip' => ip_address(), ); // Ensure proper response for 'ham' submissions. $data['post_body'] = 'ham'; $result = mollom('mollom.checkContent', $data); $this->assertMollomWatchdogMessages(); $this->_assertEqual('spam', $result['spam'], MOLLOM_ANALYSIS_HAM); $this->_assertEqual('quality', $result['quality'], 1); $session_id = $this->assertSessionID($result['session_id']); // Ensure proper response for 'spam' submissions, re-using session_id. $data['post_body'] = 'spam'; $data['session_id'] = $session_id; $result = mollom('mollom.checkContent', $data); $this->assertMollomWatchdogMessages(); $this->_assertEqual('spam', $result['spam'], MOLLOM_ANALYSIS_SPAM); $this->_assertEqual('quality', $result['quality'], 0); $session_id = $this->assertSessionID($result['session_id']); // Ensure proper response for 'unsure' submissions, re-using session_id. $data['post_body'] = 'unsure'; $data['session_id'] = $session_id; $result = mollom('mollom.checkContent', $data); $this->assertMollomWatchdogMessages(); $this->_assertEqual('spam', $result['spam'], MOLLOM_ANALYSIS_UNSURE); $this->_assertEqual('quality', $result['quality'], 0.5); $session_id = $this->assertSessionID($result['session_id']); } /** * Tests mollom.getImageCaptcha(). * * @todo Add further getImageCaptcha() response assertions. */ function testGetImageCaptcha() { // Ensure we get no SSL URL by default. $data = array( 'author_ip' => ip_address(), ); $result = mollom('mollom.getImageCaptcha', $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 = array( 'author_ip' => ip_address(), 'ssl' => TRUE, ); $result = mollom('mollom.getImageCaptcha', $data); $this->assertMollomWatchdogMessages(); $this->assertTrue(strpos($result['url'], 'https://') === 0, t('CAPTCHA URL uses HTTPS protocol.')); } } class MollomAccessTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Mollom 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/settings/mollom/settings'); // Set up invalid test keys and check that an error message is shown. $edit = array( 'mollom_public_key' => 'invalid-public-key', 'mollom_private_key' => 'invalid-private-key', ); $this->drupalPost(NULL, $edit, t('Save configuration'), array('watchdog' => FALSE)); $this->assertText(t('The configuration options have been saved.')); $this->assertRaw(t('We contacted the Mollom servers to verify your keys: your keys do not exist or are no longer valid. Please visit the Manage sites page on the Mollom website again: @mollom-user.', array('@mollom-user' => 'http://mollom.com/user')), t('Invalid keys error message appears.')); } /** * 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/settings/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/settings/mollom'); $this->assertResponse(403); } /** * Tests 'bypass access' property of registered forms. */ function testBypassAccess() { $node = $this->drupalCreateNode(array('body' => 'node body', 'type' => 'story')); // Create a regular user and post a comment. $this->web_user = $this->drupalCreateUser(array('post comments', 'post comments without approval')); $this->drupalLogin($this->web_user); $edit = array( 'comment' => 'ham', ); $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); $this->drupalPost(NULL, array(), t('Save')); $this->assertText('node body'); $this->assertText($edit['comment']); // 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'); $this->drupalPost(NULL, array('subject' => '', 'comment' => 'spam'), t('Preview')); $this->assertNoText($this->spam_message); $this->drupalPost(NULL, array(), t('Save')); $this->assertNoText($this->spam_message); $this->assertText('node body'); // 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 MollomFallbackTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Fallback behavior', 'description' => 'Check that the module uses the correct fallback mechanism when one or more of the specified Mollom servers are not available.', 'group' => 'Mollom', ); } function setUp() { // Enable testing server implementation. parent::setUp('mollom_test'); } /** * Make sure that "request new password" submissions can be blocked when * the Mollom servers are unreachable. */ function testFallbackMechanismBlock() { // Enable Mollom for the request password form. $this->drupalLogin($this->admin_user); $this->setProtection('user_pass'); // Set the fallback strategy to 'blocking mode'. $this->drupalPost('admin/settings/mollom/settings', array('mollom_fallback' => MOLLOM_FALLBACK_BLOCK), t('Save configuration')); $this->assertText('The configuration options have been saved.'); $this->drupalLogout(); // Configure Mollom to use a non-existent server as that should trigger // the fallback mechanism. variable_set('mollom_servers', array('http://fake-host')); // Check the password request form. // @todo Test mail sending with assertMail() now that it is available. $this->drupalGet('user/password', array('watchdog' => FALSE)); $this->assertNoCaptchaField(); $this->assertText($this->fallback_message); } /** * Make sure that "request new password" submissions can be allowed when * the Mollom servers are unreachable. */ function testFallbackMechanismAccept() { // Enable Mollom for the request password form. $this->drupalLogin($this->admin_user); $this->setProtection('user_pass'); // Set the fallback strategy to 'accept mode'. $this->drupalPost('admin/settings/mollom/settings', array('mollom_fallback' => MOLLOM_FALLBACK_ACCEPT), t('Save configuration')); $this->assertText('The configuration options have been saved.'); $this->drupalLogout(); // Configure Mollom to use a non-existent server as that should trigger // the fallback mechanism. variable_set('mollom_servers', array('http://fake-host')); // Check the password request form. $this->drupalGet('user/password', array('watchdog' => FALSE)); $this->assertNoCaptchaField(); $this->assertNoText($this->fallback_message); } /** * Make sure that spam protection is still active even when some of the * Mollom servers are unavailable. * * @todo Test mail sending with assertMail() now that it is available. */ function testFailoverMechanism() { // 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); // Configure Mollom to use a list of servers that have a number of // unknown servers, but one real server. variable_set('mollom_servers', array( 'http://fake-host-1', 'http://fake-host-2', $GLOBALS['base_url'] . '/xmlrpc.php?version=', 'http://xmlrpc1.mollom.com', // The real server. 'http://fake-host-3', )); // 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.')); } } class MollomServerListRecoveryTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Server list recovery', 'description' => 'Check that the module can recover from an invalid server list.', 'group' => 'Mollom', ); } /** * Make sure the server list is reset when the Mollom servers are unavailable or incorrect. */ function testServerListRecovery() { $list = array( array( 'http://not-a-valid-server-1', 'http://not-a-valid-server-2', ), // The lack of the http://-schema results in different error codes array( 'not-a-valid-server-url-1', 'not-a-valid-server-url-2', ), ); foreach ($list as $servers) { // Call mollom.verifyKey with an invalid server list. The expected behavior // is that the first call fails, but that the second call succeeds because // the server list is automatically reset or recovered by the Mollom module. variable_set('mollom_servers', $servers); $key_is_valid = mollom('mollom.verifyKey'); $this->assertIdentical($key_is_valid, NETWORK_ERROR, t('The Mollom servers could not be contacted.')); $this->assertMollomWatchdogMessages(FALSE); $key_is_valid = mollom('mollom.verifyKey'); $this->assertIdentical($key_is_valid, TRUE, t('The Mollom servers could be contacted.')); $this->assertMollomWatchdogMessages(); } } } class MollomLanguageDetectionTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Language detection', 'description' => 'Tests language detection functionality.', 'group' => 'Mollom', ); } /** * Test the language detection functionality at the API level without using a web interface. */ function testLanguageDetectionAPI() { // Note that Mollom supports more languages than those tested. $strings = array( 'en' => "Hi, this is a test of the language detection code to see if it works well.", 'nl' => "Hallo, dit is een test van de taaldetectiecode om te controleren of het werkt.", 'fr' => "Bonjour, ceci est un test du detecteur langue automatique pour voir ci ça marche bien.", 'de' => "Bedecke deinen Himmel, Zeus, Mit Wolkendunst Und übe, dem Knaben gleich, der Disteln köpft, An Eichen dich und Bergeshöhn.", 'ko' => "'엄마야 누나야 강변살자. 뜰에는 반짝이는 금모래 빛. 뒷문 밖에는 갈잎의 노래", 'ru' => "Холуй трясется. Раб хохочет. Палач свою секиру точит. Тиран кромсает каплуна. Сверкает зимняя луна.", 'hu' => "Földszintiek mászófámról pillantva fejjel lefelé ti lógtok bele nézőim az űrbe ki tudja így kölcsönös kíváncsiak a helyes felelet kié", 'el' => "Σαν να 'χουνε την όψη της αιώνες οργωμένη. Κάτι άναρχο κι ατέλειωτο στο πρόσωπό της μένει.", 'ja' => "吹くからに秋の草木のしをるれば", 'th' => "ทั่วประเทศ ประมาณ ๔๐,๐๐๐ แห่ง ชาวไทยนับตั้งแต่ครั้งอดีตมีวิถี ชีวิตผูกพันกับพุทธศาสนาอย่างใกล้ชิด แสดงออกมาเป็น ขนบธรรมเนียมประเพณี", 'zh' => "螽斯羽,诜诜兮。宜尔子孙,振振兮", ); foreach ($strings as $language => $text) { $result = mollom('mollom.detectLanguage', array('text' => $text)); $this->assertEqual($result[0]['language'], $language, t('A language code was specified and they match.')); $this->assertTrue($result[0]['confidence'] > 0, t('A confidence value was specified and it is greater than 0.')); } } } /** * 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 { public static function getInfo() { return array( 'name' => 'Blacklisting', 'description' => 'Tests URL and text blacklist functionality.', 'group' => 'Mollom', ); } /** * Test the URL blacklist functionality at the API level without using a web interface. */ function testUrlBlacklistAPI() { // Blacklist a URL. $result = mollom('mollom.addBlacklistURL', array('url' => 'http://unicorn.com')); $this->assertTrue($result, t('The URL was blacklisted.')); // Validate that there is one entry. $blacklist = mollom('mollom.listBlacklistURL'); $this->assertEqual(count($blacklist), 1, t('The server-side blacklist has one entry.')); // Check whether posts containing the blacklisted URL are properly blocked. $result = mollom('mollom.checkContent', array( 'post_body' => "When the exact URL is present, the post should get blocked: http://unicorn.com", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); $result = mollom('mollom.checkContent', array( 'post_body' => "When the URL is expanded in the back, the post should get blocked: http://unicorn.com/oh-my", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); $result = mollom('mollom.checkContent', array( 'post_body' => "When the URL is expanded in the front, the post should get blocked: http://www.unicorn.com", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); $result = mollom('mollom.checkContent', array( 'post_body' => "When the URL has a different schema, the post should get blocked: ftp://www.unicorn.com", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); // Although we only added one element, we're using a loop because we want to // reset the blacklist in case it got somehow polluted. foreach ($blacklist as $entry) { $result = mollom('mollom.removeBlacklistURL', array('url' => $entry['url'])); $this->assertTrue($result, t('The blacklisted URL was removed.')); } // Retrieve the blacklist again, and make sure it is empty. $blacklist = mollom('mollom.listBlacklistURL'); $this->assertEqual(count($blacklist), 0, t('The server-side blacklist is empty.')); } /** * Test the text blacklist functionality at the API level without using a web interface. */ function testTextBlacklistAPI() { // Blacklist a word: $result = mollom('mollom.addBlacklistText', array( 'text' => 'unicorn', 'match' => 'contains', 'reason' => 'spam', )); $this->assertTrue($result, t('The text was blacklisted.')); // Validate that there is one entry. $blacklist = mollom('mollom.listBlacklistText'); $this->assertEqual(count($blacklist), 1, t('The server-side blacklist has one entry.')); // Check whether posts containing the blacklisted URL are properly blocked. $result = mollom('mollom.checkContent', array( 'post_body' => "When the text is present, the post should get blocked: unicorn", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); $result = mollom('mollom.checkContent', array( 'post_body' => "When match is 'contains', the word can be surrounded by other text: abcunicorndef", )); $this->assertEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was blocked.')); // To update the work (i.e. to change the 'match' property), we simply // overwrite it. $result = mollom('mollom.addBlacklistText', array( 'text' => 'unicorn', 'match' => 'exact', 'reason' => 'spam', )); $this->assertTrue($result, t('The text was blacklisted.')); // Validate that there is one entry. $blacklist = mollom('mollom.listBlacklistText'); $this->assertEqual(count($blacklist), 1, t('The server-side blacklist has one entry.')); $result = mollom('mollom.checkContent', array( 'post_body' => "When match is 'exact', it has to be exact: abcunicorndef", )); $this->assertNotEqual($result['spam'], MOLLOM_ANALYSIS_SPAM, t('The message was not blocked.')); // Although we added only one element, we're using a loop because we want to // reset the blacklist in case it got somehow polluted. foreach ($blacklist as $entry) { $result = mollom('mollom.removeBlacklistText', array('text' => $entry['text'])); $this->assertTrue($result, t('The blacklisted text was removed.')); } // Retrieve the blacklist again, and make sure it is empty. $blacklist = mollom('mollom.listBlacklistText'); $this->assertEqual(count($blacklist), 0, t('The server-side blacklist is empty.')); } /** * 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->drupalLogin($this->admin_user); $this->drupalGet('admin/settings/mollom/blacklist'); // Add a word to the text blacklist. $edit = array( 'entry[match]' => 'contains', 'entry[text]' => 'spam word', 'entry[reason]' => 'spam', ); $this->drupalPost(NULL, $edit, t('Add')); $this->assertText(t('The text has been added to the blacklist.')); $this->assertText('spam word'); // Remove the word from the text blacklist. $this->clickLink(t('delete')); $this->drupalPost(NULL, array(), t('Delete')); $this->assertEqual($this->getUrl(), url('admin/settings/mollom/blacklist', array('absolute' => TRUE)), t('Correct page redirection.')); $this->assertNoText('spam word', 'Text blacklist removed.'); // Add an URL to the text blacklist. $edit = array( 'entry[url]' => 'spam.com', ); $this->drupalPost(NULL, $edit, t('Add')); $this->assertText(t('The URL has been added to the blacklist.')); $this->assertText('http://spam.com'); // Remove the URL from the text blacklist. $this->clickLink(t('delete')); $this->drupalPost(NULL, array(), t('Delete')); $this->assertEqual($this->getUrl(), url('admin/settings/mollom/blacklist', array('absolute' => TRUE)), t('Correct page redirection.')); $this->assertText(t('The item has been removed from the blacklist.')); $this->assertNoText('http://spam.com', 'URL blacklist removed.'); } } /** * Tests Mollom form configuration functionality. */ class MollomFormConfigurationTestCase extends MollomWebTestCase { 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('mollom_test'); // Re-route Mollom communication to this testing site. variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version=')); $this->drupalLogin($this->admin_user); } /** * Tests configuration of form fields for textual analysis. */ function testFormFieldsConfiguration() { // Protect Mollom test form. $this->drupalGet('admin/settings/mollom/add'); $edit = array( 'mollom[form_id]' => 'mollom_test_form', ); $this->drupalPost(NULL, $edit, t('Next')); $this->assertText('Mollom test form'); $edit = array( '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/settings/mollom/manage/mollom_test_form'); foreach ($edit as $name => $value) { // assertFieldByName() does not work for checkboxes. // @see assertFieldChecked() $elements = $this->xpath('//input[@name="' . $name . '"]'); if ($value) { $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), t('Field @name is checked', array('@name' => $name))); } else { $this->assertFalse(isset($elements[0]) && !empty($elements[0]['checked']), t('Field @name is checked', array('@name' => $name))); } } // 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/settings/mollom/manage/mollom_test_form'); $form_info = mollom_get_form_info('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); } /** * Tests default configuration, protecting, and unprotecting forms. */ function testFormAdministration() { // @todo Figure out why we need to reset the static cache here. mollom_get_form_info(NULL, TRUE); $form_info = mollom_get_form_info(); // Verify that all registered forms specifying a default mode are // auto-protected after installation. // @todo During Drupal installation, modules are installed in an arbitrary // order, so module_list() only returns modules that were installed before // this module. Registered forms for Contact and Node modules are not // contained. Uncomment these assertions in D7. /* $this->drupalGet('admin/settings/mollom'); foreach ($form_info as $form_id => $info) { if (!empty($info['mode'])) { $this->assertText($info['title']); } } */ // Unprotect user_register form. $this->drupalPost('admin/settings/mollom/unprotect/user_register', array(), t('Confirm')); $this->assertNoText($form_info['user_register']['title']); $this->assertFalse(mollom_form_load('user_register'), t('Form configuration no longer exists.')); // Re-protect user_register form. $this->drupalGet('admin/settings/mollom/add'); $this->assertNoText(t('All available forms are protected already.')); $edit = array( 'mollom[form_id]' => 'user_register', ); $this->drupalPost(NULL, $edit, t('Next')); $this->assertText($form_info['user_register']['title']); $this->assertText(t('No fields are available.')); $this->drupalPost(NULL, array(), t('Save')); // Verify that user_register form was protected. $this->assertText(t('The form protection has been added.')); $this->assertText($form_info['user_register']['title']); $this->assertTrue(mollom_form_load('user_register'), t('Form configuration exists.')); // 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/settings/mollom/add', $edit, t('Next')); $this->assertText($info['title']); if (isset($info['mode'])) { // Verify that CAPTCHA-only forms contain no configurable fields. if ($info['mode'] == MOLLOM_MODE_CAPTCHA) { $this->assertText(t('No fields are available.')); } // Verify that forms specifying textual analysis as default mode have // all possible elements preselected. elseif ($info['mode'] == MOLLOM_MODE_ANALYSIS && !empty($info['elements'])) { foreach ($info['elements'] as $field => $label) { $this->assertFieldByName("mollom[enabled_fields][$field]", TRUE); } } } $this->drupalPost(NULL, array(), 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/settings/mollom/add'); $this->assertText(t('All available forms are protected already.')); $this->assertText(t('Operations')); } } class MollomUserFormsTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'User registration and password protection', 'description' => 'Check that the user registration and password request forms can be protected.', 'group' => 'Mollom', ); } /** * Make sure that the request password form is protected correctly. * * @todo Test mail sending with assertMail() now that it is available. */ function testProtectRequestPassword() { // We first enable Mollom for the request password form. $this->drupalLogin($this->admin_user); $this->setProtection('user_pass'); $this->drupalLogout(); // Create a new user. $this->web_user = $this->drupalCreateUser(); $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')); // 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.')); } /** * Make sure that the user registration form is protected correctly. */ function testProtectRegisterUser() { // We first enable Mollom for the user registration form. $this->drupalLogin($this->admin_user); $this->setProtection('user_register'); $this->drupalLogout(); // 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(array('name' => $name)), t('The user who attempted to register cannot be found in the database when the CAPTCHA is invalid.')); // 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 password and further instructions have been sent to your e-mail address.')); $this->assertTrue(user_load(array('name' => $name)), t('The user who attempted to register appears in the database when the CAPTCHA is valid.')); } } class MollomCommentFormTestCase extends MollomWebTestCase { private $node; public static function getInfo() { return array( 'name' => 'Comment form protection', 'description' => 'Check that the comment submission form can be protected.', 'group' => 'Mollom', ); } function setUp() { parent::setUp('comment'); $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'post comments without approval', 'create story content')); $this->node = $this->drupalCreateNode(array('type' => 'story', 'uid' => $this->web_user->uid)); variable_set('comment_preview_story', COMMENT_PREVIEW_OPTIONAL); } /** * Make sure that the comment submission form can be unprotected. */ function testUnprotectedCommentForm() { // Disable Mollom for comments. $this->drupalLogin($this->admin_user); $this->delProtection('comment_form'); $this->drupalLogout(); // 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' => '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->setProtection('comment_form', array()); $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->assertSessionIDInForm(); $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->assertSessionIDInForm(); $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.')); $session_id = $this->assertSessionIDInForm(); $this->assertNoPrivacyLink(); // Finally, we should be able to submit a comment. $this->drupalPost(NULL, array('comment' => 'spam'), t('Save')); $this->assertRaw('

spam

', t('Spam comment could be posted with correct CAPTCHA.')); $cid = db_result(db_query("SELECT cid FROM {comments} WHERE comment = '%s' ORDER BY timestamp DESC", array('spam'))); $this->assertData('comment', $cid, $session_id); } /** * 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->setProtection('comment_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. $this->drupalPost(NULL, array('comment' => 'unsure'), t('Save')); $this->assertCaptchaField(); $session_id = $this->assertSessionIDInForm(); $this->assertPrivacyLink(); // Try to submit the form by using an invalid CAPTCHA. At this point, // the submission should be rejected and a new CAPTCHA generated (even // if the text of the comment is changed to ham). $this->postIncorrectCaptcha(NULL, array('comment' => 'ham'), t('Save')); $session_id = $this->assertSessionIDInForm(); $this->assertPrivacyLink(); // Now try using a valid CAPTCHA. The CAPTCHA form should no longer // be present. $this->postCorrectCaptcha(NULL, array(), t('Save')); $this->assertRaw('

ham

', t('A comment that is known to be ham appears on the screen after it is submitted.')); $cid = db_result(db_query("SELECT cid FROM {comments} WHERE comment = '%s' ORDER BY timestamp DESC", array('ham'))); $this->assertData('comment', $cid, $session_id); // Try to save a new 'spam' comment; it should be rejected, with no CAPTCHA // appearing on the page. $this->resetSessionID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); $this->assertSpamSubmit(NULL, array('comment'), array(), t('Save')); $session_id = $this->assertSessionIDInForm(); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Try to save again; it should be rejected, with no CAPTCHA. $this->assertSpamSubmit(NULL, array('comment'), array(), t('Save')); $session_id = $this->assertSessionIDInForm(); $this->assertCommentCount($this->node->nid, $original_number_of_comments); $this->assertPrivacyLink(); // Save a new 'ham' comment. $this->resetSessionID(); $this->drupalGet('comment/reply/' . $this->node->nid); $this->assertPrivacyLink(); $original_number_of_comments = $this->getCommentCount($this->node->nid); $this->assertHamSubmit(NULL, array('comment'), 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_result(db_query("SELECT cid FROM {comments} WHERE comment = '%s' ORDER BY timestamp DESC", array('ham'))); $this->assertData('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_result(db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = %d', $nid)); } /** * 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); } } class MollomContactFormTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Contact form protection', 'description' => 'Check that the contact form can be protected.', 'group' => 'Mollom', ); } function setUp() { parent::setUp('contact'); $this->web_user = $this->drupalCreateUser(array('access site-wide contact form', 'access user profiles')); } /** * Make sure that the user contact form is protected correctly. * * @todo Test mail sending with assertMail() now that it is available. */ function testProtectContactUserForm() { // Enable Mollom for the contact form. $this->drupalLogin($this->admin_user); $this->setProtection('contact_mail_user'); $this->drupalLogout(); $this->drupalLogin($this->web_user); $url = 'user/' . $this->admin_user->uid . '/contact'; $button = t('Send e-mail'); $success = t('The 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); // Submit an 'unsure' message. This should be accepted only after the // CAPTCHA has been solved. $this->assertUnsureSubmit($url, array('subject', 'message'), array(), $button, $success); } /** * Make sure that the site-wide contact form is protected correctly. * * @todo Test mail sending with assertMail() now that it is available. */ function testProtectContactSiteForm() { // Enable Mollom for the contact form. $this->drupalLogin($this->admin_user); $this->setProtection('contact_mail_page'); $this->drupalLogout(); // Add some fields to the contact form so that it is active. $this->drupalLogin($this->web_user); db_query("INSERT INTO {contact} (category, recipients, reply) VALUES ('%s', '%s', '%s')", 'test category', $this->web_user->mail, 'test auto-reply'); $url = 'contact'; $button = t('Send e-mail'); $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); // Submit an 'unsure' message. This should be accepted only after the // CAPTCHA has been solved. $this->assertUnsureSubmit($url, array('subject', 'message'), array(), $button, $success); } } class MollomResellerTestCase extends MollomWebTestCase { public static function getInfo() { return array( 'name' => 'Key provisioning for Mollom reseller', 'description' => 'Check that the reseller APIs are working properly.', 'group' => 'Mollom', ); } /** * Make sure that resellers can create a new site. */ function testKeyManagement() { if (!$this->is_reseller) { // If the current test keys are not reseller keys, skip this test. return; } // Create 3 test sites: for ($i = 1; $i <= 3; $i++) { $keys[] = mollom('mollom.createSite', array( 'url' => 'http://example.com/site-'. $i, 'mail' => 'mail@example.com', 'status' => 0, 'testing' => 1, )); $this->assertEqual(xmlrpc_errno(), FALSE, t('A new site has been registered with Mollom.')); } $sites = mollom('mollom.listSites'); foreach ($sites as $site) { // Retrieve the information from the site: $details = mollom('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: $details['mail'] = 'root@example.com'; mollom('mollom.updateSite', array('client_key' => $site) + $details); $this->assertEqual(xmlrpc_errno(), FALSE, t('The information has been updated on the Mollom server.')); // Retrieve the information from the site and check if it was updated properly: $details = mollom('mollom.getSite', array('client_key' => $site)); $this->assertEqual($details['mail'], 'root@example.com', t('The updated information is correctly retrieved from Mollom.')); // Delete the test site: mollom('mollom.deleteSite', array('client_key' => $site)); $this->assertEqual(xmlrpc_errno(), FALSE, t('The Mollom server deleted a site.')); } else { $this->fail(t('We tried to delete a non-test site.')); } } // 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 { 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() { // Enable testing server implementation. parent::setUp('mollom_test'); // Re-route Mollom communication to this testing site. variable_set('mollom_servers', array($GLOBALS['base_url'] . '/xmlrpc.php?version=')); } /** * Test mollom_form_get_values(). */ function testFormGetValues() { global $user; // Form registry information. $form_info = array( 'elements' => array( 'subject' => 'Subject', 'message' => 'Message', 'parent][child' => 'Some nested element', ), 'mapping' => array( 'post_title' => 'subject', 'author_name' => 'name', 'author_mail' => 'mail', ), ); // Fields configured via Mollom admin UI based on $form_info['elements']. $fields = array( 'subject', 'message', 'parent][child', ); // Verify submitted form values for an anonymous/arbitrary user. $values = array( 'subject' => 'Foo', 'message' => 'Bar', 'parent' => array( 'child' => 'Beer', ), 'name' => 'Drupaler', 'mail' => 'drupaler@example.com', ); $data = mollom_form_get_values($values, $fields, $form_info['mapping']); $this->_assertEqual('post_title', $data['post_title'], $values['subject']); $this->_assertEqual('post_body', $data['post_body'], $values['message'] . "\n" . $values['parent']['child']); $this->_assertEqual('author_name', $data['author_name'], $values['name']); $this->_assertEqual('author_mail', $data['author_mail'], $values['mail']); $this->assertFalse(isset($data['author_url']), t('author_url: Undefined.')); $this->assertFalse(isset($data['author_openid']), t('author_openid: Undefined.')); $this->assertFalse(isset($data['author_id']), t('author_id: Undefined.')); $this->_assertEqual('author_ip', $data['author_ip'], ip_address()); // Verify submitted form values for an registered user. $values = array( 'subject' => 'Foo', 'message' => 'Bar', 'name' => $this->admin_user->name, ); $data = mollom_form_get_values($values, $fields, $form_info['mapping']); $this->_assertEqual('post_title', $data['post_title'], $values['subject']); $this->_assertEqual('post_body', $data['post_body'], $values['message']); $this->_assertEqual('author_name', $data['author_name'], $this->admin_user->name); $this->_assertEqual('author_mail', $data['author_mail'], $this->admin_user->mail); $this->assertFalse(isset($data['author_url']), t('author_url: Undefined.')); // @todo Test this. $this->assertFalse(isset($data['author_openid']), t('author_openid: Undefined.')); $this->_assertEqual('author_id', $data['author_id'], $this->admin_user->uid); $this->_assertEqual('author_ip', $data['author_ip'], ip_address()); } /** * Test submitted post and author information for textual analysis. */ function testAnalysis() { // Verify that comment form is protected. $this->drupalLogin($this->admin_user); $this->drupalGet('admin/settings/mollom'); $this->assertText(t('Comment form')); $this->drupalGet('admin/settings/mollom/manage/comment_form'); // Make comment preview optional. $edit = array( 'comment_preview' => 0, ); $this->drupalPost('admin/content/node-type/story', $edit, t('Save content type')); // Create a node we can comment on. $node = $this->drupalCreateNode(array('type' => 'story', 'promote' => 1)); $this->drupalGet(''); $this->assertText($node->title); // Log in regular user and post a comment. $this->drupalLogout(); $this->web_user = $this->drupalCreateUser(array('post comments without approval')); $this->drupalLogin($this->web_user); $this->drupalGet(''); $this->clickLink(t('Add new comment')); $edit = array( 'subject' => $this->randomString(), 'comment' => 'unsure', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertText($this->unsure_message); $this->PostCorrectCaptcha(NULL, array(), t('Save')); $comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE subject = '%s'", $edit['subject'])); $this->assertTrue($comment, t('Comment exists in database.')); // Verify that submitted data equals post data. $data = $this->getServerRecord(); $this->_assertEqual('post_title', $data['post_title'], $edit['subject']); $this->_assertEqual('post_body', $data['post_body'], $edit['comment']); $this->_assertEqual('author_name', $data['author_name'], $this->web_user->name); $this->_assertEqual('author_mail', $data['author_mail'], $this->web_user->mail); $this->_assertEqual('author_id', $data['author_id'], $this->web_user->uid); // Allow anonymous users to post comments without approval. $this->drupalLogin($this->admin_user); $edit = array( DRUPAL_ANONYMOUS_RID . '[access comments]' => TRUE, DRUPAL_ANONYMOUS_RID . '[post comments]' => TRUE, DRUPAL_ANONYMOUS_RID . '[post comments without approval]' => TRUE, ); $this->drupalPost('admin/user/permissions', $edit, t('Save permissions')); // Allow anonymous users to post contact information. $edit = array( 'comment_anonymous' => COMMENT_ANONYMOUS_MAY_CONTACT, ); $this->drupalPost('admin/content/node-type/story', $edit, t('Save content type')); // Log out and post a comment as anonymous user. $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://mollom.com', 'subject' => '"' . $this->randomString() . '"', 'comment' => 'unsure', ); $this->drupalPost(NULL, $edit, t('Save')); $this->assertText($this->unsure_message); $this->PostCorrectCaptcha(NULL, array(), t('Save')); $comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE subject = '%s'", $edit['subject'])); $this->assertTrue($comment, t('Comment exists in database.')); // Verify that submitted data equals post data. $data = $this->getServerRecord(); $this->_assertEqual('post_title', $data['post_title'], $edit['subject']); $this->_assertEqual('post_body', $data['post_body'], $edit['comment']); $this->_assertEqual('author_name', $data['author_name'], $edit['name']); $this->_assertEqual('author_mail', $data['author_mail'], $edit['mail']); $this->_assertEqual('author_url', $data['author_url'], $edit['homepage']); $this->assertFalse(isset($data['author_id']), t('author_id: Undefined.')); // Log in admin user and edit comment containing spam. $this->drupalLogin($this->admin_user); $this->drupalGet('comment/edit/' . $comment->cid); // 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 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->setProtection('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->assertData('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.')); // Update the stored entry. $edit['title'] = 'unsure'; $this->drupalPost(NULL, $edit, 'Submit'); $this->assertCaptchaField(); $this->postCorrectCaptcha(NULL, array(), 'Submit', 'Successful form submission.'); $new_data = $this->assertData('mollom_test', $mid); // Verify that only session data was updated. $this->_assertEqual('entity', $data->entity, $new_data->entity); $this->_assertEqual('id', $data->did, $new_data->did); $this->_assertNotEqual('session_id', $data->session, $new_data->session); $this->_assertNotEqual('quality', $data->quality, $new_data->quality); $count = db_result(db_query("SELECT COUNT(1) FROM {mollom}")); $this->assertEqual($count, 1, t('Stored data in {mollom} was updated.')); } } /** * 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('comment'); $this->web_user = $this->drupalCreateUser(array('access comments', 'post comments', 'post comments without approval', 'create story content')); } /** * Tests reporting comments. */ function testReportComment() { $this->node = $this->drupalCreateNode(array('type' => 'story')); variable_set('comment_preview_story', COMMENT_PREVIEW_OPTIONAL); // Post a comment. $this->drupalLogin($this->web_user); $edit = array( 'comment' => 'ham', ); $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save')); $this->comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE comment = '%s' AND nid = %d", array($edit['comment'], $this->node->nid))); $this->assertTrue($this->comment, t('Comment was found in the database.')); $this->assertData('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'], t('Comment found.')); $this->clickLink('report to Mollom'); $edit = array( 'feedback' => 'spam', ); $this->drupalPost(NULL, $edit, t('Delete')); $this->assertText(t('The content was successfully reported as inappropriate.')); $this->assertText(t('The comment has been deleted.')); // Verify that the comment and Mollom session data has been deleted. $this->assertFalse(_comment_load($this->comment->cid), t('Comment was deleted.')); $this->assertNoData('comment', $this->comment->cid); } /** * Tests mass-reporting comments. */ function testMassReportComments() { $this->node = $this->drupalCreateNode(array('type' => 'story')); variable_set('comment_preview_story', COMMENT_PREVIEW_OPTIONAL); // Post 3 comments. $this->drupalLogin($this->web_user); $this->comments = array(); foreach (range(1, 3) as $num) { $edit = array( 'subject' => $this->randomName(), 'comment' => 'ham', ); $this->drupalPost('comment/reply/' . $this->node->nid, $edit, t('Save')); $this->comments[$num] = db_fetch_object(db_query("SELECT * FROM {comments} WHERE subject = '%s' AND nid = %d", array($edit['subject'], $this->node->nid))); $this->assertTrue($this->comments[$num], t('Comment was found in the database.')); $this->assertData('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' => 'mollom-unpublish'); foreach ($this->comments as $comment) { $this->assertText($comment->subject, t('Comment found.')); $edit["comments[{$comment->cid}]"] = TRUE; } $this->drupalPost(NULL, $edit, t('Update')); $this->assertText(t('The selected comments have been reported as inappropriate and are unpublished.')); // Verify that unpublished comments are found in approval queue and // mass-report all comments again to delete them. $this->drupalGet('admin/content/comment/approval'); $edit['operation'] = 'mollom-delete'; foreach ($this->comments as $comment) { $this->assertText($comment->subject, t('Comment found.')); } $this->drupalPost(NULL, $edit, t('Update')); $this->assertText(t('The selected comments have been reported as inappropriate and are deleted.')); // 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->assertNoData('comment', $comment->cid); } } }