Merge branch 'develop-olympus' of github.com:phpbb/phpbb3 into ticket/8610

# By Oleg Pudeyev (29) and others
# Via Andreas Fischer (3) and others
* 'develop-olympus' of github.com:phpbb/phpbb3: (36 commits)
  [ticket/11262] Add .lock in cache directory to .gitignore
  [ticket/11265] Add assertions for board installation success.
  [ticket/11162] Reformat.
  [ticket/10491] Make recreate_database static.
  [ticket/11162] Rename tricky updates to database helper.
  [ticket/10491] Install board once per test run.
  [ticket/10972] Drop user deletion.
  [ticket/10972] Tweak user addition.
  [ticket/10972] Add destroy method to mock cache.
  [ticket/10972] Add mock null cache.
  [ticket/10972] Backport get_db from develop.
  [ticket/10972] Added explicit checks for creating duplicate users.
  [ticket/10972] Moved tests into appropriate places and added comments
  [ticket/10972] Added methods for creating and deleting basic users
  [ticket/11162] Use empty($queries).
  [ticket/11162] Review comments fixed.
  [ticket/11162] Reformat.
  [ticket/11162] Newlines to LF.
  [ticket/11162] Use correct functions.
  [ticket/11162] Account for notify_status.
  ...

Conflicts:
	phpBB/includes/mcp/mcp_topic.php
This commit is contained in:
Nathan Guse 2012-12-13 20:43:31 -06:00
commit 2469225a72
12 changed files with 694 additions and 46 deletions

2
.gitignore vendored
View file

