mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-19 12:00:55 +01:00
Provide an "isolated" database connection for testing
Summary: This provides a new connection which doesn't connect to anything, so effects can be isolated to the current process (for unit testing). Test Plan: Ran unit tests. Reviewed By: aran Reviewers: aran, tuomaspelkonen, jungejason CC: aran, epriestley Differential Revision: 193
This commit is contained in:
parent
0e06cd85b7
commit
7387cd63ac
7 changed files with 232 additions and 0 deletions
|
@ -39,6 +39,8 @@ phutil_register_library_map(array(
|
||||||
'AphrontFormView' => 'view/form/base',
|
'AphrontFormView' => 'view/form/base',
|
||||||
'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
|
'AphrontHeadsupActionListView' => 'view/layout/headsup/actionlist',
|
||||||
'AphrontHeadsupActionView' => 'view/layout/headsup/action',
|
'AphrontHeadsupActionView' => 'view/layout/headsup/action',
|
||||||
|
'AphrontIsolatedDatabaseConnection' => 'storage/connection/isolated',
|
||||||
|
'AphrontIsolatedDatabaseConnectionTestCase' => 'storage/connection/isolated/__tests__',
|
||||||
'AphrontListFilterView' => 'view/layout/listfilter',
|
'AphrontListFilterView' => 'view/layout/listfilter',
|
||||||
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
|
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
|
||||||
'AphrontNullView' => 'view/null',
|
'AphrontNullView' => 'view/null',
|
||||||
|
@ -416,6 +418,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorStandardPageView' => 'view/page/standard',
|
'PhabricatorStandardPageView' => 'view/page/standard',
|
||||||
'PhabricatorStatusController' => 'applications/status/base',
|
'PhabricatorStatusController' => 'applications/status/base',
|
||||||
'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster',
|
'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/taskmaster',
|
||||||
|
'PhabricatorTestCase' => 'infrastructure/testing/testcase',
|
||||||
'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor',
|
'PhabricatorTimelineCursor' => 'infrastructure/daemon/timeline/storage/cursor',
|
||||||
'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base',
|
'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/base',
|
||||||
'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event',
|
'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/event',
|
||||||
|
@ -504,6 +507,8 @@ phutil_register_library_map(array(
|
||||||
'AphrontFormView' => 'AphrontView',
|
'AphrontFormView' => 'AphrontView',
|
||||||
'AphrontHeadsupActionListView' => 'AphrontView',
|
'AphrontHeadsupActionListView' => 'AphrontView',
|
||||||
'AphrontHeadsupActionView' => 'AphrontView',
|
'AphrontHeadsupActionView' => 'AphrontView',
|
||||||
|
'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection',
|
||||||
|
'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase',
|
||||||
'AphrontListFilterView' => 'AphrontView',
|
'AphrontListFilterView' => 'AphrontView',
|
||||||
'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
|
'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
|
||||||
'AphrontNullView' => 'AphrontView',
|
'AphrontNullView' => 'AphrontView',
|
||||||
|
@ -795,6 +800,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorStandardPageView' => 'AphrontPageView',
|
'PhabricatorStandardPageView' => 'AphrontPageView',
|
||||||
'PhabricatorStatusController' => 'PhabricatorController',
|
'PhabricatorStatusController' => 'PhabricatorController',
|
||||||
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
||||||
|
'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
|
||||||
'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
|
'PhabricatorTimelineCursor' => 'PhabricatorTimelineDAO',
|
||||||
'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
|
'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
|
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
|
||||||
|
|
26
src/infrastructure/testing/testcase/PhabricatorTestCase.php
Normal file
26
src/infrastructure/testing/testcase/PhabricatorTestCase.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class PhabricatorTestCase extends ArcanistPhutilTestCase {
|
||||||
|
|
||||||
|
protected function willRunTests() {
|
||||||
|
$root = dirname(phutil_get_library_root('phabricator'));
|
||||||
|
require_once $root.'/scripts/__init_env__.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/infrastructure/testing/testcase/__init__.php
Normal file
14
src/infrastructure/testing/testcase/__init__.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('arcanist', 'unit/engine/phutil/testcase');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'moduleutils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorTestCase.php');
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group storage
|
||||||
|
*/
|
||||||
|
class AphrontIsolatedDatabaseConnection extends AphrontDatabaseConnection {
|
||||||
|
|
||||||
|
private $configuration;
|
||||||
|
private static $nextInsertID;
|
||||||
|
private $insertID;
|
||||||
|
|
||||||
|
public function __construct(array $configuration) {
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
|
||||||
|
if (self::$nextInsertID === null) {
|
||||||
|
// Generate test IDs into a distant ID space to reduce the risk of
|
||||||
|
// collisions and make them distinctive.
|
||||||
|
self::$nextInsertID = 55555000000 + mt_rand(0, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeString($string) {
|
||||||
|
return '<S>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeColumnName($name) {
|
||||||
|
return '<C>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeMultilineComment($comment) {
|
||||||
|
return '<K>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeStringForLikeClause($value) {
|
||||||
|
return '<L>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConfiguration($key, $default = null) {
|
||||||
|
return idx($this->configuration, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInsertID() {
|
||||||
|
return $this->insertID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAffectedRows() {
|
||||||
|
return $this->affectedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionKey() {
|
||||||
|
return 'xaction'; // TODO, probably need to stub this better.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectAllResults() {
|
||||||
|
return $this->allResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeRawQuery($raw_query) {
|
||||||
|
|
||||||
|
// NOTE: This method is intentionally simplified for now, since we're only
|
||||||
|
// using it to stub out inserts/updates. In the future it will probably need
|
||||||
|
// to grow more powerful.
|
||||||
|
|
||||||
|
$this->allResults = array();
|
||||||
|
|
||||||
|
// NOTE: We jitter the insert IDs to keep tests honest; a test should cover
|
||||||
|
// the relationship between objects, not their exact insertion order. This
|
||||||
|
// guarantees that IDs are unique but makes it impossible to hard-code tests
|
||||||
|
// against this specific implementation detail.
|
||||||
|
$this->insertID = (self::$nextInsertID += mt_rand(1, 10));
|
||||||
|
$this->affectedRows = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/storage/connection/isolated/__init__.php
Normal file
14
src/storage/connection/isolated/__init__.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'storage/connection/base');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('AphrontIsolatedDatabaseConnection.php');
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AphrontIsolatedDatabaseConnectionTestCase
|
||||||
|
extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function testIsolation() {
|
||||||
|
$conn = $this->newIsolatedConnection();
|
||||||
|
|
||||||
|
$test_phid = 'PHID-TEST-'.sha1(mt_rand());
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn,
|
||||||
|
'INSERT INTO phabricator_phid.phid (phid) VALUES (%s)',
|
||||||
|
$test_phid);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$real_phid = id(new PhabricatorPHID())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$test_phid);
|
||||||
|
$this->assertEqual(
|
||||||
|
null,
|
||||||
|
$real_phid,
|
||||||
|
'Expect fake PHID to exist only in isolation.');
|
||||||
|
} catch (AphrontQueryConnectionException $ex) {
|
||||||
|
// If we can't connect to the database, conclude that the isolated
|
||||||
|
// connection actually is isolated. Philosophically, this perhaps allows
|
||||||
|
// us to claim this test does not depend on the database?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInsertGeneratesID() {
|
||||||
|
$conn = $this->newIsolatedConnection();
|
||||||
|
|
||||||
|
queryfx($conn, 'INSERT');
|
||||||
|
$id1 = $conn->getInsertID();
|
||||||
|
|
||||||
|
queryfx($conn, 'INSERT');
|
||||||
|
$id2 = $conn->getInsertID();
|
||||||
|
|
||||||
|
$this->assertEqual(true, (bool)$id1, 'ID1 exists.');
|
||||||
|
$this->assertEqual(true, (bool)$id2, 'ID2 exists.');
|
||||||
|
$this->assertEqual(true, $id1 != $id2, 'IDs are distinct.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newIsolatedConnection() {
|
||||||
|
$config = array();
|
||||||
|
return new AphrontIsolatedDatabaseConnection($config);
|
||||||
|
}
|
||||||
|
}
|
17
src/storage/connection/isolated/__tests__/__init__.php
Normal file
17
src/storage/connection/isolated/__tests__/__init__.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/storage/phid');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/testing/testcase');
|
||||||
|
phutil_require_module('phabricator', 'storage/connection/isolated');
|
||||||
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('AphrontIsolatedDatabaseConnectionTestCase.php');
|
Loading…
Reference in a new issue