|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Vectorface\SnappyRouterTests\Handler; |
|
4
|
|
|
|
|
5
|
|
|
use \Exception; |
|
6
|
|
|
use \ReflectionClass; |
|
7
|
|
|
use \PHPUnit_Framework_TestCase; |
|
8
|
|
|
use Vectorface\SnappyRouter\Config\Config; |
|
9
|
|
|
use Vectorface\SnappyRouter\Handler\JsonRpcHandler; |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* A test for the JsonRpcHandler class. |
|
13
|
|
|
* |
|
14
|
|
|
* @copyright Copyright (c) 2014, VectorFace, Inc. |
|
15
|
|
|
*/ |
|
16
|
|
|
class JsonRpcHandlerTest extends PHPUnit_Framework_TestCase |
|
17
|
|
|
{ |
|
18
|
|
|
/** |
|
19
|
|
|
* Helper method to override the internals of php://input for test purposes. |
|
20
|
|
|
* @param JsonRpcHandler $handler The handler to override. |
|
21
|
|
|
* @param mixed $payload The payload to hand off for the request input. |
|
22
|
|
|
*/ |
|
23
|
|
|
public static function setRequestPayload(JsonRpcHandler $handler, $payload) |
|
24
|
|
|
{ |
|
25
|
|
|
$tmpfile = tempnam(sys_get_temp_dir(), __CLASS__); |
|
26
|
|
|
file_put_contents($tmpfile, is_string($payload) ? $payload : json_encode($payload)); |
|
27
|
|
|
|
|
28
|
|
|
$refCls = new ReflectionClass($handler); |
|
29
|
|
|
$prop = $refCls->getProperty('stdin'); |
|
30
|
|
|
$prop->setAccessible(true); |
|
31
|
|
|
$prop->setValue($handler, $tmpfile); |
|
32
|
|
|
} |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* An overview of how to use the JsonRpcHandler class. |
|
36
|
|
|
* @test |
|
37
|
|
|
*/ |
|
38
|
|
|
public function synopsis() |
|
39
|
|
|
{ |
|
40
|
|
|
$options = array( |
|
41
|
|
|
Config::KEY_CONTROLLERS => array( |
|
42
|
|
|
'TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController' |
|
43
|
|
|
) |
|
44
|
|
|
); |
|
45
|
|
|
$handler = new JsonRpcHandler($options); |
|
46
|
|
|
|
|
47
|
|
|
/* Ignores unconfigured services and anything other than POST */ |
|
48
|
|
|
$this->assertFalse($handler->isAppropriate('/anything', array(), array(), 'GET')); |
|
49
|
|
|
$this->assertFalse($handler->isAppropriate('/anything', array(), array(), 'POST')); |
|
50
|
|
|
|
|
51
|
|
|
/* A single JSON-RPC 1.0 request. */ |
|
52
|
|
|
$this->setRequestPayload($handler, array('method' => 'testAction', 'id' => '1')); |
|
53
|
|
|
$this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
54
|
|
|
|
|
55
|
|
|
$result = json_decode($handler->performRoute()); |
|
56
|
|
|
$this->assertEquals("This is a test service.", $result->result); |
|
57
|
|
|
$this->assertEquals(1, $result->id); |
|
58
|
|
|
|
|
59
|
|
|
/* A batch of JSON-RPC 2.0 requests. */ |
|
60
|
|
|
$this->setRequestPayload($handler, array( |
|
61
|
|
|
array('jsonrpc' => '2.0', 'method' => 'testAction', 'id' => '1'), |
|
62
|
|
|
array('jsonrpc' => '2.0', 'method' => 'testAction', 'id' => '2') |
|
63
|
|
|
)); |
|
64
|
|
|
$this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
65
|
|
|
$this->assertEquals(2, count($handler->getRequests())); |
|
66
|
|
|
|
|
67
|
|
|
$result = json_decode($handler->performRoute()); |
|
68
|
|
|
$this->assertEquals(2, count($result), "Expect 2 responses for 2 calls"); |
|
69
|
|
|
$this->assertEquals("2.0", $result[0]->jsonrpc); |
|
70
|
|
|
$this->assertEquals("This is a test service.", $result[0]->result); |
|
71
|
|
|
$this->assertEquals("1", $result[0]->id); |
|
72
|
|
|
$this->assertEquals("2.0", $result[1]->jsonrpc); |
|
73
|
|
|
$this->assertEquals("This is a test service.", $result[1]->result); |
|
74
|
|
|
$this->assertEquals("2", $result[1]->id); |
|
75
|
|
|
|
|
76
|
|
|
/* Handles notifications without replying. */ |
|
77
|
|
|
$this->setRequestPayload($handler, array( |
|
78
|
|
|
array('jsonrpc' => '2.0', 'method' => 'testAction', 'id' => '1'), |
|
79
|
|
|
array('jsonrpc' => '2.0', 'method' => 'testAction') // Notification |
|
80
|
|
|
)); |
|
81
|
|
|
$this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
82
|
|
|
$result = json_decode($handler->performRoute()); |
|
83
|
|
|
$this->assertEquals(1, count($result), "Expect 1 response for 1 call and 1 notification"); |
|
84
|
|
|
$this->assertEquals("1", $result[0]->id); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Tests various edge cases of the JsonRpcHandler::isAppropriate method. |
|
89
|
|
|
*/ |
|
90
|
|
|
public function testIsAppropriate() |
|
91
|
|
|
{ |
|
92
|
|
|
/* Without a base path, only the last path element is used to map the controller/service */ |
|
93
|
|
|
$options = array( |
|
94
|
|
|
Config::KEY_CONTROLLERS => array( |
|
95
|
|
|
'TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController' |
|
96
|
|
|
) |
|
97
|
|
|
); |
|
98
|
|
|
$handler = new JsonRpcHandler($options); |
|
99
|
|
|
|
|
100
|
|
|
/* Ignores non-JSON POST'ed data */ |
|
101
|
|
|
$this->setRequestPayload($handler, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><document></document>"); |
|
102
|
|
|
$this->assertFalse($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
103
|
|
|
|
|
104
|
|
|
/* With a base path, a longer controller key can be used */ |
|
105
|
|
|
$options = array( |
|
106
|
|
|
JsonRpcHandler::KEY_BASE_PATH => 'x/y/z', |
|
107
|
|
|
Config::KEY_CONTROLLERS => array( |
|
108
|
|
|
'Test/TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController' |
|
109
|
|
|
) |
|
110
|
|
|
); |
|
111
|
|
|
$handler = new JsonRpcHandler($options); |
|
112
|
|
|
$this->setRequestPayload($handler, array()); |
|
113
|
|
|
$this->assertTrue($handler->isAppropriate("/x////y//z/Test/TestController.php", array(), array(), 'POST')); |
|
114
|
|
|
|
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
/** |
|
118
|
|
|
* Tests the edge cases of the JsonRpcHandler::performRoute method. |
|
119
|
|
|
*/ |
|
120
|
|
|
public function testPerformRoute() |
|
121
|
|
|
{ |
|
122
|
|
|
$options = array( |
|
123
|
|
|
Config::KEY_CONTROLLERS => array( |
|
124
|
|
|
'TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController' |
|
125
|
|
|
) |
|
126
|
|
|
); |
|
127
|
|
|
$handler = new JsonRpcHandler($options); |
|
128
|
|
|
|
|
129
|
|
|
/* Blows up safely for invalid requests. */ |
|
130
|
|
|
$this->setRequestPayload($handler, array('id' => '1')); |
|
131
|
|
|
$this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
132
|
|
|
$result = json_decode($handler->performRoute()); |
|
133
|
|
|
$this->assertFalse(isset($result->result)); |
|
134
|
|
|
$this->assertEquals(-32600, $result->error->code); |
|
135
|
|
|
|
|
136
|
|
|
/* Blows up safely for internal errors */ |
|
137
|
|
|
$this->setRequestPayload($handler, array('method' => 'genericExceptionAction', 'id' => '1')); |
|
138
|
|
|
$this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST')); |
|
139
|
|
|
$result = json_decode($handler->performRoute()); |
|
140
|
|
|
$this->assertFalse(isset($result->result)); |
|
141
|
|
|
$this->assertEquals('A generic exception.', $result->error->message); |
|
142
|
|
|
$this->assertTrue(-32000 >= $result->error->code); |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
/** |
|
146
|
|
|
* Tests the method JsonRpcHandler::handleException. |
|
147
|
|
|
*/ |
|
148
|
|
|
public function testHandleException() |
|
149
|
|
|
{ |
|
150
|
|
|
$handler = new JsonRpcHandler(array()); |
|
151
|
|
|
/* Any internal error should be wrapped in a JSON-RPC exception. */ |
|
152
|
|
|
$internal = $handler->handleException(new Exception("foo", 123)); |
|
153
|
|
|
$this->assertTrue($internal->error->code <= -32000); |
|
154
|
|
|
|
|
155
|
|
|
/* Any JSON-RPC exception (code <= 32000) can pass through */ |
|
156
|
|
|
$passthrough = $handler->handleException(new Exception("passthrough", -32001)); |
|
157
|
|
|
$this->assertEquals(-32001, $passthrough->error->code); |
|
158
|
|
|
$this->assertEquals("passthrough", $passthrough->error->message); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Tests that a request to a service that doesn't exist returns a 404 |
|
163
|
|
|
* response. |
|
164
|
|
|
* @expectedException Vectorface\SnappyRouter\Exception\ResourceNotFoundException |
|
165
|
|
|
* @expectedExceptionMessage No such service: nonexistent |
|
166
|
|
|
*/ |
|
167
|
|
|
public function testServiceNotFound() |
|
168
|
|
|
{ |
|
169
|
|
|
$handler = new JsonRpcHandler(array()); |
|
170
|
|
|
$this->setRequestPayload($handler, array('method' => 'someMethod', 'id' => '1')); |
|
171
|
|
|
$this->assertTrue($handler->isAppropriate('/nonexistent', array(), array(), 'POST')); |
|
172
|
|
|
$handler->performRoute(); |
|
173
|
|
|
} |
|
174
|
|
|
} |
|
175
|
|
|
|