JsonRpcHandlerTest   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 158
rs 10
c 0
b 0
f 0
wmc 7
lcom 1
cbo 2

6 Methods

Rating   Name   Duplication   Size   Complexity  
A setRequestPayload() 0 10 2
A synopsis() 0 48 1
A testIsAppropriate() 0 25 1
A testPerformRoute() 0 24 1
A testHandleException() 0 12 1
A testServiceNotFound() 0 7 1
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 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->assertCount(2, $handler->getRequests());
66
67
        $result = json_decode($handler->performRoute());
68
        $this->assertCount(2, $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->assertCount(1, $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
     * Tests the edge cases of the JsonRpcHandler::performRoute method.
118
     */
119
    public function testPerformRoute()
120
    {
121
        $options = array(
122
            Config::KEY_CONTROLLERS => array(
123
                'TestController' => 'Vectorface\\SnappyRouterTests\\Controller\\TestDummyController'
124
            )
125
        );
126
        $handler = new JsonRpcHandler($options);
127
128
        /* Blows up safely for invalid requests. */
129
        $this->setRequestPayload($handler, array('id' => '1'));
130
        $this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST'));
131
        $result = json_decode($handler->performRoute());
132
        $this->assertFalse(isset($result->result));
133
        $this->assertEquals(-32600, $result->error->code);
134
135
        /* Blows up safely for internal errors */
136
        $this->setRequestPayload($handler, array('method' => 'genericExceptionAction', 'id' => '1'));
137
        $this->assertTrue($handler->isAppropriate('/x/y/z/TestController', array(), array(), 'POST'));
138
        $result = json_decode($handler->performRoute());
139
        $this->assertFalse(isset($result->result));
140
        $this->assertEquals('A generic exception.', $result->error->message);
141
        $this->assertTrue(-32000 >= $result->error->code);
142
    }
143
144
    /**
145
     * Tests the method JsonRpcHandler::handleException.
146
     */
147
    public function testHandleException()
148
    {
149
        $handler = new JsonRpcHandler(array());
150
        /* Any internal error should be wrapped in a JSON-RPC exception. */
151
        $internal = $handler->handleException(new Exception("foo", 123));
152
        $this->assertTrue($internal->error->code <= -32000);
153
154
        /* Any JSON-RPC exception (code <= 32000) can pass through */
155
        $passthrough = $handler->handleException(new Exception("passthrough", -32001));
156
        $this->assertEquals(-32001, $passthrough->error->code);
157
        $this->assertEquals("passthrough", $passthrough->error->message);
158
    }
159
160
    /**
161
     * Tests that a request to a service that doesn't exist returns a 404
162
     * response.
163
     * @expectedException Vectorface\SnappyRouter\Exception\ResourceNotFoundException
164
     * @expectedExceptionMessage No such service: nonexistent
165
     */
166
    public function testServiceNotFound()
167
    {
168
        $handler = new JsonRpcHandler(array());
169
        $this->setRequestPayload($handler, array('method' => 'someMethod', 'id' => '1'));
170
        $this->assertTrue($handler->isAppropriate('/nonexistent', array(), array(), 'POST'));
171
        $handler->performRoute();
172
    }
173
}
174