@ -2,7 +2,7 @@
/phpunit.xml
/phpBB/cache/*.html
/phpBB/cache/*.php
/phpBB/cache/queue.php.lock
/phpBB/cache/*.lock
/phpBB/composer.phar
/phpBB/config.php
/phpBB/config_dev.php

View file

@ -0,0 +1,206 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2012 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* Updates rows in given table from a set of values to a new value.
* If this results in rows violating uniqueness constraints, the duplicate
* rows are eliminated.
*
* The only supported table is bookmarks.
*
* @param dbal $db Database object
* @param string $table Table on which to perform the update
* @param string $column Column whose values to change
* @param array $from_values An array of values that should be changed
* @param int $to_value The new value
* @return null
*/
function phpbb_update_rows_avoiding_duplicates($db, $table, $column, $from_values, $to_value)
{
$sql = "SELECT $column, user_id
FROM $table
WHERE " . $db->sql_in_set($column, $from_values);
$result = $db->sql_query($sql);
$old_user_ids = array();
while ($row = $db->sql_fetchrow($result))
{
$old_user_ids[$row[$column]][] = (int) $row['user_id'];
}
$db->sql_freeresult($result);
$sql = "SELECT $column, user_id
FROM $table
WHERE $column = " . (int) $to_value;
$result = $db->sql_query($sql);
$new_user_ids = array();
while ($row = $db->sql_fetchrow($result))
{
$new_user_ids[$row[$column]][] = (int) $row['user_id'];
}
$db->sql_freeresult($result);
$queries = array();
foreach ($from_values as $from_value)
{
if (!isset($old_user_ids[$from_value]))
{
continue;
}
if (empty($new_user_ids))
{
$sql = "UPDATE $table
SET $column = " . (int) $to_value . "
WHERE $column = '" . $db->sql_escape($from_value) . "'";
$queries[] = $sql;
}
else
{
$different_user_ids = array_diff($old_user_ids[$from_value], $new_user_ids[$to_value]);
if (!empty($different_user_ids))
{
$sql = "UPDATE $table
SET $column = " . (int) $to_value . "
WHERE $column = '" . $db->sql_escape($from_value) . "'
AND " . $db->sql_in_set('user_id', $different_user_ids);
$queries[] = $sql;
}
}
}
if (!empty($queries))
{
$db->sql_transaction('begin');
foreach ($queries as $sql)
{
$db->sql_query($sql);
}
$sql = "DELETE FROM $table
WHERE " . $db->sql_in_set($column, $from_values);
$db->sql_query($sql);
$db->sql_transaction('commit');
}
}
/**
* Updates rows in given table from a set of values to a new value.
* If this results in rows violating uniqueness constraints, the duplicate
* rows are merged respecting notify_status (0 takes precedence over 1).
*
* The only supported table is topics_watch.
*
* @param dbal $db Database object
* @param string $table Table on which to perform the update
* @param string $column Column whose values to change
* @param array $from_values An array of values that should be changed
* @param int $to_value The new value
* @return null
*/
function phpbb_update_rows_avoiding_duplicates_notify_status($db, $table, $column, $from_values, $to_value)
{
$sql = "SELECT $column, user_id, notify_status
FROM $table
WHERE " . $db->sql_in_set($column, $from_values);
$result = $db->sql_query($sql);
$old_user_ids = array();
while ($row = $db->sql_fetchrow($result))
{
$old_user_ids[(int) $row['notify_status']][$row[$column]][] = (int) $row['user_id'];
}
$db->sql_freeresult($result);
$sql = "SELECT $column, user_id
FROM $table
WHERE $column = " . (int) $to_value;
$result = $db->sql_query($sql);
$new_user_ids = array();
while ($row = $db->sql_fetchrow($result))
{
$new_user_ids[$row[$column]][] = (int) $row['user_id'];
}
$db->sql_freeresult($result);
$queries = array();
$extra_updates = array(
0 => 'notify_status = 0',
1 => '',
);
foreach ($from_values as $from_value)
{
foreach ($extra_updates as $notify_status => $extra_update)
{
if (!isset($old_user_ids[$notify_status][$from_value]))
{
continue;
}
if (empty($new_user_ids))
{
$sql = "UPDATE $table
SET $column = " . (int) $to_value . "
WHERE $column = '" . $db->sql_escape($from_value) . "'";
$queries[] = $sql;
}
else
{
$different_user_ids = array_diff($old_user_ids[$notify_status][$from_value], $new_user_ids[$to_value]);
if (!empty($different_user_ids))
{
$sql = "UPDATE $table
SET $column = " . (int) $to_value . "
WHERE $column = '" . $db->sql_escape($from_value) . "'
AND " . $db->sql_in_set('user_id', $different_user_ids);
$queries[] = $sql;
}
if ($extra_update)
{
$same_user_ids = array_diff($old_user_ids[$notify_status][$from_value], $different_user_ids);
if (!empty($same_user_ids))
{
$sql = "UPDATE $table
SET $extra_update
WHERE $column = '" . (int) $to_value . "'
AND " . $db->sql_in_set('user_id', $same_user_ids);
$queries[] = $sql;
}
}
}
}
}
if (!empty($queries))
{
$db->sql_transaction('begin');
foreach ($queries as $sql)
{
$db->sql_query($sql);
}
$sql = "DELETE FROM $table
WHERE " . $db->sql_in_set($column, $from_values);
$db->sql_query($sql);
$db->sql_transaction('commit');
}
}

View file

@ -414,19 +414,15 @@ function merge_topics($forum_id, $topic_ids, $to_topic_id)
// Message and return links
$success_msg = 'POSTS_MERGED_SUCCESS';
// If the topic no longer exist, we will update the topic watch table.
// To not let it error out on users watching both topics, we just return on an error...
$db->sql_return_on_error(true);
$db->sql_query('UPDATE ' . TOPICS_WATCH_TABLE . ' SET topic_id = ' . (int) $to_topic_id . ' WHERE ' . $db->sql_in_set('topic_id', $topic_ids));
$db->sql_return_on_error(false);
$db->sql_query('DELETE FROM ' . TOPICS_WATCH_TABLE . ' WHERE ' . $db->sql_in_set('topic_id', $topic_ids));
// If the topic no longer exist, we will update the bookmarks table.
if (!function_exists('phpbb_update_rows_avoiding_duplicates'))
if (!function_exists('phpbb_update_rows_avoiding_duplicates_notify_status'))
{
include($phpbb_root_path . 'includes/functions_database_helper.' . $phpEx);
}
// Update the topic watch table.
phpbb_update_rows_avoiding_duplicates_notify_status($db, TOPICS_WATCH_TABLE, 'topic_id', $topic_ids, $to_topic_id);
// If the topic no longer exist, we will update the bookmarks table.
phpbb_update_rows_avoiding_duplicates($db, BOOKMARKS_TABLE, 'topic_id', $topic_ids, $to_topic_id);
// Link to the new topic

