From a2d7675e57a1fe00bf1fc38783c89a756c8d283e Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Wed, 19 Dec 2012 15:18:23 +0100 Subject: [PATCH] API Combined "setdb" into "start". Renamed "emptydb" to "clear". Added "set". Using templates, listing state in them. --- README.md | 39 ++-- code/TestSessionController.php | 250 ++++++++++++------------ templates/Includes/TestSession_State.ss | 10 + templates/TestSession_end.ss | 9 + templates/TestSession_inprogress.ss | 5 + templates/TestSession_start.ss | 34 ++++ 6 files changed, 206 insertions(+), 141 deletions(-) create mode 100644 templates/Includes/TestSession_State.ss create mode 100644 templates/TestSession_end.ss create mode 100644 templates/TestSession_inprogress.ss create mode 100644 templates/TestSession_start.ss diff --git a/README.md b/README.md index b62a58d..c583cc7 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,31 @@ The resulting configuration code needs to be placed in `mysite/_config.php`. ## Usage - * `dev/testsession/start`: A test database will be constructed, and your - browser session will be amended to use this database. - * `dev/testsession/start?fixture=`: Same as above, but also 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. - * `dev/testsession/end`: Removes the test state, and resets to the original database - * `dev/testsession/setdb?database=`: Set an alternative database name in the current - browser session as a cookie. Does not actually create the database, - that's usually handley by `SapphireTest::create_temp_db()`. - * `dev/testsession/emptydb`: Empties the test state. +You start a test session by manually requesting a certain URL, +and interact with it through other URL endpoints. + +Commands: + + * `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. + * `dev/testsession/loadfixture?fixture=`: Loads a fixture into an existing test state. + * `dev/testsession/clear`: Empties the test state. + +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. + * `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. + + +Example usage with parameters: + + dev/testsession/start?database=ss_tmpdb_1234567&fixture=cms/tests/controller/CMSMainTest.yml -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. diff --git a/code/TestSessionController.php b/code/TestSessionController.php index d5209c7..af3dd43 100644 --- a/code/TestSessionController.php +++ b/code/TestSessionController.php @@ -1,12 +1,15 @@ You're in the middle of a test session;" - . " click here to end it.

"; + public function start($request) { + if(SapphireTest::using_temp_db()) return $this->renderWith('TestSession_inprogress'); - } else if(!isset($_GET['fixture'])) { - $me = Director::baseURL() . "dev/testsession/start"; - return << -

Enter a fixture file name to start a new test session. Don't forget to visit dev/testsession/end when -you're done!

-

Fixture file (leave blank to start with default set-up):

- -

