# # Copyright (c) 2011 Edward Langley # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # Neither the name of the project's author nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # from twisted.trial import unittest import StringIO import mock import jsonrpc.server import jsonrpc.jsonutil from twisted.web.test.test_web import DummyRequest from twisted.internet.defer import succeed from twisted.web.static import server def _render(resource, request): result = resource.render(request) if isinstance(result, str): request.write(result) request.finish() return succeed(None) elif result is server.NOT_DONE_YET: if request.finished: return succeed(None) else: return request.notifyFinish() else: raise ValueError("Unexpected return value: %r" % (result,)) class SimpleEventHandler(jsonrpc.server.ServerEvents): def log(self, result, request): pass def findmethod(self, method): if method in set(['echo', 'add']): return getattr(self, method) def add(self, a,b): return a+b def echo(self, v): return v class TestJSONRPCServer(unittest.TestCase): def setUp(self): self.id_ = 'an_id' self.param = "some data" def test_invalid_data(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO(' {"v": %s}, "method": "echo"}' % (jsonrpc.jsonutil.encode(self.param))) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data == {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": None} return d def test_wrongversion(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.1", "params": %s, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_)) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_} return d def test_invalidmethodname(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": 0, "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_)) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": self.id_} return d def test_missingmethod(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": "non_existent", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_)) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data == {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Procedure not found."}, "id": self.id_} return d def test_simplecall(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": %s, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode([self.param]), self.id_)) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data['id'] == self.id_ assert data['result'] == self.param return d def test_notify(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": {"v": %s}, "method": "echo"}' % (jsonrpc.jsonutil.encode(self.param))) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 0 return d def test_kwcall(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": {"v": %s}, "method": "echo", "id": "%s"}' % (jsonrpc.jsonutil.encode(self.param), self.id_)) d = _render(resource, request) @d.addCallback def rendered(ignored): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data['id'] == self.id_ assert data['result'] == self.param return d def test_err(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.getCookie = mock.Mock() request.content = StringIO.StringIO('{"jsonrpc": "2.0", "params": [1, "sss"], "method": "add", "id": "%s"}' % self.id_) d = _render(resource, request) @d.addCallback def rendered(ignored, *a): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert data['id'] == self.id_ assert data.get('error', False) return rendered def test_batchcall(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.content = StringIO.StringIO( '[{"jsonrpc": "2.0", "params": [1, 2], "method": "add", "id": "1"},' '{"jsonrpc": "2.0", "params": {"a": 3, "b": 2}, "method": "add", "id": "2"}]' ) request.getCookie = mock.Mock() d = _render(resource, request) @d.addCallback def rendered(ignored, *a): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert len(data) == 2 assert set(x['id'] for x in data) == set("12") assert set(x['result'] for x in data) == set([3,5]) assert not any(x.get('error', False) for x in data) return rendered def test_batchcall_1err(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.content = StringIO.StringIO( '[{"jsonrpc": "2.0", "params": [1, 2], "method": "add", "id": "1"},' '{"jsonrpc": "2.0", "params": {"a": "3", "b": 2}, "method": "add", "id": "2"}]' ) request.getCookie = mock.Mock() d = _render(resource, request) @d.addCallback def rendered(ignored, *a): assert len(request.written) == 1 data = jsonrpc.jsonutil.decode(request.written[0]) assert len(data) == 2 assert set(x['id'] for x in data) == set("12") assert set(x.get('result', False) for x in data) == set([3,False]) assert len(filter(None, [x.get('error') for x in data])) == 1 return rendered def test_batchcall_emptylist(self): resource = jsonrpc.server.JSON_RPC() resource.customize(SimpleEventHandler) request = DummyRequest(['']) request.content = StringIO.StringIO('[]') request.getCookie = mock.Mock() d = _render(resource, request) @d.addCallback def rendered(ignored, *a): data = jsonrpc.jsonutil.decode(request.written[0]) assert data == {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": None} return rendered