View file

@ -662,13 +662,13 @@ function merge_posts($topic_id, $to_topic_id)
}
else
{
// If the topic no longer exist, we will update the topic watch table.
// To not let it error out on users watching both topics, we just return on an error...
$db->sql_return_on_error(true);
$db->sql_query('UPDATE ' . TOPICS_WATCH_TABLE . ' SET topic_id = ' . (int) $to_topic_id . ' WHERE topic_id = ' . (int) $topic_id);
$db->sql_return_on_error(false);
if (!function_exists('phpbb_update_rows_avoiding_duplicates_notify_status'))
{
include($phpbb_root_path . 'includes/functions_database_helper.' . $phpEx);
}
$db->sql_query('DELETE FROM ' . TOPICS_WATCH_TABLE . ' WHERE topic_id = ' . (int) $topic_id);
// If the topic no longer exist, we will update the topic watch table.
phpbb_update_rows_avoiding_duplicates_notify_status($db, TOPICS_WATCH_TABLE, 'topic_id', $topic_ids, $to_topic_id);
// If the topic no longer exist, we will update the bookmarks table.
phpbb_update_rows_avoiding_duplicates($db, BOOKMARKS_TABLE, 'topic_id', $topic_id, $to_topic_id);

View file

@ -18,9 +18,19 @@ class phpbb_functional_auth_test extends phpbb_functional_test_case
// check for logout link
$crawler = $this->request('GET', 'index.php');
$this->assert_response_success();
$this->assertContains($this->lang('LOGOUT_USER', 'admin'), $crawler->filter('.navbar')->text());
}
public function test_login_other()
{
$this->create_user('anothertestuser');
$this->login('anothertestuser');
$crawler = $this->request('GET', 'index.php');
$this->assert_response_success();
$this->assertContains('anothertestuser', $crawler->filter('.icon-logout')->text());
}
/**
* @depends test_login
*/
@ -31,10 +41,12 @@ class phpbb_functional_auth_test extends phpbb_functional_test_case
// logout
$crawler = $this->request('GET', 'ucp.php?sid=' . $this->sid . '&mode=logout');
$this->assert_response_success();
$this->assertContains($this->lang('LOGOUT_REDIRECT'), $crawler->filter('#message')->text());
// look for a register link, which should be visible only when logged out
$crawler = $this->request('GET', 'index.php');
$this->assert_response_success();
$this->assertContains($this->lang('REGISTER'), $crawler->filter('.navbar')->text());
}
}

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<table name="phpbb_bookmarks">
<column>user_id</column>
<column>topic_id</column>
<!-- one entry for this topic -->
<row>
<value>1</value>
<value>1</value>
</row>
<!-- non-conflicting entries -->
<row>
<value>2</value>
<value>2</value>
</row>
<row>
<value>3</value>
<value>3</value>
</row>
<!-- conflicting entries -->
<row>
<value>1</value>
<value>4</value>
</row>
<row>
<value>1</value>
<value>5</value>
</row>
<!-- conflicting and non-conflicting entries -->
<row>
<value>1</value>
<value>6</value>
</row>
<row>
<value>1</value>
<value>7</value>
</row>
<row>
<value>2</value>
<value>6</value>
</row>
</table>
</dataset>

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<table name="phpbb_topics_watch">
<column>user_id</column>
<column>topic_id</column>
<column>notify_status</column>
<!-- one entry for this topic -->
<row>
<value>1</value>
<value>1</value>
<value>1</value>
</row>
<!-- non-conflicting entries -->
<row>
<value>1</value>
<value>2</value>
<value>1</value>
</row>
<row>
<value>2</value>
<value>3</value>
<value>0</value>
</row>
<!-- conflicting entries, same notify status -->
<row>
<value>1</value>
<value>4</value>
<value>1</value>
</row>
<row>
<value>1</value>
<value>5</value>
<value>1</value>
</row>
<!-- conflicting entries, notify status 0 into 1 -->
<row>
<value>1</value>
<value>6</value>
<value>0</value>
</row>
<row>
<value>1</value>
<value>7</value>
<value>1</value>
</row>
<!-- conflicting entries, notify status 1 into 0 -->
<row>
<value>1</value>
<value>8</value>
<value>1</value>
</row>
<row>
<value>1</value>
<value>9</value>
<value>0</value>
</row>
<!-- conflicting and non-conflicting entries -->
<row>
<value>1</value>
<value>10</value>
<value>0</value>
</row>
<row>
<value>1</value>
<value>11</value>
<value>1</value>
</row>
<row>
<value>2</value>
<value>10</value>
<value>1</value>
</row>
</table>
</dataset>