- -HTML; - } else { - $fixtureFile = $_GET['fixture']; - - if($fixtureFile) { - // Validate fixture file - $realFile = realpath(BASE_PATH.'/'.$fixtureFile); - $baseDir = realpath(Director::baseFolder()); - if(!$realFile || !file_exists($realFile)) { - return "

Fixture file doesn't exist

"; - } else if(substr($realFile,0,strlen($baseDir)) != $baseDir) { - return "

Fixture file must be inside $baseDir

"; - } else if(substr($realFile,-4) != '.yml') { - return "

Fixture file must be a .yml file

"; - } else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) { - return "

Fixture file must be inside the tests subfolder of one of your modules.

"; - } - } - - $dbname = SapphireTest::create_temp_db(); - + // Database + if(!$request->getVar('database')) { + // Create a new one with a randomized name + $dbname = SapphireTest::create_temp_db(); DB::set_alternative_database_name($dbname); - - // Fixture - if($fixtureFile) { - $fixture = Injector::inst()->create('YamlFixture', $fixtureFile); - $fixture->saveIntoDatabase(); - - // If no fixture, then use defaults - } else { - $dataClasses = ClassInfo::subclassesFor('DataObject'); - array_shift($dataClasses); - foreach($dataClasses as $dataClass) singleton($dataClass)->requireDefaultRecords(); - } - - return "

Started testing session with fixture '$fixtureFile'. - Time to start testing; where would you like to start?

- "; } + + $this->setState($request->getVars()); + + return $this->renderWith('TestSession_start'); } - /** - * Set an alternative database name in the current browser session as a cookie. - * Useful for functional testing libraries like behat to create a "clean slate". - * Does not actually create the database, that's usually handled - * by {@link SapphireTest::create_temp_db()}. - * - * 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. - * - * See {@link start()} for a different approach which actually creates - * the DB and loads a fixture file instead. - * - * Requires PHP's mycrypt extension in order to set the database name - * as an encrypted cookie. - */ - public function setdb() { - if(!isset($_GET['database'])) { - return $this->httpError(400, "dev/testsession/setdb must be used with a 'database' parameter"); - } - - $name = $_GET['database']; - $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; - $pattern = strtolower(sprintf('#^%stmpdb\d{7}#', $prefix)); - if($name && !preg_match($pattern, $name)) { - return $this->httpError(400, "Invalid database name format"); + 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?" + ); } - DB::set_alternative_database_name($name); + $this->setState($request->getVars()); - if($name) { - return "

Set database session to '$name'.

"; - } else { - return "

Unset database session.

"; + 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?" + ); } - + + SapphireTest::empty_temp_db(); + + return "Cleared database and test state"; } - public function emptydb() { - if(SapphireTest::using_temp_db()) { - SapphireTest::empty_temp_db(); - - if(isset($_GET['fixture']) && ($fixtureFile = $_GET['fixture'])) { - $fixture = Injector::inst()->create('YamlFixture', $fixtureFile); - $fixture->saveIntoDatabase(); - return "

Re-test the test database with fixture '$fixtureFile'. Time to start testing; where would" - . " you like to start?

"; - - } else { - return "

Re-test the test database. Time to start testing; where would you like to start?

"; - } - - } else { - return "

dev/testsession/emptydb can only be used with a temporary database. Perhaps you should use" - . " dev/testsession/start first?

"; - } - } - 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?" + ); + } + SapphireTest::kill_temp_db(); DB::set_alternative_database_name(null); Session::clear('testsession'); - return "

Test session ended.

- "; + return $this->renderWith('TestSession_end'); + } + + protected function loadFixtureIntoDb($fixtureFile) { + $realFile = realpath(BASE_PATH.'/'.$fixtureFile); + $baseDir = realpath(Director::baseFolder()); + if(!$realFile || !file_exists($realFile)) { + throw new LogicException("Fixture file doesn't exist"); + } else if(substr($realFile,0,strlen($baseDir)) != $baseDir) { + throw new LogicException("Fixture file must be inside $baseDir"); + } else if(substr($realFile,-4) != '.yml') { + throw new LogicException("Fixture file must be a .yml file"); + } else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) { + throw new LogicException("Fixture file must be inside the tests subfolder of one of your modules."); + } + + $fixture = Injector::inst()->create('YamlFixture', $fixtureFile); + $fixture->saveIntoDatabase(); + + Session::add_to_array('testsession.fixtures', $fixtureFile); + + return $fixture; + } + + /** + * @return boolean + */ + public function isTesting() { + return SapphireTest::using_temp_db(); + } + + public function setState($data) { + // Database + $dbname = (isset($data['database'])) ? $data['database'] : null; + if($dbname) { + // 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)) { + throw new InvalidArgumentException("Invalid database name format"); + } + DB::set_alternative_database_name($dbname); + } + + // Fixtures + $fixtureFile = (isset($data['fixture'])) ? $data['fixture'] : null; + if($fixtureFile) { + $this->loadFixtureIntoDb($fixtureFile); + } else { + // If no fixture, then use defaults + $dataClasses = ClassInfo::subclassesFor('DataObject'); + array_shift($dataClasses); + foreach($dataClasses as $dataClass) singleton($dataClass)->requireDefaultRecords(); + } + } + + /** + * @return ArrayList + */ + public function getState() { + $state = array(); + if($dbname = DB::get_alternative_database_name()) { + $state[] = new ArrayData(array( + 'Name' => 'Database', + 'Value' => $dbname, + )); + } + if($fixtures = Session::get('testsession.fixtures')) { + $state[] = new ArrayData(array( + 'Name' => 'Fixture', + 'Value' => implode(',', array_unique($fixtures)), + )); + } + + return new ArrayList($state); } } \ No newline at end of file diff --git a/templates/Includes/TestSession_State.ss b/templates/Includes/TestSession_State.ss new file mode 100644 index 0000000..169959d --- /dev/null +++ b/templates/Includes/TestSession_State.ss @@ -0,0 +1,10 @@ +<% if State %> +

+ Current testing state +

    + <% control State %> +
  • $Name: $Value
  • + <% end_control %> +
+

+<% end_if %> \ No newline at end of file diff --git a/templates/TestSession_end.ss b/templates/TestSession_end.ss new file mode 100644 index 0000000..1b0d0ad --- /dev/null +++ b/templates/TestSession_end.ss @@ -0,0 +1,9 @@ +

Test session ended.

+ \ No newline at end of file diff --git a/templates/TestSession_inprogress.ss b/templates/TestSession_inprogress.ss new file mode 100644 index 0000000..5e78787 --- /dev/null +++ b/templates/TestSession_inprogress.ss @@ -0,0 +1,5 @@ +

+ You're in the middle of a test session. + Click here to end it. +

+<% include TestSession_State %> \ No newline at end of file diff --git a/templates/TestSession_start.ss b/templates/TestSession_start.ss new file mode 100644 index 0000000..f7ffdf2 --- /dev/null +++ b/templates/TestSession_start.ss @@ -0,0 +1,34 @@ +

+ Started testing session. + <% if Fixture %>Loaded fixture "$Fixture" into database.<% end_if %> + Time to start testing; where would you like to start? +

+ +<% include TestSession_State %> +
+

+ Enter a fixture file name to add it to the test session. + Don't forget to visit dev/testsession/end when you're done! +

+

+ Fixture file: + +

+ +

+ +

+
\ No newline at end of file