|
297
|
1 |
# Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net> |
|
|
2 |
# |
|
|
3 |
# This file is part of Iro. |
|
|
4 |
# |
|
|
5 |
# Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
|
6 |
# this software and associated documentation files (the "Software"), to deal in |
|
|
7 |
# the Software without restriction, including without limitation the rights to use, |
|
|
8 |
# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
|
|
9 |
# #Software, and to permit persons to whom the Software is furnished to do so, |
|
|
10 |
# subject to the following conditions: |
|
|
11 |
# |
|
|
12 |
# The above copyright notice and this permission notice shall be included in |
|
|
13 |
# all copies or substantial portions of the Software. |
|
|
14 |
# |
|
|
15 |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
|
16 |
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
|
|
17 |
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
|
18 |
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
|
19 |
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
|
20 |
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
21 |
from twisted.trial import unittest |
|
|
22 |
|
|
|
23 |
from twisted.web.client import Agent, ResponseDone |
|
|
24 |
from twisted.internet import reactor |
|
|
25 |
from twisted.web.http_headers import Headers |
|
|
26 |
from twisted.python.failure import Failure |
|
|
27 |
from twisted.internet.protocol import Protocol |
|
|
28 |
from twisted.internet import defer |
|
|
29 |
|
|
|
30 |
from zope.interface import implements |
|
|
31 |
|
|
|
32 |
from twisted.internet.defer import succeed |
|
|
33 |
from twisted.web.iweb import IBodyProducer |
|
|
34 |
|
|
|
35 |
import json |
|
|
36 |
|
|
|
37 |
from twisted.web import server |
|
|
38 |
from txjsonrpc.web.jsonrpc import Fault |
|
|
39 |
|
|
|
40 |
from datetime import datetime |
|
|
41 |
|
|
|
42 |
from iro.model.schema import User, Offer, Userright, Job, Message |
|
|
43 |
|
|
|
44 |
from iro.view import jsonresource |
|
|
45 |
import iro.error as IroError |
|
|
46 |
|
|
|
47 |
from iro.test_helpers.dbtestcase import DBTestCase |
|
|
48 |
|
|
|
49 |
from iro.model import setEngine, setPool |
|
|
50 |
from iro.controller.pool import startPool, dbPool |
|
|
51 |
|
|
|
52 |
|
|
|
53 |
class StringProducer(object): |
|
|
54 |
implements(IBodyProducer) |
|
|
55 |
|
|
|
56 |
def __init__(self, body): |
|
|
57 |
self.body = json.dumps(body) |
|
|
58 |
self.length = len(self.body) |
|
|
59 |
|
|
|
60 |
def startProducing(self, consumer): |
|
|
61 |
consumer.write(self.body) |
|
|
62 |
return succeed(None) |
|
|
63 |
|
|
|
64 |
def pauseProducing(self): |
|
|
65 |
pass |
|
|
66 |
|
|
|
67 |
def stopProducing(self): |
|
|
68 |
pass |
|
|
69 |
|
|
|
70 |
class SimpleReceiver(Protocol): |
|
|
71 |
def __init__(self, d): |
|
|
72 |
self.d = d |
|
|
73 |
self.data = "" |
|
|
74 |
|
|
|
75 |
def dataReceived(self, data): |
|
|
76 |
self.data += data |
|
|
77 |
|
|
|
78 |
def connectionLost(self, reason): |
|
|
79 |
try: |
|
|
80 |
if reason.check(ResponseDone): |
|
|
81 |
self.d.callback(json.loads(self.data)) |
|
|
82 |
else: |
|
|
83 |
self.d.errback(reason) |
|
|
84 |
except: |
|
|
85 |
self.d.errback(Failure()) |
|
|
86 |
|
|
|
87 |
class JSONRPCTest(DBTestCase): |
|
|
88 |
"""tests for the jsonrpc interface""" |
|
|
89 |
ContentType = 'application/json' |
|
|
90 |
StringProducer = StringProducer |
|
|
91 |
def setUp(self): |
|
|
92 |
DBTestCase.setUp(self) |
|
|
93 |
|
|
|
94 |
setEngine(self.engine) |
|
|
95 |
startPool(reactor) |
|
|
96 |
setPool(dbPool) |
|
|
97 |
self.p = reactor.listenTCP(0, server.Site(jsonresource.JSONFactory()), |
|
|
98 |
interface="127.0.0.1") |
|
|
99 |
self.port = self.p.getHost().port |
|
|
100 |
self.agent = Agent(reactor) |
|
|
101 |
|
|
|
102 |
def tearDown(self): |
|
|
103 |
DBTestCase.tearDown(self) |
|
|
104 |
return self.p.stopListening() |
|
|
105 |
|
|
|
106 |
def proxy(self, method, data=None, **kargs): |
|
|
107 |
d = self.agent.request( |
|
|
108 |
'GET', |
|
|
109 |
'http://localhost:%d/%s'%(self.port, method), |
|
|
110 |
Headers({'Content-Type':[self.ContentType]}), |
|
|
111 |
self.StringProducer(data) if data else None, |
|
|
112 |
) |
|
|
113 |
|
|
|
114 |
def cbResponse(response): |
|
|
115 |
if kargs.has_key('code'): |
|
|
116 |
self.failUnlessEqual(response.code, kargs["code"]) |
|
|
117 |
if response.code not in [200,400,500]: |
|
|
118 |
raise Fault(response.code,'') |
|
|
119 |
self.failUnlessEqual(response.headers.getRawHeaders('Content-Type'), ['application/json']) |
|
|
120 |
d = defer.Deferred() |
|
|
121 |
response.deliverBody(SimpleReceiver(d)) |
|
|
122 |
def _(data): |
|
|
123 |
if data["status"]: |
|
|
124 |
return data["result"] |
|
|
125 |
else: |
|
|
126 |
raise Fault(data["error"]["code"],data["error"]["msg"]) |
|
|
127 |
|
|
|
128 |
d.addCallback(_) |
|
|
129 |
return d |
|
|
130 |
d.addCallback(cbResponse) |
|
|
131 |
return d |
|
|
132 |
|
|
|
133 |
|
|
|
134 |
def testListMethods(self): |
|
|
135 |
'''list of all offical Methods, that can be executed''' |
|
|
136 |
|
|
|
137 |
def cbMethods(ret): |
|
|
138 |
ret.sort() |
|
|
139 |
self.failUnlessEqual(ret, ['bill', 'defaultRoute', 'email', 'fax', 'listMethods', 'mail', 'routes', 'sms', 'status', 'telnumber']) |
|
|
140 |
|
|
|
141 |
d=self.proxy("listMethods", code=200) |
|
|
142 |
d.addCallback(cbMethods) |
|
|
143 |
return d |
|
|
144 |
|
|
|
145 |
def testApikey(self): |
|
|
146 |
''' test apikey''' |
|
|
147 |
with self.session() as session: |
|
|
148 |
session.add(User(name='test',apikey='abcdef123456789')) |
|
|
149 |
|
|
|
150 |
d = self.proxy('status', ['abcdef123456789'], code=200) |
|
|
151 |
d.addCallback(lambda ret: self.failUnlessEqual(ret,{})) |
|
|
152 |
|
|
|
153 |
return d |
|
|
154 |
|
|
|
155 |
def testStatus(self): |
|
|
156 |
''' test the status function''' |
|
|
157 |
with self.session() as session: |
|
|
158 |
u = User(name='test',apikey='abcdef123456789') |
|
|
159 |
session.add(u) |
|
|
160 |
j = Job(info='info', status="started") |
|
|
161 |
j.user=u |
|
|
162 |
session.add(j) |
|
|
163 |
session.commit() |
|
|
164 |
jid=j.id |
|
|
165 |
status = {str(jid):{"status":"started"}} |
|
|
166 |
args=[('abcdef123456789',jid), |
|
|
167 |
('abcdef123456789',), |
|
|
168 |
('abcdef123456789', '', 'false'), |
|
|
169 |
('abcdef123456789', '', 0), |
|
|
170 |
('abcdef123456789', '', 0), |
|
|
171 |
{"id":jid, "user":'abcdef123456789'}, |
|
|
172 |
] |
|
|
173 |
dl=[] |
|
|
174 |
for a in args: |
|
|
175 |
d = self.proxy('status', a, code=200) |
|
|
176 |
d.addCallback(lambda ret: self.failUnlessEqual(ret,status)) |
|
|
177 |
dl.append(d) |
|
|
178 |
|
|
|
179 |
def f(exc): |
|
|
180 |
jnf = IroError.JobNotFound() |
|
|
181 |
self.failUnlessEqual(exc.faultCode, jnf.code) |
|
|
182 |
self.failUnlessEqual(exc.faultString, jnf.msg) |
|
|
183 |
d = self.proxy('status', ['abcdef123456789', jid+1], code=500) |
|
|
184 |
d = self.assertFailure(d, Fault) |
|
|
185 |
d.addCallback(f) |
|
|
186 |
dl.append(d) |
|
|
187 |
|
|
|
188 |
return defer.DeferredList(dl, fireOnOneErrback=True) |
|
|
189 |
|
|
|
190 |
def testNoSuchUser(self): |
|
|
191 |
'''a unknown user should raise a UserNotNound Exception |
|
|
192 |
bewcause xmlrpc only has a Fault exception this Exception has to be deliverd through a xmlrpclib.Fault Exception''' |
|
|
193 |
def f(exc): |
|
|
194 |
unf = IroError.UserNotFound() |
|
|
195 |
self.failUnlessEqual(exc.faultCode, unf.code) |
|
|
196 |
self.failUnlessEqual(exc.faultString, unf.msg) |
|
|
197 |
d = self.proxy('status', ['abcdef123456789'], code=500) |
|
|
198 |
d = self.assertFailure(d, Fault) |
|
|
199 |
d.addCallback(f) |
|
|
200 |
return d |
|
|
201 |
|
|
|
202 |
def testNoSuchMethod(self): |
|
|
203 |
'''a unknown mothod should raise a Exception ''' |
|
|
204 |
def f(exc): |
|
|
205 |
self.failUnlessEqual(exc.faultCode, 404) |
|
|
206 |
self.failUnlessEqual(exc.faultString, '') |
|
|
207 |
d = self.proxy('nosuchmethod', code=404) |
|
|
208 |
d = self.assertFailure(d, Fault) |
|
|
209 |
d.addCallback(f) |
|
|
210 |
return d |
|
|
211 |
|
|
|
212 |
def testValidationFault(self): |
|
|
213 |
'''a validate Exception should be translated to a xmlrpclib.Fault.''' |
|
|
214 |
def f(exc): |
|
|
215 |
self.failUnlessEqual(exc.faultCode, 700) |
|
|
216 |
self.failUnlessEqual(exc.faultString, "Validation of 'apikey' failed.") |
|
|
217 |
d = self.proxy('status',{'user':'xxx'}, code=400) |
|
|
218 |
d = self.assertFailure(d, Fault) |
|
|
219 |
d.addCallback(f) |
|
|
220 |
return d |
|
|
221 |
|
|
|
222 |
@defer.inlineCallbacks |
|
|
223 |
def testRoutes(self): |
|
|
224 |
'''test the route function''' |
|
|
225 |
with self.session() as session: |
|
|
226 |
u=User(name='test',apikey='abcdef123456789') |
|
|
227 |
o=Offer(name="sipgate_basic", provider="sipgate", route="basic", typ="sms") |
|
|
228 |
u.rights.append(Userright(o)) |
|
|
229 |
session.add(u) |
|
|
230 |
|
|
|
231 |
x = yield self.proxy('routes',['abcdef123456789','sms'], code=200) |
|
|
232 |
self.failUnlessEqual(x,['sipgate_basic']) |
|
|
233 |
|
|
|
234 |
def f(exc): |
|
|
235 |
self.failUnlessEqual(exc.faultCode, 700) |
|
|
236 |
self.failUnlessEqual(exc.faultString, "Typ fax is not valid.") |
|
|
237 |
d = self.proxy('routes',['abcdef123456789','fax'], code=400) |
|
|
238 |
d = self.assertFailure(d, Fault) |
|
|
239 |
d.addCallback(f) |
|
|
240 |
yield d |
|
|
241 |
|
|
|
242 |
with self.session() as session: |
|
|
243 |
o=Offer(name="sipgate_plus", provider="sipgate", route="plus", typ="sms") |
|
|
244 |
u = session.query(User).filter_by(name="test").first() |
|
|
245 |
u.rights.append(Userright(o)) |
|
|
246 |
o=Offer(name="faxde", provider="faxde", route="", typ="fax") |
|
|
247 |
session.add(o) |
|
|
248 |
session.commit() |
|
|
249 |
|
|
|
250 |
x = yield self.proxy('routes',['abcdef123456789','sms'], code=200) |
|
|
251 |
self.failUnlessEqual(x, ['sipgate_basic','sipgate_plus']) |
|
|
252 |
x = yield self.proxy('routes', ['abcdef123456789','fax'], code=200) |
|
|
253 |
self.failUnlessEqual(x, []) |
|
|
254 |
|
|
|
255 |
with self.session() as session: |
|
|
256 |
u = session.query(User).filter_by(name="test").first() |
|
|
257 |
u.rights.append(Userright(o)) |
|
|
258 |
|
|
|
259 |
x = yield self.proxy('routes',['abcdef123456789','sms'], code=200) |
|
|
260 |
self.failUnlessEqual(x, ['sipgate_basic','sipgate_plus']) |
|
|
261 |
x = yield self.proxy('routes', ['abcdef123456789','fax'], code=200) |
|
|
262 |
self.failUnlessEqual(x, ['faxde']) |
|
|
263 |
|
|
|
264 |
def testDefaultRoutes(self): |
|
|
265 |
'''test the defaultRoute function''' |
|
|
266 |
with self.session() as session: |
|
|
267 |
u=User(name='test',apikey='abcdef123456789') |
|
|
268 |
o=Offer(name="sipgate_basic", provider="sipgate", route="basic", typ="sms") |
|
|
269 |
u.rights.append(Userright(o,True)) |
|
|
270 |
o=Offer(name="sipgate_plus", provider="sipgate", route="plus", typ="sms") |
|
|
271 |
u.rights.append(Userright(o)) |
|
|
272 |
session.add(u) |
|
|
273 |
d = self.proxy('defaultRoute', ['abcdef123456789','sms'], code=200) |
|
|
274 |
d.addCallback(lambda x: self.failUnlessEqual(x,['sipgate_basic'])) |
|
|
275 |
|
|
|
276 |
return d |
|
|
277 |
|
|
|
278 |
def testTelnumbers(self): |
|
|
279 |
'''test the telefon validator''' |
|
|
280 |
dl = [] |
|
|
281 |
d = self.proxy('telnumber',[["0123/456(78)","+4912346785433","00123435456-658"]], code=200) |
|
|
282 |
d.addCallback(lambda x: self.failUnlessEqual(x,True)) |
|
|
283 |
dl.append(d) |
|
|
284 |
|
|
|
285 |
invalid=['xa','+1','1-23',';:+0','0123'] |
|
|
286 |
def f(exc): |
|
|
287 |
self.failUnlessEqual(exc.faultCode, 701) |
|
|
288 |
self.failUnlessEqual(exc.faultString, "No valid telnumber: '%s'" % invalid[0]) |
|
|
289 |
d = self.proxy('telnumber',[['01234']+invalid], code=400) |
|
|
290 |
d = self.assertFailure(d, Fault) |
|
|
291 |
d.addCallback(f) |
|
|
292 |
dl.append(d) |
|
|
293 |
|
|
|
294 |
return defer.DeferredList(dl, fireOnOneErrback=True) |
|
|
295 |
|
|
|
296 |
def testVaildEmail(self): |
|
|
297 |
'''test vaild email adresses (got from wikipedia)''' |
|
|
298 |
validmails=["niceandsimple@example.com"] |
|
|
299 |
d = self.proxy('email', {"recipients":validmails}, code=200) |
|
|
300 |
d.addCallback(lambda x: self.failUnlessEqual(x,True)) |
|
|
301 |
return d |
|
|
302 |
|
|
|
303 |
def testInvaildEmail(self): |
|
|
304 |
'''test invaild email adresses (got from wikipedia)''' |
|
|
305 |
invalid=["Abc.example.com",'foo@t.de'] |
|
|
306 |
def f(exc): |
|
|
307 |
self.failUnlessEqual(exc.faultCode, 702) |
|
|
308 |
self.failUnlessEqual(exc.faultString, "No valid email: '%s'" % invalid[0]) |
|
|
309 |
d = self.proxy('email', [invalid], code=400) |
|
|
310 |
d = self.assertFailure(d, Fault) |
|
|
311 |
d.addCallback(f) |
|
|
312 |
return d |
|
|
313 |
|
|
|
314 |
def testBill(self): |
|
|
315 |
'''test bill function''' |
|
|
316 |
apikey='abcdef123456789' |
|
|
317 |
with self.session() as session: |
|
|
318 |
u=User(name='test',apikey=apikey) |
|
|
319 |
session.add(u) |
|
|
320 |
d = self.proxy('bill', {"user":apikey}, code=200) |
|
|
321 |
d.addCallback(lambda x: self.failUnlessEqual(x,{'total':{'price':0.0,'anz':0}})) |
|
|
322 |
|
|
|
323 |
return d |
|
|
324 |
|
|
|
325 |
def testBillWithPrice(self): |
|
|
326 |
apikey='abcdef123456789' |
|
|
327 |
with self.session() as session: |
|
|
328 |
u=User(name='test',apikey=apikey) |
|
|
329 |
session.add(u) |
|
|
330 |
o = Offer(name='sipgate_basic',provider="sipgate",route="basic",typ="sms") |
|
|
331 |
u.rights.append(Userright(o)) |
|
|
332 |
j = Job(info='i',status='sended') |
|
|
333 |
j.messages.append(Message(recipient='0123456789', isBilled=False, date=datetime.now() , price=0.4, offer=o)) |
|
|
334 |
u.jobs.append(j) |
|
|
335 |
|
|
|
336 |
j = Job(info='a',status='sended') |
|
|
337 |
j.messages.append(Message(recipient='0123456789', isBilled=False, date=datetime.now(), price=0.4, offer=o)) |
|
|
338 |
u.jobs.append(j) |
|
|
339 |
|
|
|
340 |
def f(ret): |
|
|
341 |
self.failUnlessEqual(ret['total'],{'price':0.8,'anz':2}) |
|
|
342 |
self.failUnlessEqual(ret['sipgate_basic'], |
|
|
343 |
{'price':0.8,'anz':2, |
|
|
344 |
'info':{'i':{'price':0.4,'anz':1}, |
|
|
345 |
'a':{'price':0.4,'anz':1}, |
|
|
346 |
} |
|
|
347 |
}) |
|
|
348 |
|
|
|
349 |
d = self.proxy('bill', [apikey], code=200) |
|
|
350 |
d.addCallback(f) |
|
|
351 |
return d |
|
|
352 |
|
|
|
353 |
if __name__ == '__main__': |
|
|
354 |
unittest.main() |