View file

@ -0,0 +1,101 @@
<?php
/**
*
* @package testing
* @copyright (c) 2012 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_database_helper.php';
class phpbb_update_rows_avoiding_duplicates_notify_status_test extends phpbb_database_test_case
{
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/topics_watch_duplicates.xml');
}
public static function fixture_data()
{
return array(
// description
// from array
// to value
// expected count with to value post update
// expected notify_status values
array(
'trivial',
array(1),
1000,
1,
1,
),
array(
'no conflict',
array(2),
3,
2,
1,
),
array(
'conflict, same notify status',
array(4),
5,
1,
1,
),
array(
'conflict, notify status 0 into 1',
array(6),
7,
1,
0,
),
array(
'conflict, notify status 1 into 0',
array(8),
9,
1,
0,
),
array(
'conflict and no conflict',
array(10),
11,
2,
0,
),
);
}
/**
* @dataProvider fixture_data
*/
public function test_update($description, $from, $to, $expected_result_count, $expected_notify_status)
{
$db = $this->new_dbal();
phpbb_update_rows_avoiding_duplicates_notify_status($db, TOPICS_WATCH_TABLE, 'topic_id', $from, $to);
$sql = 'SELECT COUNT(*) AS remaining_rows
FROM ' . TOPICS_WATCH_TABLE . '
WHERE topic_id = ' . (int) $to;
$result = $db->sql_query($sql);
$result_count = $db->sql_fetchfield('remaining_rows');
$db->sql_freeresult($result);
$this->assertEquals($expected_result_count, $result_count);
// user id of 1 is the user being updated
$sql = 'SELECT notify_status
FROM ' . TOPICS_WATCH_TABLE . '
WHERE topic_id = ' . (int) $to . '
AND user_id = 1';
$result = $db->sql_query($sql);
$notify_status = $db->sql_fetchfield('notify_status');
$db->sql_freeresult($result);
$this->assertEquals($expected_notify_status, $notify_status);
}
}

View file

