mirror of
https://github.com/silverstripe/silverstripe-testsession
synced 2024-10-22 14:06:00 +02:00
API State setting within session, make tmpdb optional
- Allow limited state setting when session is already in progress - Allow test sessions without a test database - Denote an “in progress” session through a “testsession.started” session flag rather than the usage of a temporary database
This commit is contained in:
parent
c901abe83c
commit
72b48f4c38
@ -34,6 +34,7 @@ and interact with it through other URL endpoints.
|
||||
|
||||
Commands:
|
||||
|
||||
* `dev/testsession`: Shows options for starting a test session
|
||||
* `dev/testsession/start`: Sets up test state, most commonly a test database will be constructed,
|
||||
and your browser session will be amended to use this database. See "Parameters" documentation below.
|
||||
* `dev/testsession/end`: Removes the test state, and resets to the original database.
|
||||
@ -45,14 +46,15 @@ Parameters for "dev/testsession/start":
|
||||
* `fixture`: Loads a YAML fixture in the format generally accepted by `SapphireTest`
|
||||
(see [fixture format docs](http://doc.silverstripe.org/framework/en/topics/testing/fixtures)).
|
||||
The path should be relative to the webroot.
|
||||
* `createDatabase`: Create a temporary database.
|
||||
* `database`: Set an alternative database name in the current
|
||||
browser session as a cookie. Does not actually create the database,
|
||||
that's usually handled by `SapphireTest::create_temp_db()`.
|
||||
Note: The database names are limited to a specific naming convention as a security measure:
|
||||
The "ss_tmpdb" prefix and a random sequence of seven digits.
|
||||
This avoids the user gaining access to other production databases available on the same connection.
|
||||
The "ss_tmpdb" prefix and a random sequence of seven digits.
|
||||
This avoids the user gaining access to other production databases available on the same connection.
|
||||
* `mailer`: Subclass of `Mailer`, typically used to record emails instead of actually sending them.
|
||||
* `date`: Sets a simulated date used for all framework operations.
|
||||
* `datetime`: Sets a simulated date used for all framework operations.
|
||||
Format as "yyyy-MM-dd HH:mm:ss" (Example: "2012-12-31 18:40:59").
|
||||
|
||||
Example usage with parameters:
|
||||
|
19
_config.php
19
_config.php
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
if(
|
||||
Config::inst()->get('Security', 'token')
|
||||
&& DB::get_alternative_database_name()
|
||||
) {
|
||||
require_once BASE_PATH . '/vendor/autoload.php';
|
||||
|
||||
// Register mailer
|
||||
if($mailer = Session::get('testsession.mailer')) {
|
||||
Email::set_mailer(new $mailer());
|
||||
\Config::inst()->update("Email","send_all_emails_to", null);
|
||||
}
|
||||
|
||||
// Set mock date and time
|
||||
$date = Session::get('testsession.date');
|
||||
if($date) {
|
||||
SS_Datetime::set_mock_now($date);
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ class TestSessionController extends Controller {
|
||||
'set',
|
||||
'end',
|
||||
'clear',
|
||||
'Form',
|
||||
'StartForm',
|
||||
'ProgressForm',
|
||||
);
|
||||
|
||||
private static $alternative_database_name = -1;
|
||||
@ -29,61 +30,87 @@ class TestSessionController extends Controller {
|
||||
return Controller::join_links(Director::baseUrl(), 'dev/testsession', $action);
|
||||
}
|
||||
|
||||
public function index() {
|
||||
if(Session::get('testsession.started')) {
|
||||
return $this->renderWith('TestSession_inprogress');
|
||||
} else {
|
||||
return $this->renderWith('TestSession_start');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a test session.
|
||||
*/
|
||||
public function start($request) {
|
||||
if(SapphireTest::using_temp_db()) return $this->renderWith('TestSession_inprogress');
|
||||
public function start() {
|
||||
$this->extend('onBeforeStart');
|
||||
$params = $this->request->requestVars();
|
||||
if(isset($params['createDatabase'])) $params['createDatabase'] = 1; // legacy default behaviour
|
||||
$this->setState($params);
|
||||
$this->extend('onAfterStart');
|
||||
|
||||
// Database
|
||||
$dbName = $request->requestVar('database');
|
||||
if($dbName) {
|
||||
$dbExists = (bool)DB::query(
|
||||
sprintf("SHOW DATABASES LIKE '%s'", Convert::raw2sql($dbName))
|
||||
)->value();
|
||||
} else {
|
||||
$dbExists = false;
|
||||
}
|
||||
|
||||
$this->extend('onBeforeStart', $dbName);
|
||||
|
||||
if(!$dbExists) {
|
||||
// Create a new one with a randomized name
|
||||
$dbName = SapphireTest::create_temp_db();
|
||||
}
|
||||
|
||||
$this->setState(array_merge($request->requestVars(), array('database' => $dbName)));
|
||||
|
||||
$this->extend('onAfterStart', $dbName);
|
||||
|
||||
return $this->renderWith('TestSession_start');
|
||||
return $this->renderWith('TestSession_inprogress');
|
||||
}
|
||||
|
||||
public function Form() {
|
||||
public function StartForm() {
|
||||
$fields = new FieldList(
|
||||
new CheckboxField('createDatabase', 'Create temporary database?', 1)
|
||||
);
|
||||
$fields->merge($this->getBaseFields());
|
||||
$form = new Form(
|
||||
$this,
|
||||
'Form',
|
||||
'StartForm',
|
||||
$fields,
|
||||
new FieldList(
|
||||
(new TextField('fixture', 'Fixture YAML file path'))
|
||||
->setAttribute('placeholder', 'Example: framework/tests/security/MemberTest.yml'),
|
||||
$datetimeField = new DatetimeField('date', 'Custom date'),
|
||||
new HiddenField('flush', null, 1)
|
||||
),
|
||||
new FormAction('start', 'Start Session')
|
||||
)
|
||||
);
|
||||
|
||||
$this->extend('updateStartForm', $form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows state which is allowed to be modified while a test session is in progress.
|
||||
*/
|
||||
public function ProgressForm() {
|
||||
$fields = $this->getBaseFields();
|
||||
$form = new Form(
|
||||
$this,
|
||||
'ProgressForm',
|
||||
$fields,
|
||||
new FieldList(
|
||||
new FormAction('set', 'Set testing state')
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$form->setFormAction($this->Link('set'));
|
||||
|
||||
$this->extend('updateProgressForm', $form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
protected function getBaseFields() {
|
||||
$fields = new FieldList(
|
||||
(new TextField('fixture', 'Fixture YAML file path'))
|
||||
->setAttribute('placeholder', 'Example: framework/tests/security/MemberTest.yml'),
|
||||
$datetimeField = new DatetimeField('datetime', 'Custom date'),
|
||||
new HiddenField('flush', null, 1)
|
||||
);
|
||||
$datetimeField->getDateField()
|
||||
->setConfig('dateformat', 'yyyy-MM-dd')
|
||||
->setConfig('showcalendar', true)
|
||||
->setAttribute('placeholder', 'Date');
|
||||
->setAttribute('placeholder', 'Date (yyyy-MM-dd)');
|
||||
$datetimeField->getTimeField()
|
||||
->setAttribute('placeholder', 'Time');
|
||||
$form->setFormAction($this->Link('set'));
|
||||
->setConfig('timeformat', 'HH:mm:ss')
|
||||
->setAttribute('placeholder', 'Time (HH:mm:ss)');
|
||||
$datetimeField->setValue(Session::get('testsession.datetime'));
|
||||
|
||||
$this->extend('updateForm', $form);
|
||||
$this->extend('updateBaseFields', $fields);
|
||||
|
||||
return $form;
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function DatabaseName() {
|
||||
@ -98,15 +125,12 @@ class TestSessionController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
public function set($request) {
|
||||
if(!SapphireTest::using_temp_db()) {
|
||||
throw new LogicException(
|
||||
"This command can only be used with a temporary database. "
|
||||
. "Perhaps you should use dev/testsession/start first?"
|
||||
);
|
||||
public function set() {
|
||||
if(!Session::get('testsession.started')) {
|
||||
throw new LogicException("No test session in progress.");
|
||||
}
|
||||
|
||||
$params = $request->requestVars();
|
||||
$params = $this->request->requestVars();
|
||||
$this->extend('onBeforeSet', $params);
|
||||
$this->setState($params);
|
||||
$this->extend('onAfterSet');
|
||||
@ -114,17 +138,16 @@ class TestSessionController extends Controller {
|
||||
return $this->renderWith('TestSession_inprogress');
|
||||
}
|
||||
|
||||
public function clear($request) {
|
||||
if(!SapphireTest::using_temp_db()) {
|
||||
throw new LogicException(
|
||||
"This command can only be used with a temporary database. "
|
||||
. "Perhaps you should use dev/testsession/start first?"
|
||||
);
|
||||
public function clear() {
|
||||
if(!Session::get('testsession.started')) {
|
||||
throw new LogicException("No test session in progress.");
|
||||
}
|
||||
|
||||
$this->extend('onBeforeClear');
|
||||
|
||||
SapphireTest::empty_temp_db();
|
||||
if(SapphireTest::using_temp_db()) {
|
||||
SapphireTest::empty_temp_db();
|
||||
}
|
||||
|
||||
if(isset($_SESSION['_testsession_codeblocks'])) {
|
||||
unset($_SESSION['_testsession_codeblocks']);
|
||||
@ -136,19 +159,19 @@ class TestSessionController extends Controller {
|
||||
}
|
||||
|
||||
public function end() {
|
||||
if(!SapphireTest::using_temp_db()) {
|
||||
throw new LogicException(
|
||||
"This command can only be used with a temporary database. "
|
||||
. "Perhaps you should use dev/testsession/start first?"
|
||||
);
|
||||
if(!Session::get('testsession.started')) {
|
||||
throw new LogicException("No test session in progress.");
|
||||
}
|
||||
|
||||
$this->extend('onBeforeEnd');
|
||||
|
||||
SapphireTest::kill_temp_db();
|
||||
DB::set_alternative_database_name(null);
|
||||
// Workaround for bug in Cookie::get(), fixed in 3.1-rc1
|
||||
self::$alternative_database_name = null;
|
||||
if(SapphireTest::using_temp_db()) {
|
||||
SapphireTest::kill_temp_db();
|
||||
DB::set_alternative_database_name(null);
|
||||
// Workaround for bug in Cookie::get(), fixed in 3.1-rc1
|
||||
self::$alternative_database_name = null;
|
||||
}
|
||||
|
||||
Session::clear('testsession');
|
||||
|
||||
$this->extend('onAfterEnd');
|
||||
@ -189,26 +212,47 @@ class TestSessionController extends Controller {
|
||||
// Filter keys
|
||||
$data = array_diff_key(
|
||||
$data,
|
||||
array('action_set' => true, 'SecurityID' => true, 'url' => true)
|
||||
array(
|
||||
'action_set' => true,
|
||||
'action_start' => true,
|
||||
'SecurityID' => true,
|
||||
'url' => true,
|
||||
'flush' => true,
|
||||
)
|
||||
);
|
||||
|
||||
// Database
|
||||
$dbname = (isset($data['database'])) ? $data['database'] : null;
|
||||
if($dbname) {
|
||||
if(
|
||||
!Session::get('testsession.started')
|
||||
&& (@$data['createDatabase'] || @$data['database'])
|
||||
) {
|
||||
$dbName = (isset($data['database'])) ? $data['database'] : null;
|
||||
if($dbName) {
|
||||
$dbExists = (bool)DB::query(
|
||||
sprintf("SHOW DATABASES LIKE '%s'", Convert::raw2sql($dbName))
|
||||
)->value();
|
||||
} else {
|
||||
$dbExists = false;
|
||||
}
|
||||
if(!$dbExists) {
|
||||
// Create a new one with a randomized name
|
||||
$dbName = SapphireTest::create_temp_db();
|
||||
}
|
||||
|
||||
// Set existing one, assumes it already has been created
|
||||
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
|
||||
$pattern = strtolower(sprintf('#^%stmpdb\d{7}#', $prefix));
|
||||
if(!preg_match($pattern, $dbname)) {
|
||||
if(!preg_match($pattern, $dbName)) {
|
||||
throw new InvalidArgumentException("Invalid database name format");
|
||||
}
|
||||
DB::set_alternative_database_name($dbname);
|
||||
DB::set_alternative_database_name($dbName);
|
||||
// Workaround for bug in Cookie::get(), fixed in 3.1-rc1
|
||||
self::$alternative_database_name = $dbname;
|
||||
self::$alternative_database_name = $dbName;
|
||||
|
||||
// Database name is set in cookie (next request), ensure its available on this request already
|
||||
global $databaseConfig;
|
||||
DB::connect(array_merge($databaseConfig, array('database' => $dbname)));
|
||||
unset($data['database']);
|
||||
DB::connect(array_merge($databaseConfig, array('database' => $dbName)));
|
||||
if(isset($data['database'])) unset($data['database']);
|
||||
}
|
||||
|
||||
// Fixtures
|
||||
@ -227,34 +271,33 @@ class TestSessionController extends Controller {
|
||||
$mailer
|
||||
));
|
||||
}
|
||||
|
||||
// Configured through testsession/_config.php
|
||||
Session::set('testsession.mailer', $mailer);
|
||||
unset($data['mailer']);
|
||||
}
|
||||
|
||||
// Date
|
||||
$date = (isset($data['date'])) ? $data['date'] : null;
|
||||
if($date) {
|
||||
// Date and time
|
||||
if(@$data['datetime']['date'] && @$data['datetime']['time']) {
|
||||
require_once 'Zend/Date.php';
|
||||
// Convert DatetimeField format
|
||||
if(is_array($date)) $date = $date['date'] . ' ' . $date['time'];
|
||||
if(!Zend_Date::isDate($date, 'yyyy-MM-dd HH:mm:ss')) {
|
||||
$datetime = $data['datetime']['date'] . ' ' . $data['datetime']['time'];
|
||||
if(!Zend_Date::isDate($datetime, 'yyyy-MM-dd HH:mm:ss')) {
|
||||
throw new LogicException(sprintf(
|
||||
'Invalid date format "%s", use yyyy-MM-dd HH:mm:ss',
|
||||
$date
|
||||
$datetime
|
||||
));
|
||||
}
|
||||
|
||||
// Configured through testsession/_config.php
|
||||
Session::set('testsession.date', $date);
|
||||
unset($data['date']);
|
||||
Session::set('testsession.datetime', $datetime);
|
||||
unset($data['datetime']);
|
||||
} else {
|
||||
unset($data['datetime']);
|
||||
}
|
||||
|
||||
// Set all other keys without special handling
|
||||
if($data) foreach($data as $k => $v) {
|
||||
Session::set('testsession.' . $k, $v);
|
||||
}
|
||||
|
||||
Session::set('testsession.started', true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,12 +305,10 @@ class TestSessionController extends Controller {
|
||||
*/
|
||||
public function getState() {
|
||||
$state = array();
|
||||
if($dbname = DB::get_alternative_database_name()) {
|
||||
$state[] = new ArrayData(array(
|
||||
$state[] = new ArrayData(array(
|
||||
'Name' => 'Database',
|
||||
'Value' => $dbname,
|
||||
'Value' => DB::getConn()->currentDatabase(),
|
||||
));
|
||||
}
|
||||
$sessionStates = Session::get('testsession');
|
||||
if($sessionStates) foreach($sessionStates as $k => $v) {
|
||||
$state[] = new ArrayData(array(
|
||||
|
@ -1,14 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Allows inclusion of a PHP file, usually with procedural commands
|
||||
* to set up required test state. The file can be generated
|
||||
* through {@link TestSessionStubCodeWriter}, and the session state
|
||||
* set through {@link TestSessionController->set()} and the
|
||||
* 'testsessio.stubfile' state parameter.
|
||||
* Sets state previously initialized through {@link TestSessionController}.
|
||||
*/
|
||||
class TestSessionRequestFilter {
|
||||
|
||||
public function preRequest($req, $session, $model) {
|
||||
if(!$session->inst_get('testsession.started')) return;
|
||||
|
||||
// Date and time
|
||||
if($datetime = $session->inst_get('testsession.datetime')) {
|
||||
SS_Datetime::set_mock_now($datetime);
|
||||
}
|
||||
|
||||
// Register mailer
|
||||
if($mailer = $session->inst_get('testsession.mailer')) {
|
||||
Email::set_mailer(new $mailer());
|
||||
Config::inst()->update("Email","send_all_emails_to", null);
|
||||
}
|
||||
|
||||
// Allows inclusion of a PHP file, usually with procedural commands
|
||||
// to set up required test state. The file can be generated
|
||||
// through {@link TestSessionStubCodeWriter}, and the session state
|
||||
// set through {@link TestSessionController->set()} and the
|
||||
// 'testsessio.stubfile' state parameter.
|
||||
$file = $session->inst_get('testsession.stubfile');
|
||||
if(!Director::isLive() && $file && file_exists($file)) {
|
||||
// Connect to the database so the included code can interact with it
|
||||
|
@ -8,4 +8,3 @@
|
||||
</ul>
|
||||
</p>
|
||||
<% end_if %>
|
||||
$Form
|
@ -18,7 +18,7 @@
|
||||
<a id="home-link" href="$BaseHref">Return to your site</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="start-link" href="$Link(start)">Start a new test session</a>
|
||||
<a id="start-link" href="$Link">Start a new test session</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -12,11 +12,25 @@
|
||||
<h1>SilverStripe TestSession</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- SUCCESS: DBNAME=$DatabaseName -->
|
||||
<p>
|
||||
You're in the middle of a test session.
|
||||
Test session in progress.
|
||||
<a id="end-session" href="$Link(end)">Click here to end it.</a>
|
||||
</p>
|
||||
<p>Where would you like to start?</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a id="home-link" href="$BaseHref">Homepage - published site</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="draft-link" href="$BaseHref/?stage=Stage">Homepage - draft site</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="admin-link" href="$BaseHref/admin/">CMS Admin</a>
|
||||
</li>
|
||||
</ul>
|
||||
<% include TestSession_State %>
|
||||
$ProgressForm
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -12,27 +12,8 @@
|
||||
<h1>SilverStripe TestSession</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- SUCCESS: DBNAME=$DatabaseName -->
|
||||
<p>
|
||||
Started testing session.
|
||||
<% if Fixture %>Loaded fixture "$Fixture" into database.<% end_if %>
|
||||
Time to start testing; where would you like to start?
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a id="home-link" href="$BaseHref">Homepage - published site</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="draft-link" href="$BaseHref/?stage=Stage">Homepage - draft site</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="admin-link" href="$BaseHref/admin/">CMS Admin</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="end-link" href="$Link(end)">End your test session</a>
|
||||
</li>
|
||||
</ul>
|
||||
<% include TestSession_State %>
|
||||
<p>Start a new test session</p>
|
||||
$StartForm
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user