* Add a set of failing tests for UrlMap -- TDD!
authorRobert Sesek <rsesek@bluestatic.org>
Sat, 18 Jun 2011 18:19:20 +0000 (14:19 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sat, 18 Jun 2011 18:19:20 +0000 (14:19 -0400)
* Document how strict pattern matching works (versus prefix)

README
http/request.php
http/url_map.php
testing/tests/http/url_map_test.php [new file with mode: 0644]

diff --git a/README b/README
index 026dfc968a91cd7c8cdf869c7eede892fac59f8f..ba29b9ec586e3117f83c2c4de52dfcaa7f6ebc38 100644 (file)
--- a/README
+++ b/README
@@ -137,7 +137,7 @@ Requests will be in the rewritten form of:
 
   https://example.comm/webapp/user/view/42
 
-The UrlMap would be any of the following:
+The UrlMap could be any of the following:
 
   ID paramter extracted manually from the URL or through GET or POST variables
   'user/view'
@@ -147,8 +147,22 @@ The UrlMap would be any of the following:
 
   Using an ActionController (see previous section)
   'user/{action}'
+  Or,
   'user/{action}/{id}'
 
+In the above forms, the matcher does prefix matching. Combined with the fact
+that rules are evaluated linearly, this can lead to a potentially unexpected
+result. When you have a more general rule before a specific one, the general
+will always be the one matched. For example:
+
+  'document/edit'
+  'document/edit/{id}'
+
+The second rule will never be matched, because the evaluator will find and
+invoke the first rule and then stop. This isn't always the desired behavior and
+can be altered by adding two slashes to the end of a rule. These two slashes
+make the parser matching strict equality, rather than prefix.
+
 The second type of matching is done by regular expressions. These patterns both
 start and end with a slash. They are evaluated every time a request is routed
 and any pattern groups are saved in the url_pattern key of the HttpRequest data.
index 7f0590e01e6768dbdd4104f2ab9e0f63d102f889..ff48b00ff1ad686903656760e3ac36ebcd9cb3bd 100644 (file)
@@ -32,4 +32,12 @@ class Request extends \hoplite\base\StrictObject
 
   /*! @var array HTTP request data. */
   public $data = array();
+
+  /*!
+    Constructor. Takes an optional URL.
+  */
+  public function __construct($url = '')
+  {
+    $this->url = $url;
+  }
 }
index b7d3fa4975a5a796ad6a2629a7905eb02c6e7645..d3590beb14d305d0febc777a504182aa0d29f35d 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_url)
+  public function Evaluate(Request $request)
   {}
 
   /*! @brief Takes a value from the map and returns an Action object.
diff --git a/testing/tests/http/url_map_test.php b/testing/tests/http/url_map_test.php
new file mode 100644 (file)
index 0000000..7f4280f
--- /dev/null
@@ -0,0 +1,152 @@
+<?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/url_map.php';
+
+class UrlMapTest extends \PHPUnit_Framework_TestCase
+{
+  public function setUp()
+  {
+    $globals = array();
+    $this->fixture = new http\UrlMap(new http\RootController($globals));
+  }
+
+  public function testSimpleEvaluate()
+  {
+    $map = array(
+      'action/two' => 'Invalid',
+      'some/long/action' => 'Valid'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('some/long/action');
+    $this->assertEquals('Valid', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('another/action');
+    $this->assertNull($this->fixture->Evaluate($request));
+  }
+
+  public function testSimpleExtendedEvaluate()
+  {
+    $map = array(
+      'one/two/three' => 'Invald',
+      'another/action' => 'Valid',
+      'four/five/six' => 'Invalid2'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('another/action/thing');
+    $this->assertEquals('Valid', $this->fixture->Evaluate($request));
+  }
+
+  public function testSimplePrecedenceEvaluate()
+  {
+    $map = array(
+      'some/action/first' => 'First',
+      'some/action' => 'Second',
+      'some/action/second' => 'Third'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('some/action/first');
+    $this->assertEquals('First', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('some/action/second');
+    $this->assertEquals('Second', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('some/action/third');
+    $this->assertNull($this->fixture->Evaluate($request));
+  }
+
+  public function testExtractSingle()
+  {
+    $map = array(
+      'action/one' => 'First',
+      'user/view/{id}' => 'Second',
+      'user/add' => 'Third'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('user/view/42');
+    $this->assertEquals('Second', $this->fixture->Evaluate($request));
+    $this->assertEquals('42', $request->data['id']);
+
+    $request = new http\Request('user/add');
+    $this->assertEquals('Third', $this->fixture->Evaluate($request));
+  }
+
+  public function testExtractDouble()
+  {
+    $map = array(
+      'action/unused' => 'Invalid',
+      'document/{action}/{key}' => 'DocumentController'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('document/edit/42');
+    $this->assertEquals('DocumentController', $this->fixture->Evaluate($request));
+    $this->assertEquals('edit', $request->data['action']);
+    $this->assertEquals('42', $request->data['key']);
+  }
+
+  public function testExactMatch()
+  {
+    $map = array(
+      'action/one' => 'First',
+      'action/two//' => 'Second',
+      'action/two/alpha' => 'Third'
+    );
+    $this->fixure->set_map($map);
+
+    $request = new http\Request('action/one');
+    $this->assertEquals('First', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('action/two');
+    $this->assertEquals('Second', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('action/two/alpha');
+    $this->assertEquals('Third', $this->fixture->Evaluate($request));
+  }
+
+  public function testRegEx()
+  {
+    $map = array(
+      'user/test' => 'First',
+      '/user\/([a-z]+)(\/([0-9]+))?/' => 'Second',
+      'user/test2' => 'Third'
+    );
+    $this->fixture->set_map($map);
+
+    $request = new http\Request('user/test');
+    $this->assertEquals('First', $this->fixture->Evaluate($request));
+
+    $request = new http\Request('user/add');
+    $this->assertEquals('Second', $this->fixture->Evaluate($request));
+    $this->assertEquals('add', $request->data['url_pattern'][1]);
+    $this->assertNull($request->data['url_pattern'][2]);
+
+    $request = new http\Request('user/view/14');
+    $this->assertEquals('Second', $this->fixture->Evaluate($request));
+    $this->assertEquals('view', $request->data['url_pattern'][1]);
+    $this->assertEquals('14', $request->data['url_pattern'][2]);
+
+    $request = new http\Request('user/test2');
+    $this->assertEquals('Third', $this->fixture->Evaluate($request));
+  }
+}