@ -0,0 +1,71 @@
<?php
/**
*
* @package testing
* @copyright (c) 2012 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions_database_helper.php';
class phpbb_update_rows_avoiding_duplicates_test extends phpbb_database_test_case
{
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/bookmarks_duplicates.xml');
}
public static function fixture_data()
{
return array(
// description
// from array
// to value
// expected count with to value post update
array(
'trivial',
array(1),
10,
1,
),
array(
'no conflict',
array(2),
3,
2,
),
array(
'conflict',
array(4),
5,
1,
),
array(
'conflict and no conflict',
array(6),
7,
2,
),
);
}
/**
* @dataProvider fixture_data
*/
public function test_update($description, $from, $to, $expected_result_count)
{
$db = $this->new_dbal();
phpbb_update_rows_avoiding_duplicates($db, BOOKMARKS_TABLE, 'topic_id', $from, $to);
$sql = 'SELECT COUNT(*) AS remaining_rows
FROM ' . BOOKMARKS_TABLE . '
WHERE topic_id = ' . (int) $to;
$result = $db->sql_query($sql);
$result_count = $db->sql_fetchfield('remaining_rows');
$db->sql_freeresult($result);
$this->assertEquals($expected_result_count, $result_count);
}
}

View file

@ -34,6 +34,16 @@ class phpbb_mock_cache
$this->data[$var_name] = $var;
}
public function destroy($var_name, $table = '')
{
if ($table)
{
throw new Exception('Destroying tables is not implemented yet');
}
unset($this->data[$var_name]);
}
/**
* Obtain active bots
*/

42
tests/mock/null_cache.php Normal file
View file

