* Write a unittest for RootControler
authorRobert Sesek <rsesek@bluestatic.org>
Sun, 12 Jun 2011 14:30:22 +0000 (10:30 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sun, 12 Jun 2011 14:30:22 +0000 (10:30 -0400)
* Fix a bunch of syntax errors

http/action.php
http/output_filter.php
http/response.php
http/response_code.php
http/root_controller.php
http/url_map.php
testing/tests/base/functions_test.php
testing/tests/http/root_controller_test.php [new file with mode: 0644]

index 1e73a2eb3bfea6700473607e53f4da5d5f8d6fb4..46f1859d5cf644788cdf7c3487fbf4ae6f717be5 100644 (file)
@@ -45,7 +45,7 @@ abstract class Action
   /*!
     Performs the action and fills out the response's data model.
   */
-  public function Invoke(Request $request, Response $response);
+  public abstract function Invoke(Request $request, Response $response);
 
   /*!
     Called after this has been Invoked().
index 7ffde90afafc3ed0cd5f43a4f3192c55e8c2412a..2eeb0e53087fbdfcbe99e7388b587f0657d04f87 100644 (file)
@@ -16,7 +16,7 @@
 
 namespace hoplite\http;
 
-require_once HOPLITE_ROOT . 'http/response_code.php';
+require_once HOPLITE_ROOT . '/http/response_code.php';
 
 /*!
   The OutputFilter is executed after all Actions have been processed. The
index 86f2d02ec16558d50f4c18990d353f91c508345a..aa90ef38bb611b5878d51dbd7729fbb7e2645d0b 100644 (file)
@@ -16,8 +16,8 @@
 
 namespace hoplite\http;
 
-require_once HOPLITE_ROOT . 'base/strict_object.php';
-require_once HOPLITE_ROOT . 'http/response_code.php';
+require_once HOPLITE_ROOT . '/base/strict_object.php';
+require_once HOPLITE_ROOT . '/http/response_code.php';
 
 /*!
   A Response holds data processed by Action objects. When the RootController is
index ab8cd54b2980e2e4521af50ab333ebe7ddcfef53..f8370797918ea7518f696f29d4356105454414b4 100644 (file)
@@ -23,7 +23,7 @@ namespace hoplite\http;
 */
 class ResponseCode
 {
-  const CONTINUE                         = 100;
+  const HTTP_CONTINUE                    = 100;  // CONTINUE is a keyword.
   const SWITCHING_PROTOCOLS              = 101;
   const OK                               = 200;
   const CREATED                          = 201;
index f63cda9a353b1de0f72ef62033619863e05bc47f..e2e90ae7ce86e9d1ac0c2edb4a68ff2b55a678d9 100644 (file)
 
 namespace hoplite\http;
 
+require_once HOPLITE_ROOT . '/http/request.php';
+require_once HOPLITE_ROOT . '/http/response.php';
+require_once HOPLITE_ROOT . '/http/response_code.php';
+
 /*!
   The RootController is meant to be invoked from the index.php of the
   application.
 */
 class RootController
 {
+  /*! @var Request */
+  private $request = NULL;
+
+  /*! @var Response */
+  private $response = NULL;
+
+  /*! @var UrlMap */
+  private $url_map = NULL;
+
+  /*! @var OutputFilter */
+  private $output_filter = NULL;
+
   /*!
     Creates the controller with the request context information, typicallhy
     from the global scope ($GLOBALS), but can be injected for testing.
-    @param globals
+    @param UrlMap The routing map
+    @param OutputFilter The object responsible for decorating output.
+    @param array& PHP globals array
   */
-  public function __construct($globals)
-  {}
+  public function __construct(array $globals)
+  {
+    $this->response = new Response();
+    $this->request = new Request();
+    $this->request->data = array(
+      '_GET'    => &$globals['_GET'],
+      '_POST'   => &$globals['_POST'],
+      '_COOKIE' => &$globals['_COOKIE'],
+      '_SERVER' => &$globals['_SERVER']
+    );
+  }
+
+  /*! Accessors */
+  public function request() { return $this->request; }
+  public function response() { return $this->response; }
+
+  /*! Sets the UrlMap. */
+  public function set_urL_map(UrlMap $url_map) { $this->url_map = $url_map; }
+
+  /*! Sest the Output Filter. */
+  public function set_output_filter(OutputFilter $output_filter)
+  {
+    $this->output_filter = $output_filter;
+  }
 
   /*!
     Createst the Request and Response that are used throughout the duration of
     the execution.
   */
   public function Run()
-  {}
+  {
+    // The query rewriter module of the webserver rewrites a request from:
+    //    http://example.com/webapp/user/view/42
+    // to:
+    //    http://example.com/webapp/index.php/user/view/42
+    // ... which then becomes accessible from PATH_INFO.
+    $url = $this->request->data['_SERVER']['PATH_INFO'];
+    if ($url[0] == '/')
+      $url = substr($url, 1);
+
+    // Set the final pieces of the request.
+    $this->request->url = $url;
+    $this->request->http_method = $this->request->data['_SERVER']['REQUEST_METHOD'];
+
+    // Dispatch the request to an Action.
+    $this->RouteRequest($url);
+
+    // When control returns here, all actions have been invoked and it's time
+    // to start the output filter and exit.
+    $this->Stop();
+  }
 
   /*!
     Prevents any other Actions from executing. This starts the OutputFilter and
     then exits.
   */
   public function Stop()
-  {}
+  {
+    $this->output_filter->FilterOutput($this->request, $this->response);
+    $this->_Exit();
+  }
+
+  /*!
+    Wrapper around PHP exit().
+  */
+  protected function _Exit()
+  {
+    exit;
+  }
 
   /*!
     Invoked by Run() and can be invoked by others to evaluate and perform the
@@ -50,14 +121,32 @@ class RootController
     @param string The URL fragment to look up in the
   */
   public function RouteRequest($url_fragment)
-  {}
+  {
+    $url_map_value = $this->url_map->Evaluate($url_fragment);
+
+    $action = NULL;
+    if ($url_map_value)
+      $action = $this->url_map->LookupAction($url_map_value);
+
+    if (!$action) {
+      $this->response->response_code = ResponseCode::NOT_FOUND;
+      $this->Stop();
+      return;
+    }
+
+    $this->InvokeAction($action);
+  }
 
   /*!
     Used to run an Action and drive it through its states.
     @param Action
   */
   public function InvokeAction(Action $action)
-  {}
+  {
+    $action->FilterRequest($this->request, $this->response);
+    $action->Invoke($this->request, $this->response);
+    $action->FilterResponse($this->request, $this->response);
+  }
 
   /*!
     Performs a reverse-lookup in the UrlMap for the pattern/fragment for the
index d3590beb14d305d0febc777a504182aa0d29f35d..b7d3fa4975a5a796ad6a2629a7905eb02c6e7645 100644 (file)
@@ -90,7 +90,7 @@ class UrlMap
 
     @return string|NULL A matched value in the ::map() or NULL if no match.
   */
-  public function Evaluate(Request $request)
+  public function Evaluate($request_url)
   {}
 
   /*! @brief Takes a value from the map and returns an Action object.
index a58b9654c7674f9adbf107ae5669a967db6b8515..395b800b632520f071f26b0f6c55198698acb3f8 100644 (file)
@@ -15,7 +15,7 @@
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
 namespace hoplite\test;
-use \hoplite\base as base;
+use hoplite\base as base;
 
 require_once HOPLITE_ROOT . '/base/functions.php';
 
diff --git a/testing/tests/http/root_controller_test.php b/testing/tests/http/root_controller_test.php
new file mode 100644 (file)
index 0000000..c499fe1
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+// Hoplite
+// Copyright (c) 2011 Blue Static
+// 
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or any later version.
+// 
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+namespace hoplite\test;
+use hoplite\http as http;
+
+require_once HOPLITE_ROOT . '/http/action.php';
+require_once HOPLITE_ROOT . '/http/output_filter.php';
+require_once HOPLITE_ROOT . '/http/root_controller.php';
+require_once HOPLITE_ROOT . '/http/url_map.php';
+
+class ActionReporter extends http\Action
+{
+  public $did_filter_request = FALSE;
+  public $did_invoke = FALSE;
+  public $did_filter_response = FALSE;
+
+  public function FilterRequest(http\Request $q, http\Response $s)
+  {
+    $this->did_filter_request = TRUE;
+  }
+
+  public function Invoke(http\Request $q, http\Response $s)
+  {
+    $this->did_invoke = TRUE;
+  }
+
+  public function FilterResponse(http\Request $q, http\Response $s)
+  {
+    $this->did_filter_response = TRUE;
+  }
+}
+
+class RootControllerTest extends \PHPUnit_Framework_TestCase
+{
+  /*!
+    Configures a mock RootControler.
+    @param array|NULL Array of methods to mock
+    @param varargs Constructor parameters.
+    @return Mock RootControler
+  */
+  public function ConfigureMock()
+  {
+    $args = func_get_args();
+    return $this->getMock('hoplite\http\RootController', $args[0], array_slice($args, 1));
+  }
+
+  public function testRun()
+  {
+    $globals = array('_SERVER' => array(
+        'REQUEST_METHOD' => 'GET',
+        'PATH_INFO' => '/some/action/42'
+    ));
+    $mock = $this->ConfigureMock(array('RouteRequest', 'Stop'), $globals);
+
+    $mock->expects($this->once())
+         ->method('RouteRequest')
+         ->with($this->equalTo('some/action/42'));
+
+    $mock->expects($this->once())
+         ->method('Stop');
+
+    $mock->Run();
+  }
+
+  public function testInvokeAction()
+  {
+    $globals = array();
+    $fixture = new http\RootController($globals);
+
+    $action = new ActionReporter($fixture);
+
+    $this->assertFalse($action->did_filter_request);
+    $this->assertFalse($action->did_invoke);
+    $this->assertFalse($action->did_filter_response);
+
+    $fixture->InvokeAction($action);
+
+    $this->assertTrue($action->did_filter_request);
+    $this->assertTrue($action->did_invoke);
+    $this->assertTrue($action->did_filter_response);
+  }
+
+  public function testRouteRequest()
+  {
+    $globals = array();
+    $mock = $this->ConfigureMock(array('Stop', 'InvokeAction'), $globals);
+
+    $fragment = 'some/action/42';
+    $map_value = 'ActionReporter';
+    $action = new ActionReporter($mock);
+
+    $mock->expects($this->once())
+         ->method('InvokeAction')
+         ->with($this->isInstanceOf('hoplite\test\ActionReporter'));
+
+    $url_map = $this->getMock('hoplite\http\UrlMap', array(), array($mock));
+    $url_map->expects($this->once())
+            ->method('Evaluate')
+            ->with($this->equalTo($fragment))
+            ->will($this->returnValue($map_value));
+    $url_map->expects($this->once())
+            ->method('LookupAction')
+            ->with($this->equalTo($map_value))
+            ->will($this->returnValue($action));
+
+    $mock->set_url_map($url_map);
+    $mock->RouteRequest($fragment);
+  }
+
+  public function testRouteRequestInvalid()
+  {
+    $globals = array();
+    $mock = $this->ConfigureMock(array('Stop'), $globals);
+
+    $fragment = 'another/action';
+    
+    $mock->expects($this->once())
+         ->method('Stop');
+
+    $url_map = $this->getMock('hoplite\http\UrlMap', array(), array($mock));
+    $url_map->expects($this->once())
+            ->method('Evaluate')
+            ->with($this->equalTo($fragment));
+
+    $mock->set_url_map($url_map);
+    $mock->RouteRequest($fragment);
+    $this->assertEquals(http\ResponseCode::NOT_FOUND, $mock->response()->response_code);
+  }
+
+  public function testStop()
+  {
+    $globals = array();
+    $mock = $this->ConfigureMock(array('_Exit'), $globals);
+
+    $mock->expects($this->once())
+         ->method('_Exit');
+
+    $output_filter = $this->getMock('hoplite\http\OutputFilter', array(), array($mock));
+    $output_filter->expects($this->once())
+                  ->method('FilterOutput')
+                  ->with($this->isInstanceOf('hoplite\http\Request'),
+                         $this->isInstanceOf('hoplite\http\Response'));
+
+    $mock->set_output_filter($output_filter);
+    $mock->Stop();
+  }
+}