Newer
Older

Dries Buytaert
committed
<?php
// $Id$
/**
* @file
* Provides SimpleTests for core session handling functionality.
*/
class SessionTestCase extends DrupalWebTestCase {

Angie Byron
committed
public static function getInfo() {

Dries Buytaert
committed
return array(
'name' => t('Session tests'),
'description' => t('Drupal session handling tests.'),
'group' => t('Session')
);
}
function setUp() {
parent::setUp('session_test');
}

Dries Buytaert
committed
/**

Dries Buytaert
committed
* Tests for drupal_save_session() and drupal_session_regenerate().

Dries Buytaert
committed
*/

Dries Buytaert
committed
function testSessionSaveRegenerate() {

Angie Byron
committed
$this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE (inside of testing framework) when initially called with no arguments.'), t('Session'));

Dries Buytaert
committed
$this->assertFalse(drupal_save_session(FALSE), t('drupal_save_session() correctly returns FALSE when called with FALSE.'), t('Session'));
$this->assertFalse(drupal_save_session(), t('drupal_save_session() correctly returns FALSE when saving has been disabled.'), t('Session'));
$this->assertTrue(drupal_save_session(TRUE), t('drupal_save_session() correctly returns TRUE when called with TRUE.'), t('Session'));
$this->assertTrue(drupal_save_session(), t('drupal_save_session() correctly returns TRUE when saving has been enabled.'), t('Session'));

Dries Buytaert
committed
// Test session hardening code from SA-2008-044.
$user = $this->drupalCreateUser(array('access content'));
// Enable sessions.
$this->sessionReset($user->uid);
// Make sure the session cookie is set as HttpOnly.
$this->drupalLogin($user);

Dries Buytaert
committed
$this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), t('Session cookie is set as HttpOnly.'));

Dries Buytaert
committed
$this->drupalLogout();
// Verify that the session is regenerated if a module calls exit
// in hook_user_login().
user_save($user, array('name' => 'session_test_user'));
$user->name = 'session_test_user';
$this->drupalGet('session-test/id');
$matches = array();
preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
$this->assertTrue(!empty($matches[1]) , t('Found session ID before logging in.'));
$original_session = $matches[1];
// We cannot use $this->drupalLogin($user); because we exit in
// session_test_user_login() which breaks a normal assertion.
$edit = array(
'name' => $user->name,
'pass' => $user->pass_raw
);
$this->drupalPost('user', $edit, t('Log in'));

Dries Buytaert
committed
$this->drupalGet('user');

Dries Buytaert
committed
$pass = $this->assertText($user->name, t('Found name: %name', array('%name' => $user->name)), t('User login'));
$this->_logged_in = $pass;
$this->drupalGet('session-test/id');
$matches = array();
preg_match('/\s*session_id:(.*)\n/', $this->drupalGetContent(), $matches);
$this->assertTrue(!empty($matches[1]) , t('Found session ID after logging in.'));
$this->assertTrue($matches[1] != $original_session, t('Session ID changed after login.'));