@ -0,0 +1,42 @@
<?php
/**
*
* @package testing
* @copyright (c) 2012 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_mock_null_cache
{
public function __construct()
{
}
public function get($var_name)
{
return false;
}
public function put($var_name, $var, $ttl = 0)
{
}
public function destroy($var_name, $table = '')
{
}
public function obtain_bots()
{
return array();
}
public function obtain_word_list()
{
return array();
}
public function set_bots($bots)
{
}
}

View file

@ -16,7 +16,9 @@ class phpbb_functional_test_case extends phpbb_test_case
{
protected $client;
protected $root_url;
protected $cache = null;
protected $db = null;
/**
* Session ID for current test's session (each test makes its own)
@ -33,13 +35,30 @@ class phpbb_functional_test_case extends phpbb_test_case
static protected $config = array();
static protected $already_installed = false;
public function setUp()
static public function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$config = phpbb_test_case_helpers::get_test_config();
if (!isset(self::$config['phpbb_functional_url']))
{
$this->markTestSkipped('phpbb_functional_url was not set in test_config and wasn\'t set as PHPBB_FUNCTIONAL_URL environment variable either.');
self::markTestSkipped('phpbb_functional_url was not set in test_config and wasn\'t set as PHPBB_FUNCTIONAL_URL environment variable either.');
}
if (!self::$already_installed)
{
self::install_board();
self::$already_installed = true;
}
}
public function setUp()
{
parent::setUp();
$this->bootstrap();
$this->cookieJar = new CookieJar;
$this->client = new Goutte\Client(array(), null, $this->cookieJar);
// Reset the curl handle because it is 0 at this point and not a valid
@ -65,6 +84,23 @@ class phpbb_functional_test_case extends phpbb_test_case
{
}
protected function get_db()
{
global $phpbb_root_path, $phpEx;
// so we don't reopen an open connection
if (!($this->db instanceof dbal))
{
if (!class_exists('dbal_' . self::$config['dbms']))
{
include($phpbb_root_path . 'includes/db/' . self::$config['dbms'] . ".$phpEx");
}
$sql_db = 'dbal_' . self::$config['dbms'];
$this->db = new $sql_db();
$this->db->sql_connect(self::$config['dbhost'], self::$config['dbuser'], self::$config['dbpasswd'], self::$config['dbname'], self::$config['dbport']);
}
return $this->db;
}
protected function get_cache_driver()
{
if (!$this->cache)
@ -91,28 +127,14 @@ class phpbb_functional_test_case extends phpbb_test_case
$this->backupStaticAttributesBlacklist += array(
'phpbb_functional_test_case' => array('config', 'already_installed'),
);
if (!static::$already_installed)
{
$this->install_board();
$this->bootstrap();
static::$already_installed = true;
}
}
protected function install_board()
static protected function install_board()
{
global $phpbb_root_path, $phpEx;
self::$config = phpbb_test_case_helpers::get_test_config();
if (!isset(self::$config['phpbb_functional_url']))
{
return;
}
self::$config['table_prefix'] = 'phpbb_';
$this->recreate_database(self::$config);
self::recreate_database(self::$config);
if (file_exists($phpbb_root_path . "config.$phpEx"))
{
@ -157,19 +179,28 @@ class phpbb_functional_test_case extends phpbb_test_case
));
// end data
$content = $this->do_request('install');
$this->assertContains('Welcome to Installation', $content);
$content = self::do_request('install');
self::assertNotSame(false, $content);
self::assertContains('Welcome to Installation', $content);
$this->do_request('create_table', $data);
$content = self::do_request('create_table', $data);
self::assertNotSame(false, $content);
self::assertContains('The database tables used by phpBB', $content);
// 3.0 or 3.1
self::assertContains('have been created and populated with some initial data.', $content);
$this->do_request('config_file', $data);
$content = self::do_request('config_file', $data);
self::assertNotSame(false, $content);
self::assertContains('Configuration file', $content);
file_put_contents($phpbb_root_path . "config.$phpEx", phpbb_create_config_file_data($data, self::$config['dbms'], array(), true, true));
$this->do_request('final', $data);
$content = self::do_request('final', $data);
self::assertNotSame(false, $content);
self::assertContains('You have successfully installed', $content);
copy($phpbb_root_path . "config.$phpEx", $phpbb_root_path . "config_test.$phpEx");
}
private function do_request($sub, $post_data = null)
static private function do_request($sub, $post_data = null)
{
$context = null;
@ -188,13 +219,63 @@ class phpbb_functional_test_case extends phpbb_test_case
return file_get_contents(self::$config['phpbb_functional_url'] . 'install/index.php?mode=install&sub=' . $sub, false, $context);
}
private function recreate_database($config)
static private function recreate_database($config)
{
$db_conn_mgr = new phpbb_database_test_connection_manager($config);
$db_conn_mgr->recreate_db();
}
protected function login()
/**
* Creates a new user with limited permissions
*
* @param string $username Also doubles up as the user's password
* @return int ID of created user
*/
protected function create_user($username)
{
// Required by unique_id
global $config;
if (!is_array($config))
{
$config = array();
}
$config['rand_seed'] = '';
$config['rand_seed_last_update'] = time() + 600;
// Required by user_add
global $db, $cache;
$db = $this->get_db();
if (!function_exists('phpbb_mock_null_cache'))
{
require_once(__DIR__ . '/../mock/null_cache.php');
}
$cache = new phpbb_mock_null_cache;
if (!function_exists('utf_clean_string'))
{
require_once(__DIR__ . '/../../phpBB/includes/utf/utf_tools.php');
}
if (!function_exists('user_add'))
{
require_once(__DIR__ . '/../../phpBB/includes/functions_user.php');
}
$user_row = array(
'username' => $username,
'group_id' => 2,
'user_email' => 'nobody@example.com',
'user_type' => 0,
'user_lang' => 'en',
'user_timezone' => 0,
'user_dateformat' => '',
'user_password' => phpbb_hash($username),
);
return user_add($user_row);
}
protected function login($username = 'admin')
{
$this->add_lang('ucp');
@ -202,7 +283,9 @@ class phpbb_functional_test_case extends phpbb_test_case
$this->assertContains($this->lang('LOGIN_EXPLAIN_UCP'), $crawler->filter('html')->text());
$form = $crawler->selectButton($this->lang('LOGIN'))->form();
$login = $this->client->submit($form, array('username' => 'admin', 'password' => 'admin'));
$crawler = $this->client->submit($form, array('username' => $username, 'password' => $username));
$this->assert_response_success();
$this->assertContains($this->lang('LOGIN_REDIRECT'), $crawler->filter('html')->text());
$cookies = $this->cookieJar->all();