17.12
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Crossbar.io Technologies GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from autobahn.wamp import cryptobox
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
@unittest.skipIf(not cryptobox.HAS_CRYPTOBOX, 'no cryptobox support present')
|
||||
class TestCryptoBox(unittest.TestCase):
|
||||
|
||||
def test_create_keyring(self):
|
||||
kr = cryptobox.KeyRing()
|
||||
assert kr
|
||||
@@ -0,0 +1,127 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Crossbar.io Technologies GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from mock import Mock
|
||||
|
||||
import txaio
|
||||
|
||||
if os.environ.get('USE_TWISTED', False):
|
||||
txaio.use_twisted()
|
||||
elif os.environ.get('USE_ASYNCIO', False):
|
||||
txaio.use_asyncio()
|
||||
else:
|
||||
raise Exception('no networking framework selected')
|
||||
|
||||
from autobahn.wamp.cryptosign import _makepad, HAS_CRYPTOSIGN
|
||||
from autobahn.wamp import types
|
||||
from autobahn.wamp.auth import create_authenticator
|
||||
|
||||
if HAS_CRYPTOSIGN:
|
||||
from autobahn.wamp.cryptosign import SigningKey
|
||||
from nacl.encoding import HexEncoder
|
||||
|
||||
import tempfile
|
||||
|
||||
import unittest
|
||||
|
||||
keybody = '''-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACAa38i/4dNWFuZN/72QAJbyOwZvkUyML/u2b2B1uW4RbQAAAJj4FLyB+BS8
|
||||
gQAAAAtzc2gtZWQyNTUxOQAAACAa38i/4dNWFuZN/72QAJbyOwZvkUyML/u2b2B1uW4RbQ
|
||||
AAAEBNV9l6aPVVaWYgpthJwM5YJWhRjXKet1PcfHMt4oBFEBrfyL/h01YW5k3/vZAAlvI7
|
||||
Bm+RTIwv+7ZvYHW5bhFtAAAAFXNvbWV1c2VyQGZ1bmt0aGF0LmNvbQ==
|
||||
-----END OPENSSH PRIVATE KEY-----'''
|
||||
|
||||
pubkey = '''ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVp3hjHwIQyEladzd8mFcf0YSXcmyKS3qMLB7VqTQKm someuser@example.com
|
||||
'''
|
||||
|
||||
|
||||
@unittest.skipIf(not HAS_CRYPTOSIGN, 'nacl library not present')
|
||||
class TestAuth(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.key = SigningKey.from_ssh_data(keybody)
|
||||
self.privkey_hex = self.key._key.encode(encoder=HexEncoder)
|
||||
m = hashlib.sha256()
|
||||
m.update("some TLS message".encode())
|
||||
self.channel_id = m.digest()
|
||||
|
||||
def test_valid(self):
|
||||
session = Mock()
|
||||
session._transport.get_channel_id = Mock(return_value=self.channel_id)
|
||||
challenge = types.Challenge(u"ticket", dict(challenge="ff" * 32))
|
||||
signed = yield self.key.sign_challenge(session, challenge)
|
||||
self.assertEqual(
|
||||
u'9b6f41540c9b95b4b7b281c3042fa9c54cef43c842d62ea3fd6030fcb66e70b3e80d49d44c29d1635da9348d02ec93f3ed1ef227dfb59a07b580095c2b82f80f9d16ca518aa0c2b707f2b2a609edeca73bca8dd59817a633f35574ac6fd80d00',
|
||||
signed.result,
|
||||
)
|
||||
|
||||
def test_authenticator(self):
|
||||
authenticator = create_authenticator(
|
||||
u"cryptosign",
|
||||
authid="someone",
|
||||
privkey=self.privkey_hex,
|
||||
)
|
||||
session = Mock()
|
||||
session._transport.get_channel_id = Mock(return_value=self.channel_id)
|
||||
challenge = types.Challenge(u"cryptosign", dict(challenge="ff" * 32))
|
||||
reply = yield authenticator.on_challenge(session, challenge)
|
||||
self.assertEqual(
|
||||
reply.result,
|
||||
u'9b6f41540c9b95b4b7b281c3042fa9c54cef43c842d62ea3fd6030fcb66e70b3e80d49d44c29d1635da9348d02ec93f3ed1ef227dfb59a07b580095c2b82f80f9d16ca518aa0c2b707f2b2a609edeca73bca8dd59817a633f35574ac6fd80d00',
|
||||
)
|
||||
|
||||
|
||||
class TestKey(unittest.TestCase):
|
||||
|
||||
def test_pad(self):
|
||||
self.assertEqual(_makepad(0), '')
|
||||
self.assertEqual(_makepad(2), '\x01\x02')
|
||||
self.assertEqual(_makepad(3), '\x01\x02\x03')
|
||||
|
||||
@unittest.skipIf(not HAS_CRYPTOSIGN, 'nacl library not present')
|
||||
def test_key(self):
|
||||
with tempfile.NamedTemporaryFile('w+t') as fp:
|
||||
fp.write(keybody)
|
||||
fp.seek(0)
|
||||
|
||||
key = SigningKey.from_ssh_key(fp.name)
|
||||
self.assertEqual(key.public_key(), '1adfc8bfe1d35616e64dffbd900096f23b066f914c8c2ffbb66f6075b96e116d')
|
||||
|
||||
@unittest.skipIf(not HAS_CRYPTOSIGN, 'nacl library not present')
|
||||
def test_pubkey(self):
|
||||
with tempfile.NamedTemporaryFile('w+t') as fp:
|
||||
fp.write(pubkey)
|
||||
fp.seek(0)
|
||||
|
||||
key = SigningKey.from_ssh_key(fp.name)
|
||||
self.assertEqual(key.public_key(), '9569de18c7c0843212569dcddf2615c7f46125dc9b2292dea30b07b56a4d02a6')
|
||||
self.assertEqual(key.comment(), 'someuser@example.com')
|
||||
@@ -0,0 +1,580 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Crossbar.io Technologies GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from autobahn import wamp
|
||||
from autobahn.wamp.uri import Pattern, RegisterOptions, SubscribeOptions
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestUris(unittest.TestCase):
|
||||
|
||||
def test_invalid_uris(self):
|
||||
for u in [u"",
|
||||
u"com.myapp.<product:foo>.update",
|
||||
u"com.myapp.<123:int>.update",
|
||||
u"com.myapp.<:product>.update",
|
||||
u"com.myapp.<product:>.update",
|
||||
u"com.myapp.<int:>.update",
|
||||
]:
|
||||
self.assertRaises(Exception, Pattern, u, Pattern.URI_TARGET_ENDPOINT)
|
||||
|
||||
def test_valid_uris(self):
|
||||
for u in [u"com.myapp.proc1",
|
||||
u"123",
|
||||
u"com.myapp.<product:int>.update",
|
||||
u"com.myapp.<category:string>.<subcategory>.list"
|
||||
u"com.myapp.something..update"
|
||||
]:
|
||||
p = Pattern(u, Pattern.URI_TARGET_ENDPOINT)
|
||||
self.assertIsInstance(p, Pattern)
|
||||
|
||||
def test_parse_uris(self):
|
||||
tests = [
|
||||
(u"com.myapp.<product:int>.update", [
|
||||
(u"com.myapp.0.update", {u'product': 0}),
|
||||
(u"com.myapp.123456.update", {u'product': 123456}),
|
||||
(u"com.myapp.aaa.update", None),
|
||||
(u"com.myapp..update", None),
|
||||
(u"com.myapp.0.delete", None),
|
||||
]
|
||||
),
|
||||
(u"com.myapp.<product:string>.update", [
|
||||
(u"com.myapp.box.update", {u'product': u'box'}),
|
||||
(u"com.myapp.123456.update", {u'product': u'123456'}),
|
||||
(u"com.myapp..update", None),
|
||||
]
|
||||
),
|
||||
(u"com.myapp.<product>.update", [
|
||||
(u"com.myapp.0.update", {u'product': u'0'}),
|
||||
(u"com.myapp.abc.update", {u'product': u'abc'}),
|
||||
(u"com.myapp..update", None),
|
||||
]
|
||||
),
|
||||
(u"com.myapp.<category:string>.<subcategory:string>.list", [
|
||||
(u"com.myapp.cosmetic.shampoo.list", {u'category': u'cosmetic', u'subcategory': u'shampoo'}),
|
||||
(u"com.myapp...list", None),
|
||||
(u"com.myapp.cosmetic..list", None),
|
||||
(u"com.myapp..shampoo.list", None),
|
||||
]
|
||||
)
|
||||
]
|
||||
for test in tests:
|
||||
pat = Pattern(test[0], Pattern.URI_TARGET_ENDPOINT)
|
||||
for ptest in test[1]:
|
||||
uri = ptest[0]
|
||||
kwargs_should = ptest[1]
|
||||
if kwargs_should is not None:
|
||||
args_is, kwargs_is = pat.match(uri)
|
||||
self.assertEqual(kwargs_is, kwargs_should)
|
||||
else:
|
||||
self.assertRaises(Exception, pat.match, uri)
|
||||
|
||||
|
||||
class TestDecorators(unittest.TestCase):
|
||||
|
||||
def test_decorate_endpoint(self):
|
||||
|
||||
@wamp.register(u"com.calculator.square")
|
||||
def square(_):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(square, '_wampuris'))
|
||||
self.assertTrue(type(square._wampuris) == list)
|
||||
self.assertEqual(len(square._wampuris), 1)
|
||||
self.assertIsInstance(square._wampuris[0], Pattern)
|
||||
self.assertTrue(square._wampuris[0].is_endpoint())
|
||||
self.assertFalse(square._wampuris[0].is_handler())
|
||||
self.assertFalse(square._wampuris[0].is_exception())
|
||||
self.assertEqual(square._wampuris[0].uri(), u"com.calculator.square")
|
||||
self.assertEqual(square._wampuris[0]._type, Pattern.URI_TYPE_EXACT)
|
||||
|
||||
@wamp.register(u"com.myapp.product.<product:int>.update")
|
||||
def update_product(product=None, label=None):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(update_product, '_wampuris'))
|
||||
self.assertTrue(type(update_product._wampuris) == list)
|
||||
self.assertEqual(len(update_product._wampuris), 1)
|
||||
self.assertIsInstance(update_product._wampuris[0], Pattern)
|
||||
self.assertTrue(update_product._wampuris[0].is_endpoint())
|
||||
self.assertFalse(update_product._wampuris[0].is_handler())
|
||||
self.assertFalse(update_product._wampuris[0].is_exception())
|
||||
self.assertEqual(update_product._wampuris[0].uri(), u"com.myapp.product.<product:int>.update")
|
||||
self.assertEqual(update_product._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.register(u"com.myapp.<category:string>.<cid:int>.update")
|
||||
def update(category=None, cid=None):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(update, '_wampuris'))
|
||||
self.assertTrue(type(update._wampuris) == list)
|
||||
self.assertEqual(len(update._wampuris), 1)
|
||||
self.assertIsInstance(update._wampuris[0], Pattern)
|
||||
self.assertTrue(update._wampuris[0].is_endpoint())
|
||||
self.assertFalse(update._wampuris[0].is_handler())
|
||||
self.assertFalse(update._wampuris[0].is_exception())
|
||||
self.assertEqual(update._wampuris[0].uri(), u"com.myapp.<category:string>.<cid:int>.update")
|
||||
self.assertEqual(update._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.register(u"com.myapp.circle.<name:string>",
|
||||
RegisterOptions(match=u"wildcard", details_arg="details"))
|
||||
def circle(name=None, details=None):
|
||||
""" Do nothing. """
|
||||
|
||||
self.assertTrue(hasattr(circle, '_wampuris'))
|
||||
self.assertTrue(type(circle._wampuris) == list)
|
||||
self.assertEqual(len(circle._wampuris), 1)
|
||||
self.assertIsInstance(circle._wampuris[0], Pattern)
|
||||
self.assertIsInstance(circle._wampuris[0].options, RegisterOptions)
|
||||
self.assertEqual(circle._wampuris[0].options.match, u"wildcard")
|
||||
self.assertEqual(circle._wampuris[0].options.details_arg, "details")
|
||||
self.assertTrue(circle._wampuris[0].is_endpoint())
|
||||
self.assertFalse(circle._wampuris[0].is_handler())
|
||||
self.assertFalse(circle._wampuris[0].is_exception())
|
||||
self.assertEqual(circle._wampuris[0].uri(), u"com.myapp.circle.<name:string>")
|
||||
self.assertEqual(circle._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.register(u"com.myapp.something..update",
|
||||
RegisterOptions(match=u"wildcard", details_arg="details"))
|
||||
def something(dynamic=None, details=None):
|
||||
""" Do nothing. """
|
||||
self.assertTrue(hasattr(something, '_wampuris'))
|
||||
self.assertTrue(type(something._wampuris) == list)
|
||||
self.assertEqual(len(something._wampuris), 1)
|
||||
self.assertIsInstance(something._wampuris[0], Pattern)
|
||||
self.assertIsInstance(something._wampuris[0].options, RegisterOptions)
|
||||
self.assertEqual(something._wampuris[0].options.match, u"wildcard")
|
||||
self.assertEqual(something._wampuris[0].options.details_arg, "details")
|
||||
self.assertTrue(something._wampuris[0].is_endpoint())
|
||||
self.assertFalse(something._wampuris[0].is_handler())
|
||||
self.assertFalse(something._wampuris[0].is_exception())
|
||||
self.assertEqual(something._wampuris[0].uri(), u"com.myapp.something..update")
|
||||
self.assertEqual(something._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
def test_decorate_handler(self):
|
||||
|
||||
@wamp.subscribe(u"com.myapp.on_shutdown")
|
||||
def on_shutdown():
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(on_shutdown, '_wampuris'))
|
||||
self.assertTrue(type(on_shutdown._wampuris) == list)
|
||||
self.assertEqual(len(on_shutdown._wampuris), 1)
|
||||
self.assertIsInstance(on_shutdown._wampuris[0], Pattern)
|
||||
self.assertFalse(on_shutdown._wampuris[0].is_endpoint())
|
||||
self.assertTrue(on_shutdown._wampuris[0].is_handler())
|
||||
self.assertFalse(on_shutdown._wampuris[0].is_exception())
|
||||
self.assertEqual(on_shutdown._wampuris[0].uri(), u"com.myapp.on_shutdown")
|
||||
self.assertEqual(on_shutdown._wampuris[0]._type, Pattern.URI_TYPE_EXACT)
|
||||
|
||||
@wamp.subscribe(u"com.myapp.product.<product:int>.on_update")
|
||||
def on_product_update(product=None, label=None):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(on_product_update, '_wampuris'))
|
||||
self.assertTrue(type(on_product_update._wampuris) == list)
|
||||
self.assertEqual(len(on_product_update._wampuris), 1)
|
||||
self.assertIsInstance(on_product_update._wampuris[0], Pattern)
|
||||
self.assertFalse(on_product_update._wampuris[0].is_endpoint())
|
||||
self.assertTrue(on_product_update._wampuris[0].is_handler())
|
||||
self.assertFalse(on_product_update._wampuris[0].is_exception())
|
||||
self.assertEqual(on_product_update._wampuris[0].uri(), u"com.myapp.product.<product:int>.on_update")
|
||||
self.assertEqual(on_product_update._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.subscribe(u"com.myapp.<category:string>.<cid:int>.on_update")
|
||||
def on_update(category=None, cid=None, label=None):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(on_update, '_wampuris'))
|
||||
self.assertTrue(type(on_update._wampuris) == list)
|
||||
self.assertEqual(len(on_update._wampuris), 1)
|
||||
self.assertIsInstance(on_update._wampuris[0], Pattern)
|
||||
self.assertFalse(on_update._wampuris[0].is_endpoint())
|
||||
self.assertTrue(on_update._wampuris[0].is_handler())
|
||||
self.assertFalse(on_update._wampuris[0].is_exception())
|
||||
self.assertEqual(on_update._wampuris[0].uri(), u"com.myapp.<category:string>.<cid:int>.on_update")
|
||||
self.assertEqual(on_update._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.subscribe(u"com.myapp.on.<event:string>",
|
||||
SubscribeOptions(match=u"wildcard", details_arg="details"))
|
||||
def on_event(event=None, details=None):
|
||||
""" Do nothing. """
|
||||
|
||||
self.assertTrue(hasattr(on_event, '_wampuris'))
|
||||
self.assertTrue(type(on_event._wampuris) == list)
|
||||
self.assertEqual(len(on_event._wampuris), 1)
|
||||
self.assertIsInstance(on_event._wampuris[0], Pattern)
|
||||
self.assertIsInstance(on_event._wampuris[0].options, SubscribeOptions)
|
||||
self.assertEqual(on_event._wampuris[0].options.match, u"wildcard")
|
||||
self.assertEqual(on_event._wampuris[0].options.details_arg, "details")
|
||||
self.assertFalse(on_event._wampuris[0].is_endpoint())
|
||||
self.assertTrue(on_event._wampuris[0].is_handler())
|
||||
self.assertFalse(on_event._wampuris[0].is_exception())
|
||||
self.assertEqual(on_event._wampuris[0].uri(), u"com.myapp.on.<event:string>")
|
||||
self.assertEqual(on_event._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
def test_decorate_exception(self):
|
||||
|
||||
@wamp.error(u"com.myapp.error")
|
||||
class AppError(Exception):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(AppError, '_wampuris'))
|
||||
self.assertTrue(type(AppError._wampuris) == list)
|
||||
self.assertEqual(len(AppError._wampuris), 1)
|
||||
self.assertIsInstance(AppError._wampuris[0], Pattern)
|
||||
self.assertFalse(AppError._wampuris[0].is_endpoint())
|
||||
self.assertFalse(AppError._wampuris[0].is_handler())
|
||||
self.assertTrue(AppError._wampuris[0].is_exception())
|
||||
self.assertEqual(AppError._wampuris[0].uri(), u"com.myapp.error")
|
||||
self.assertEqual(AppError._wampuris[0]._type, Pattern.URI_TYPE_EXACT)
|
||||
|
||||
@wamp.error(u"com.myapp.product.<product:int>.product_inactive")
|
||||
class ProductInactiveError(Exception):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(ProductInactiveError, '_wampuris'))
|
||||
self.assertTrue(type(ProductInactiveError._wampuris) == list)
|
||||
self.assertEqual(len(ProductInactiveError._wampuris), 1)
|
||||
self.assertIsInstance(ProductInactiveError._wampuris[0], Pattern)
|
||||
self.assertFalse(ProductInactiveError._wampuris[0].is_endpoint())
|
||||
self.assertFalse(ProductInactiveError._wampuris[0].is_handler())
|
||||
self.assertTrue(ProductInactiveError._wampuris[0].is_exception())
|
||||
self.assertEqual(ProductInactiveError._wampuris[0].uri(), u"com.myapp.product.<product:int>.product_inactive")
|
||||
self.assertEqual(ProductInactiveError._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
@wamp.error(u"com.myapp.<category:string>.<product:int>.inactive")
|
||||
class ObjectInactiveError(Exception):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(ObjectInactiveError, '_wampuris'))
|
||||
self.assertTrue(type(ObjectInactiveError._wampuris) == list)
|
||||
self.assertEqual(len(ObjectInactiveError._wampuris), 1)
|
||||
self.assertIsInstance(ObjectInactiveError._wampuris[0], Pattern)
|
||||
self.assertFalse(ObjectInactiveError._wampuris[0].is_endpoint())
|
||||
self.assertFalse(ObjectInactiveError._wampuris[0].is_handler())
|
||||
self.assertTrue(ObjectInactiveError._wampuris[0].is_exception())
|
||||
self.assertEqual(ObjectInactiveError._wampuris[0].uri(), u"com.myapp.<category:string>.<product:int>.inactive")
|
||||
self.assertEqual(ObjectInactiveError._wampuris[0]._type, Pattern.URI_TYPE_WILDCARD)
|
||||
|
||||
def test_match_decorated_endpoint(self):
|
||||
|
||||
@wamp.register(u"com.calculator.square")
|
||||
def square(x):
|
||||
return x
|
||||
|
||||
args, kwargs = square._wampuris[0].match(u"com.calculator.square")
|
||||
self.assertEqual(square(666, **kwargs), 666)
|
||||
|
||||
@wamp.register(u"com.myapp.product.<product:int>.update")
|
||||
def update_product(product=None, label=None):
|
||||
return product, label
|
||||
|
||||
args, kwargs = update_product._wampuris[0].match(u"com.myapp.product.123456.update")
|
||||
kwargs['label'] = "foobar"
|
||||
self.assertEqual(update_product(**kwargs), (123456, "foobar"))
|
||||
|
||||
@wamp.register(u"com.myapp.<category:string>.<cid:int>.update")
|
||||
def update(category=None, cid=None, label=None):
|
||||
return category, cid, label
|
||||
|
||||
args, kwargs = update._wampuris[0].match(u"com.myapp.product.123456.update")
|
||||
kwargs['label'] = "foobar"
|
||||
self.assertEqual(update(**kwargs), ("product", 123456, "foobar"))
|
||||
|
||||
def test_match_decorated_handler(self):
|
||||
|
||||
@wamp.subscribe(u"com.myapp.on_shutdown")
|
||||
def on_shutdown():
|
||||
pass
|
||||
|
||||
args, kwargs = on_shutdown._wampuris[0].match(u"com.myapp.on_shutdown")
|
||||
self.assertEqual(on_shutdown(**kwargs), None)
|
||||
|
||||
@wamp.subscribe(u"com.myapp.product.<product:int>.on_update")
|
||||
def on_product_update(product=None, label=None):
|
||||
return product, label
|
||||
|
||||
args, kwargs = on_product_update._wampuris[0].match(u"com.myapp.product.123456.on_update")
|
||||
kwargs['label'] = "foobar"
|
||||
self.assertEqual(on_product_update(**kwargs), (123456, "foobar"))
|
||||
|
||||
@wamp.subscribe(u"com.myapp.<category:string>.<cid:int>.on_update")
|
||||
def on_update(category=None, cid=None, label=None):
|
||||
return category, cid, label
|
||||
|
||||
args, kwargs = on_update._wampuris[0].match(u"com.myapp.product.123456.on_update")
|
||||
kwargs['label'] = "foobar"
|
||||
self.assertEqual(on_update(**kwargs), ("product", 123456, "foobar"))
|
||||
|
||||
def test_match_decorated_exception(self):
|
||||
|
||||
@wamp.error(u"com.myapp.error")
|
||||
class AppError(Exception):
|
||||
|
||||
def __init__(self, msg):
|
||||
Exception.__init__(self, msg)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__ == other.__class__ and \
|
||||
self.args == other.args
|
||||
|
||||
args, kwargs = AppError._wampuris[0].match(u"com.myapp.error")
|
||||
# noinspection PyArgumentList
|
||||
self.assertEqual(AppError(u"fuck", **kwargs), AppError(u"fuck"))
|
||||
|
||||
@wamp.error(u"com.myapp.product.<product:int>.product_inactive")
|
||||
class ProductInactiveError(Exception):
|
||||
|
||||
def __init__(self, msg, product=None):
|
||||
Exception.__init__(self, msg)
|
||||
self.product = product
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__ == other.__class__ and \
|
||||
self.args == other.args and \
|
||||
self.product == other.product
|
||||
|
||||
args, kwargs = ProductInactiveError._wampuris[0].match(u"com.myapp.product.123456.product_inactive")
|
||||
self.assertEqual(ProductInactiveError("fuck", **kwargs), ProductInactiveError("fuck", 123456))
|
||||
|
||||
@wamp.error(u"com.myapp.<category:string>.<product:int>.inactive")
|
||||
class ObjectInactiveError(Exception):
|
||||
|
||||
def __init__(self, msg, category=None, product=None):
|
||||
Exception.__init__(self, msg)
|
||||
self.category = category
|
||||
self.product = product
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__class__ == other.__class__ and \
|
||||
self.args == other.args and \
|
||||
self.category == other.category and \
|
||||
self.product == other.product
|
||||
|
||||
args, kwargs = ObjectInactiveError._wampuris[0].match(u"com.myapp.product.123456.inactive")
|
||||
self.assertEqual(ObjectInactiveError("fuck", **kwargs), ObjectInactiveError("fuck", "product", 123456))
|
||||
|
||||
|
||||
class KwException(Exception):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Exception.__init__(self, *args)
|
||||
self.kwargs = kwargs
|
||||
|
||||
# what if the WAMP error message received
|
||||
# contains args/kwargs that cannot be
|
||||
# consumed by the constructor of the exception
|
||||
# class defined for the WAMP error URI?
|
||||
|
||||
# 1. we can bail out (but we are already signaling an error)
|
||||
# 2. we can require a generic constructor
|
||||
# 3. we can map only unconsumed args/kwargs to generic attributes
|
||||
# 4. we can silently drop unconsumed args/kwargs
|
||||
|
||||
|
||||
class MockSession(object):
|
||||
|
||||
def __init__(self):
|
||||
self._ecls_to_uri_pat = {}
|
||||
self._uri_to_ecls = {}
|
||||
|
||||
def define(self, exception, error=None):
|
||||
if error is None:
|
||||
assert(hasattr(exception, '_wampuris'))
|
||||
self._ecls_to_uri_pat[exception] = exception._wampuris
|
||||
self._uri_to_ecls[exception._wampuris[0].uri()] = exception
|
||||
else:
|
||||
assert(not hasattr(exception, '_wampuris'))
|
||||
self._ecls_to_uri_pat[exception] = [Pattern(error, Pattern.URI_TARGET_HANDLER)]
|
||||
self._uri_to_ecls[error] = exception
|
||||
|
||||
def map_error(self, error, args=None, kwargs=None):
|
||||
|
||||
# FIXME:
|
||||
# 1. map to ecls based on error URI wildcard/prefix
|
||||
# 2. extract additional args/kwargs from error URI
|
||||
|
||||
if error in self._uri_to_ecls:
|
||||
ecls = self._uri_to_ecls[error]
|
||||
try:
|
||||
# the following might fail, eg. TypeError when
|
||||
# signature of exception constructor is incompatible
|
||||
# with args/kwargs or when the exception constructor raises
|
||||
if kwargs:
|
||||
if args:
|
||||
exc = ecls(*args, **kwargs)
|
||||
else:
|
||||
exc = ecls(**kwargs)
|
||||
else:
|
||||
if args:
|
||||
exc = ecls(*args)
|
||||
else:
|
||||
exc = ecls()
|
||||
except Exception:
|
||||
# FIXME: log e
|
||||
exc = KwException(error, *args, **kwargs)
|
||||
else:
|
||||
# this never fails
|
||||
args = args or []
|
||||
kwargs = kwargs or {}
|
||||
exc = KwException(error, *args, **kwargs)
|
||||
return exc
|
||||
|
||||
|
||||
class TestDecoratorsAdvanced(unittest.TestCase):
|
||||
|
||||
def test_decorate_exception_non_exception(self):
|
||||
|
||||
def test():
|
||||
# noinspection PyUnusedLocal
|
||||
@wamp.error(u"com.test.error")
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
self.assertRaises(Exception, test)
|
||||
|
||||
def test_decorate_endpoint_multiple(self):
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@wamp.register(u"com.oldapp.oldproc")
|
||||
@wamp.register(u"com.calculator.square")
|
||||
def square(x):
|
||||
"""Do nothing."""
|
||||
|
||||
self.assertTrue(hasattr(square, '_wampuris'))
|
||||
self.assertTrue(type(square._wampuris) == list)
|
||||
self.assertEqual(len(square._wampuris), 2)
|
||||
|
||||
for i in range(2):
|
||||
self.assertIsInstance(square._wampuris[i], Pattern)
|
||||
self.assertTrue(square._wampuris[i].is_endpoint())
|
||||
self.assertFalse(square._wampuris[i].is_handler())
|
||||
self.assertFalse(square._wampuris[i].is_exception())
|
||||
self.assertEqual(square._wampuris[i]._type, Pattern.URI_TYPE_EXACT)
|
||||
|
||||
self.assertEqual(square._wampuris[0].uri(), u"com.calculator.square")
|
||||
self.assertEqual(square._wampuris[1].uri(), u"com.oldapp.oldproc")
|
||||
|
||||
def test_marshal_decorated_exception(self):
|
||||
|
||||
@wamp.error(u"com.myapp.error")
|
||||
class AppError(Exception):
|
||||
pass
|
||||
|
||||
try:
|
||||
raise AppError("fuck")
|
||||
except Exception as e:
|
||||
self.assertEqual(e._wampuris[0].uri(), u"com.myapp.error")
|
||||
|
||||
@wamp.error(u"com.myapp.product.<product:int>.product_inactive")
|
||||
class ProductInactiveError(Exception):
|
||||
|
||||
def __init__(self, msg, product=None):
|
||||
Exception.__init__(self, msg)
|
||||
self.product = product
|
||||
|
||||
try:
|
||||
raise ProductInactiveError("fuck", 123456)
|
||||
except Exception as e:
|
||||
self.assertEqual(e._wampuris[0].uri(), u"com.myapp.product.<product:int>.product_inactive")
|
||||
|
||||
session = MockSession()
|
||||
session.define(AppError)
|
||||
|
||||
def test_define_exception_undecorated(self):
|
||||
|
||||
session = MockSession()
|
||||
|
||||
class AppError(Exception):
|
||||
pass
|
||||
|
||||
# defining an undecorated exception requires
|
||||
# an URI to be provided
|
||||
self.assertRaises(Exception, session.define, AppError)
|
||||
|
||||
session.define(AppError, u"com.myapp.error")
|
||||
|
||||
exc = session.map_error(u"com.myapp.error")
|
||||
self.assertIsInstance(exc, AppError)
|
||||
|
||||
def test_define_exception_decorated(self):
|
||||
|
||||
session = MockSession()
|
||||
|
||||
@wamp.error(u"com.myapp.error")
|
||||
class AppError(Exception):
|
||||
pass
|
||||
|
||||
# when defining a decorated exception
|
||||
# an URI must not be provided
|
||||
self.assertRaises(Exception, session.define, AppError, u"com.myapp.error")
|
||||
|
||||
session.define(AppError)
|
||||
|
||||
exc = session.map_error(u"com.myapp.error")
|
||||
self.assertIsInstance(exc, AppError)
|
||||
|
||||
def test_map_exception_undefined(self):
|
||||
|
||||
session = MockSession()
|
||||
|
||||
exc = session.map_error(u"com.myapp.error")
|
||||
self.assertIsInstance(exc, Exception)
|
||||
|
||||
def test_map_exception_args(self):
|
||||
|
||||
session = MockSession()
|
||||
|
||||
@wamp.error(u"com.myapp.error")
|
||||
class AppError(Exception):
|
||||
pass
|
||||
|
||||
@wamp.error(u"com.myapp.error.product_inactive")
|
||||
class ProductInactiveError(Exception):
|
||||
def __init__(self, product=None):
|
||||
self.product = product
|
||||
|
||||
# define exceptions in mock session
|
||||
session.define(AppError)
|
||||
session.define(ProductInactiveError)
|
||||
|
||||
for test in [
|
||||
# (u"com.myapp.foo.error", [], {}, KwException),
|
||||
(u"com.myapp.error", [], {}, AppError),
|
||||
(u"com.myapp.error", ["you are doing it wrong"], {}, AppError),
|
||||
(u"com.myapp.error", ["you are doing it wrong", 1, 2, 3], {}, AppError),
|
||||
|
||||
(u"com.myapp.error.product_inactive", [], {}, ProductInactiveError),
|
||||
(u"com.myapp.error.product_inactive", [], {"product": 123456}, ProductInactiveError),
|
||||
]:
|
||||
error, args, kwargs, ecls = test
|
||||
exc = session.map_error(error, args, kwargs)
|
||||
|
||||
self.assertIsInstance(exc, ecls)
|
||||
self.assertEqual(list(exc.args), args)
|
||||
Reference in New Issue
Block a user