Dries Buytaert
committed
}
/**
* Test data persistence via the session_test module callbacks. Also tests

Dries Buytaert
committed
* drupal_session_count() since session data is already generated here.

Dries Buytaert
committed
*/
function testDataPersistence() {
$user = $this->drupalCreateUser(array('access content'));
// Enable sessions.
$this->sessionReset($user->uid);
$this->drupalLogin($user);
$this->session_count_authenticated = $this->session_count++;
$value_1 = $this->randomName();
$this->drupalGet('session-test/set/' . $value_1);
$this->assertText($value_1, t('The session value was stored.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_1, t('Session correctly returned the stored data for an authenticated user.'), t('Session'));

Dries Buytaert
committed
// Attempt to write over val_1. If drupal_save_session(FALSE) is working.

Dries Buytaert
committed
// properly, val_1 will still be set.
$value_2 = $this->randomName();
$this->drupalGet('session-test/no-set/' . $value_2);
$this->assertText($value_2, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
$this->drupalGet('session-test/get');

Dries Buytaert
committed
$this->assertText($value_1, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));

Dries Buytaert
committed
// Switch browser cookie to anonymous user, then back to user 1.
$this->sessionReset();
$this->sessionReset($user->uid);
$this->assertText($value_1, t('Session data persists through browser close.'), t('Session'));
// Logout the user and make sure the stored value no longer persists.
$this->drupalLogout();
$this->sessionReset();
$this->drupalGet('session-test/get');
// Session count should go up since we're accessing anonymously now.
$this->session_count_anonymous = $this->session_count++;
$this->assertNoText($value_1, t("After logout, previous user's session data is not available."), t('Session'));
$value_3 = $this->randomName();
$this->drupalGet('session-test/set/' . $value_3);
$this->assertText($value_3, t('Session data stored for anonymous user.'), t('Session'));
$this->drupalGet('session-test/get');
$this->assertText($value_3, t('Session correctly returned the stored data for an anonymous user.'), t('Session'));
$value_4 = $this->randomName();
$this->drupalGet('session-test/no-set/' . $value_4);
$this->assertText($value_4, t('The session value was correctly passed to session-test/no-set.'), t('Session'));
$this->drupalGet('session-test/get');

Dries Buytaert
committed
$this->assertText($value_3, t('Session data is not saved for drupal_save_session(FALSE).'), t('Session'));

Dries Buytaert
committed
// Logout and get first user back in. Sessions shouldn't persist through
// logout, so the data won't be on the page.
$this->drupalLogin($user);
$this->sessionReset($user->uid);
$this->drupalGet('session-test/get');
$this->assertNoText($value_1, t('Session has persisted for an authenticated user after logging out and then back in.'), t('Session'));
// Logout and create another user.
$user2 = $this->drupalCreateUser(array('access content'));
$this->sessionReset($user2->uid);
$this->drupalLogin($user2);
$this->session_count_authenticated = $this->session_count++;

Dries Buytaert
committed
// Perform drupal_session_count tests here in order to use the session data already generated.

Dries Buytaert
committed
// Test absolute count.

Dries Buytaert
committed
$anonymous = drupal_session_count(0, TRUE);
$authenticated = drupal_session_count(0, FALSE);

Dries Buytaert
committed
$this->assertEqual($anonymous + $authenticated, $this->session_count, t('Correctly counted @count total sessions.', array('@count' => $this->session_count)), t('Session'));
// Test anonymous count.
$this->assertEqual($anonymous, $this->session_count_anonymous, t('Correctly counted @count anonymous sessions.', array('@count' => $anonymous)), t('Session'));
// Test authenticated count.
$this->assertEqual($authenticated, $this->session_count_authenticated, t('Correctly counted @count authenticated sessions.', array('@count' => $authenticated)), t('Session'));
// Should return 0 sessions from 1 second from now.

Dries Buytaert
committed
$this->assertEqual(drupal_session_count(time() + 1), 0, t('Correctly returned 0 sessions newer than the current time.'), t('Session'));

Dries Buytaert
committed
}

Dries Buytaert
committed
/**
* Test that empty anonymous sessions are destroyed.
*/
function testEmptyAnonymousSession() {
// With caching disabled, a session is always started.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);
variable_set('cache', CACHE_NORMAL);
// During this request the session is destroyed in drupal_page_footer(),
// and the session cookie is unset.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);

Dries Buytaert
committed
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.'));

Dries Buytaert
committed
// When PHP deletes a cookie, it sends "Set-Cookie: cookiename=deleted;
// expires=..."
$this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
// Verify that the session cookie was actually deleted.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
// Start a new session by setting a message.
$this->drupalGet('session-test/set-message');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertTrue($this->drupalGetHeader('Set-Cookie'), t('New session was started.'));
// Display the message.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(FALSE);

Dries Buytaert
committed
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.'));

Dries Buytaert
committed
$this->assertText(t('This is a dummy message.'), t('Message was displayed.'));
// During this request the session is destroyed in _drupal_bootstrap(),
// and the session cookie is unset.
$this->drupalGet('');
$this->assertSessionCookie(TRUE);
$this->assertSessionStarted(TRUE);
$this->assertSessionEmpty(TRUE);

Dries Buytaert
committed
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));

Dries Buytaert
committed
$this->assertNoText(t('This is a dummy message.'), t('Message was not cached.'));
$this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), t('Session cookie was deleted.'));
// Verify that session was destroyed.
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);

Dries Buytaert
committed
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.'));

Dries Buytaert
committed
$this->assertFalse($this->drupalGetHeader('Set-Cookie'), t('New session was not started.'));
// Verify that modifying $_SESSION without having started a session
// generates a watchdog message, and that no messages have been generated
// so far.
$this->assertEqual($this->getWarningCount(), 0, t('No watchdog messages have been generated'));
$this->drupalGet('/session-test/set-not-started');
$this->assertSessionCookie(FALSE);
$this->assertSessionStarted(FALSE);
$this->assertEqual($this->getWarningCount(), 1, t('1 watchdog messages has been generated'));
}
/**
* Count watchdog messages about modifying $_SESSION without having started a
* session.
*/
function getWarningCount() {
return db_select('watchdog')
->condition('type', 'session')
->condition('message', '$_SESSION is non-empty yet no code has called drupal_session_start().')
->countQuery()
->execute()
->fetchField();
}

Dries Buytaert
committed
/**
* Reset the cookie file so that it refers to the specified user.
*
* @param $uid User id to set as the active session.
*/
function sessionReset($uid = 0) {
// Close the internal browser.
$this->curlClose();
// Change cookie file for user.
$this->cookieFile = file_directory_temp() . '/cookie.' . $uid . '.txt';
$this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile;
$this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE;

Dries Buytaert
committed
$this->drupalGet('session-test/get');
$this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session'));
}

Dries Buytaert
committed
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/**
* Assert whether the SimpleTest browser sent a session cookie.
*/
function assertSessionCookie($sent) {
if ($sent) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Cookie'), '1', t('Session cookie was sent.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Cookie'), '0', t('Session cookie was not sent.'));
}
}
/**
* Assert whether session was started during the bootstrap process.
*/
function assertSessionStarted($started) {
if ($started) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Started'), '1', t('Session was started.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Started'), '0', t('Session was not started.'));
}
}
/**
* Assert whether $_SESSION is empty at the beginning of the request.
*/
function assertSessionEmpty($empty) {
if ($empty) {
$this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', t('Session was empty.'));
}
else {
$this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', t('Session was not empty.'));
}
}