Add the base message types.

This commit is contained in:
Eric Snow 2018-01-18 17:35:36 +00:00
parent b4abe56051
commit ba0e9963c0
4 changed files with 1302 additions and 22 deletions

View file

@ -1,22 +0,0 @@
from debugger_protocol._base import Readonly, WithRepr
class Base(Readonly, WithRepr):
"""Base class for message-related types."""
_INIT_ARGS = None
@classmethod
def from_data(cls, **kwargs):
"""Return an instance based on the given raw data."""
return cls(**kwargs)
def __init__(self):
self._validate()
def _validate(self):
pass
def as_data(self):
"""Return serializable data for the instance."""
return {}

View file

@ -0,0 +1,366 @@
from debugger_protocol._base import Readonly, WithRepr
from debugger_protocol.arg import param_from_datatype
from . import MESSAGE_TYPES, Message
"""
From the schema:
MESSAGE = [
name
base
description
props: [PROPERTY + (properties: [PROPERTY])]
]
PROPERTY = [
name
type: choices (one or a list)
(enum/_enum)
description
required: True/False (default: False)
]
inheritance: override properties of base
"""
class ProtocolMessage(Readonly, WithRepr, Message):
"""Base class of requests, responses, and events."""
_reqid = 0
TYPE = None
@classmethod
def from_data(cls, type, seq, **kwargs):
"""Return an instance based on the given raw data."""
return cls(type=type, seq=seq, **kwargs)
@classmethod
def _next_reqid(cls):
reqid = ProtocolMessage._reqid
ProtocolMessage._reqid += 1
return reqid
_NOT_SET = object()
def __init__(self, seq=_NOT_SET, **kwargs):
type = kwargs.pop('type', self.TYPE)
if seq is self._NOT_SET:
seq = self._next_reqid()
self._bind_attrs(
type=type or None,
seq=int(seq) if seq or seq == 0 else None,
)
self._validate()
def _validate(self):
if self.type is None:
raise TypeError('missing type')
elif self.TYPE is not None and self.type != self.TYPE:
raise ValueError('type must be {!r}'.format(self.TYPE))
elif self.type not in MESSAGE_TYPES:
raise ValueError('unsupported type {!r}'.format(self.type))
if self.seq is None:
raise TypeError('missing seq')
elif self.seq < 0:
msg = '"seq" must be a non-negative int, got {!r}'
raise ValueError(msg.format(self.seq))
def _init_args(self):
if self.TYPE is None:
yield ('type', self.type)
yield ('seq', self.seq)
def as_data(self):
"""Return serializable data for the instance."""
data = {
'type': self.type,
'seq': self.seq,
}
return data
##################################
class Request(ProtocolMessage):
"""A client or server-initiated request."""
TYPE = 'request'
TYPE_KEY = 'command'
COMMAND = None
ARGUMENTS = None
ARGUMENTS_REQUIRED = None
@classmethod
def from_data(cls, type, seq, command, arguments=None):
"""Return an instance based on the given raw data."""
return super(Request, cls).from_data(
type, seq,
command=command,
arguments=arguments,
)
@classmethod
def _arguments_required(cls):
if cls.ARGUMENTS_REQUIRED is None:
return cls.ARGUMENTS is not None
return cls.ARGUMENTS_REQUIRED
def __init__(self, arguments=None, **kwargs):
command = kwargs.pop('command', self.COMMAND)
args = None
if arguments is not None:
try:
arguments = dict(arguments)
except TypeError:
pass
if self.ARGUMENTS is not None:
param = param_from_datatype(self.ARGUMENTS)
args = param.bind(arguments)
if args is None:
raise TypeError('bad arguments {!r}'.format(arguments))
arguments = args.coerce()
self._bind_attrs(
command=command or None,
arguments=arguments or None,
_args=args,
)
super(Request, self).__init__(**kwargs)
def _validate(self):
super(Request, self)._validate()
if self.command is None:
raise TypeError('missing command')
elif self.COMMAND is not None and self.command != self.COMMAND:
raise ValueError('command must be {!r}'.format(self.COMMAND))
if self.arguments is None:
if self._arguments_required():
raise TypeError('missing arguments')
else:
if self.ARGUMENTS is None:
raise TypeError('got unexpected arguments')
self._args.validate()
def _init_args(self):
if self.COMMAND is None:
yield ('command', self.command)
if self.arguments is not None:
yield ('arguments', self.arguments)
yield ('seq', self.seq)
def as_data(self):
"""Return serializable data for the instance."""
data = super(Request, self).as_data()
data.update({
'command': self.command,
})
if self.arguments is not None:
data.update({
'arguments': self.arguments.as_data(),
})
return data
class Response(ProtocolMessage):
"""Response to a request."""
TYPE = 'response'
TYPE_KEY = 'command'
COMMAND = None
BODY = None
ERROR_BODY = None
BODY_REQUIRED = None
ERROR_BODY_REQUIRED = None
@classmethod
def from_data(cls, type, seq, request_seq, command, success,
body=None, message=None):
"""Return an instance based on the given raw data."""
return super(Response, cls).from_data(
type, seq,
request_seq=request_seq,
command=command,
success=success,
body=body,
message=message,
)
@classmethod
def _body_required(cls, success=True):
required = cls.BODY_REQUIRED if success else cls.ERROR_BODY_REQUIRED
if required is not None:
return required
bodyclass = cls.BODY if success else cls.ERROR_BODY
return bodyclass is not None
def __init__(self, request_seq, body=None, message=None, success=True,
**kwargs):
command = kwargs.pop('command', self.COMMAND)
reqseq = request_seq
bodyarg = None
if body is not None:
try:
body = dict(body)
except TypeError:
pass
bodyclass = self.BODY if success else self.ERROR_BODY
if bodyclass is not None:
param = param_from_datatype(bodyclass)
bodyarg = param.bind(body)
if bodyarg is None:
raise TypeError('bad body type {!r}'.format(body))
body = bodyarg.coerce()
self._bind_attrs(
command=command or None,
request_seq=int(reqseq) if reqseq or reqseq == 0 else None,
body=body or None,
_bodyarg=bodyarg,
message=message or None,
success=bool(success),
)
super(Response, self).__init__(**kwargs)
def _validate(self):
super(Response, self)._validate()
if self.request_seq is None:
raise TypeError('missing request_seq')
elif self.request_seq < 0:
msg = 'request_seq must be a non-negative int, got {!r}'
raise ValueError(msg.format(self.request_seq))
if not self.command:
raise TypeError('missing command')
elif self.COMMAND is not None and self.command != self.COMMAND:
raise ValueError('command must be {!r}'.format(self.COMMAND))
if self.body is None:
if self._body_required(self.success):
raise TypeError('missing body')
elif self._bodyarg is None:
raise ValueError('got unexpected body')
else:
self._bodyarg.validate()
if not self.success and not self.message:
raise TypeError('missing message')
def _init_args(self):
if self.COMMAND is None:
yield ('command', self.command)
yield ('request_seq', self.request_seq)
yield ('success', self.success)
if not self.success:
yield ('message', self.message)
if self.body is not None:
yield ('body', self.body)
yield ('seq', self.seq)
def as_data(self):
"""Return serializable data for the instance."""
data = super(Response, self).as_data()
data.update({
'request_seq': self.request_seq,
'command': self.command,
'success': self.success,
})
if self.body is not None:
data.update({
'body': self.body.as_data(),
})
if self.message is not None:
data.update({
'message': self.message,
})
return data
##################################
class Event(ProtocolMessage):
"""Server-initiated event."""
TYPE = 'event'
TYPE_KEY = 'event'
EVENT = None
BODY = None
BODY_REQUIRED = None
@classmethod
def from_data(cls, type, seq, event, body=None):
"""Return an instance based on the given raw data."""
return super(Event, cls).from_data(type, seq, event=event, body=body)
@classmethod
def _body_required(cls):
if cls.BODY_REQUIRED is None:
return cls.BODY is not None
return cls.BODY_REQUIRED
def __init__(self, body=None, **kwargs):
event = kwargs.pop('event', self.EVENT)
bodyarg = None
if body is not None:
try:
body = dict(body)
except TypeError:
pass
if self.BODY is not None:
param = param_from_datatype(self.BODY)
bodyarg = param.bind(body)
if bodyarg is None:
raise TypeError('bad body type {!r}'.format(body))
body = bodyarg.coerce()
self._bind_attrs(
event=event or None,
body=body or None,
_bodyarg=bodyarg,
)
super(Event, self).__init__(**kwargs)
def _validate(self):
super(Event, self)._validate()
if self.event is None:
raise TypeError('missing event')
if self.EVENT is not None and self.event != self.EVENT:
msg = 'event must be {!r}, got {!r}'
raise ValueError(msg.format(self.EVENT, self.event))
if self.body is None:
if self._body_required():
raise TypeError('missing body')
elif self._bodyarg is None:
raise ValueError('got unexpected body')
else:
self._bodyarg.validate()
def _init_args(self):
if self.EVENT is None:
yield ('event', self.event)
if self.body is not None:
yield ('body', self.body)
yield ('seq', self.seq)
@property
def name(self):
return self.event
def as_data(self):
"""Return serializable data for the instance."""
data = super(Event, self).as_data()
data.update({
'event': self.event,
})
if self.body is not None:
data.update({
'body': self.body.as_data(),
})
return data

View file

@ -0,0 +1,936 @@
import unittest
from debugger_protocol.arg import FieldsNamespace, Field
from debugger_protocol.messages import register
from debugger_protocol.messages.message import (
ProtocolMessage, Request, Response, Event)
@register
class DummyRequest(object):
TYPE = 'request'
TYPE_KEY = 'command'
COMMAND = '...'
@register
class DummyResponse(object):
TYPE = 'response'
TYPE_KEY = 'command'
COMMAND = '...'
@register
class DummyEvent(object):
TYPE = 'event'
TYPE_KEY = 'event'
EVENT = '...'
class FakeMsg(ProtocolMessage):
SEQ = 0
@classmethod
def _next_reqid(cls):
return cls.SEQ
class ProtocolMessageTests(unittest.TestCase):
def test_from_data(self):
data = {
'type': 'event',
'seq': 10,
}
msg = ProtocolMessage.from_data(**data)
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
def test_defaults(self): # no args
class Spam(FakeMsg):
SEQ = 10
TYPE = 'event'
msg = Spam()
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
def test_all_args(self):
msg = ProtocolMessage(10, type='event')
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
def test_coercion_seq(self):
msg = ProtocolMessage('10', type='event')
self.assertEqual(msg.seq, 10)
def test_validation(self):
# type
with self.assertRaises(TypeError):
ProtocolMessage(type=None)
with self.assertRaises(ValueError):
ProtocolMessage(type='spam')
class Other(ProtocolMessage):
TYPE = 'spam'
with self.assertRaises(ValueError):
Other(type='event')
# seq
with self.assertRaises(TypeError):
ProtocolMessage(None, type='event')
with self.assertRaises(ValueError):
ProtocolMessage(-1, type='event')
def test_readonly(self):
msg = ProtocolMessage(10, type='event')
with self.assertRaises(AttributeError):
msg.seq = 11
with self.assertRaises(AttributeError):
msg.type = 'event'
with self.assertRaises(AttributeError):
msg.spam = object()
with self.assertRaises(AttributeError):
del msg.seq
def test_repr(self):
msg = ProtocolMessage(10, type='event')
result = repr(msg)
self.assertEqual(result, "ProtocolMessage(type='event', seq=10)")
def test_repr_subclass(self):
class Eventish(ProtocolMessage):
TYPE = 'event'
msg = Eventish(10)
result = repr(msg)
self.assertEqual(result, 'Eventish(seq=10)')
def test_as_data(self):
msg = ProtocolMessage(10, type='event')
data = msg.as_data()
self.assertEqual(data, {
'type': 'event',
'seq': 10,
})
class RequestTests(unittest.TestCase):
def test_from_data_without_arguments(self):
data = {
'type': 'request',
'seq': 10,
'command': 'spam',
}
msg = Request.from_data(**data)
self.assertEqual(msg.type, 'request')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertIsNone(msg.arguments)
def test_from_data_with_arguments(self):
class Spam(Request):
class ARGUMENTS(FieldsNamespace):
FIELDS = [
Field('a'),
]
data = {
'type': 'request',
'seq': 10,
'command': 'spam',
'arguments': {'a': 'b'},
}
#msg = Request.from_data(**data)
msg = Spam.from_data(**data)
self.assertEqual(msg.type, 'request')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.arguments, {'a': 'b'})
def test_defaults(self):
class Spam(Request, FakeMsg):
SEQ = 10
COMMAND = 'spam'
msg = Spam()
self.assertEqual(msg.type, 'request')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertIsNone(msg.arguments)
def test_all_args(self):
class Spam(Request):
class ARGUMENTS(FieldsNamespace):
FIELDS = [
Field('a'),
]
args = {'a': 'b'}
msg = Spam(arguments=args, command='spam', seq=10)
self.assertEqual(msg.type, 'request')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.arguments, args)
def test_no_arguments_not_required(self):
class Spam(Request):
COMMAND = 'spam'
ARGUMENTS = True
ARGUMENTS_REQUIRED = False
msg = Spam()
self.assertIsNone(msg.arguments)
def test_no_args(self):
with self.assertRaises(TypeError):
Request()
def test_coercion_arguments(self):
class Spam(Request):
COMMAND = 'spam'
class ARGUMENTS(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
args = [('a', 'b')]
msg = Spam(args)
self.assertEqual(msg.arguments, {'a': 'b'})
with self.assertRaises(TypeError):
Spam(command='spam', arguments=11)
def test_validation(self):
with self.assertRaises(TypeError):
Request()
# command
class Other1(Request):
COMMAND = 'eggs'
with self.assertRaises(ValueError):
# command doesn't match
Other1(arguments=10, command='spam')
# arguments
with self.assertRaises(TypeError):
# unexpected arguments
Request(arguments=10, command='spam')
class Other2(Request):
COMMAND = 'spam'
ARGUMENTS = int
with self.assertRaises(ValueError):
# missing arguments (implicitly required)
Other2(command='eggs')
class Other3(Request):
COMMAND = 'eggs'
ARGUMENTS = int
ARGUMENTS_REQUIRED = True
with self.assertRaises(ValueError):
# missing arguments (explicitly required)
Other2(command='eggs')
def test_repr_minimal(self):
msg = Request(command='spam', seq=10)
result = repr(msg)
self.assertEqual(result, "Request(command='spam', seq=10)")
def test_repr_full(self):
msg = Request(command='spam', seq=10)
result = repr(msg)
self.assertEqual(result, "Request(command='spam', seq=10)")
def test_repr_subclass_minimal(self):
class SpamRequest(Request):
COMMAND = 'spam'
msg = SpamRequest(seq=10)
result = repr(msg)
self.assertEqual(result, "SpamRequest(seq=10)")
def test_repr_subclass_full(self):
class SpamRequest(Request):
COMMAND = 'spam'
class ARGUMENTS(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = SpamRequest(arguments={'a': 'b'}, seq=10)
result = repr(msg)
self.assertEqual(result,
"SpamRequest(arguments=ARGUMENTS(a='b'), seq=10)")
def test_as_data_minimal(self):
msg = Request(command='spam', seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'request',
'seq': 10,
'command': 'spam',
})
def test_as_data_full(self):
class Spam(Request):
COMMAND = 'spam'
class ARGUMENTS(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = Spam(arguments={'a': 'b'}, seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'request',
'seq': 10,
'command': 'spam',
'arguments': {'a': 'b'},
})
class ResponseTests(unittest.TestCase):
def test_from_data_without_body(self):
data = {
'type': 'response',
'seq': 10,
'command': 'spam',
'request_seq': 9,
'success': True,
}
msg = Response.from_data(**data)
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.request_seq, 9)
self.assertTrue(msg.success)
self.assertIsNone(msg.body)
self.assertIsNone(msg.message)
def test_from_data_with_body(self):
class Spam(Response):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
data = {
'type': 'response',
'seq': 10,
'command': 'spam',
'request_seq': 9,
'success': True,
'body': {'a': 'b'},
}
msg = Spam.from_data(**data)
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.request_seq, 9)
self.assertTrue(msg.success)
self.assertEqual(msg.body, {'a': 'b'})
self.assertIsNone(msg.message)
def test_from_data_error_without_body(self):
data = {
'type': 'response',
'seq': 10,
'command': 'spam',
'request_seq': 9,
'success': False,
'message': 'oops!',
}
msg = Response.from_data(**data)
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.request_seq, 9)
self.assertFalse(msg.success)
self.assertIsNone(msg.body)
self.assertEqual(msg.message, 'oops!')
def test_from_data_error_with_body(self):
class Spam(Response):
class ERROR_BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
data = {
'type': 'response',
'seq': 10,
'command': 'spam',
'request_seq': 9,
'success': False,
'message': 'oops!',
'body': {'a': 'b'},
}
msg = Spam.from_data(**data)
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.request_seq, 9)
self.assertFalse(msg.success)
self.assertEqual(msg.body, {'a': 'b'})
self.assertEqual(msg.message, 'oops!')
def test_defaults(self):
class Spam(Response, FakeMsg):
SEQ = 10
COMMAND = 'spam'
msg = Spam('9')
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.request_seq, 9)
self.assertEqual(msg.command, 'spam')
self.assertTrue(msg.success)
self.assertIsNone(msg.body)
self.assertIsNone(msg.message)
def test_all_args_not_error(self):
class Spam(Response):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
msg = Spam('9', command='spam', success=True, body={'a': 'b'},
seq=10, type='response')
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.request_seq, 9)
self.assertEqual(msg.command, 'spam')
self.assertTrue(msg.success)
self.assertEqual(msg.body, {'a': 'b'})
self.assertIsNone(msg.message)
def test_all_args_error(self):
class Spam(Response):
COMMAND = 'spam'
class ERROR_BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = Spam('9', success=False, message='oops!', body={'a': 'b'},
seq=10, type='response')
self.assertEqual(msg.type, 'response')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.command, 'spam')
self.assertEqual(msg.request_seq, 9)
self.assertFalse(msg.success)
self.assertEqual(msg.body, Spam.ERROR_BODY(a='b'))
self.assertEqual(msg.message, 'oops!')
def test_no_body_not_required(self):
class Spam(Response):
COMMAND = 'spam'
BODY = True
BODY_REQUIRED = False
msg = Spam('9')
self.assertIsNone(msg.body)
def test_no_error_body_not_required(self):
class Spam(Response):
COMMAND = 'spam'
ERROR_BODY = True
ERROR_BODY_REQUIRED = False
msg = Spam('9', success=False, message='oops!')
self.assertIsNone(msg.body)
def test_no_args(self):
with self.assertRaises(TypeError):
Response()
def test_coercion_request_seq(self):
msg = Response('9', command='spam')
self.assertEqual(msg.request_seq, 9)
def test_coercion_success(self):
msg1 = Response(9, success=1, command='spam')
msg2 = Response(9, success=None, command='spam', message='oops!')
self.assertIs(msg1.success, True)
self.assertIs(msg2.success, False)
def test_coercion_body(self):
class Spam(Response):
COMMAND = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
body = [('a', 'b')]
msg = Spam(9, body=body)
self.assertEqual(msg.body, {'a': 'b'})
with self.assertRaises(TypeError):
Spam(9, command='spam', body=11)
def test_coercion_error_body(self):
class Spam(Response):
COMMAND = 'spam'
class ERROR_BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
body = [('a', 'b')]
msg = Spam(9, body=body, success=False, message='oops!')
self.assertEqual(msg.body, {'a': 'b'})
with self.assertRaises(TypeError):
Spam(9, command='spam', success=False, message='oops!', body=11)
def test_validation(self):
# request_seq
with self.assertRaises(TypeError):
# missing
Response(None, command='spam')
with self.assertRaises(TypeError):
# missing
Response('', command='spam')
with self.assertRaises(TypeError):
# couldn't convert to int
Response(object(), command='spam')
with self.assertRaises(ValueError):
# not non-negative
Response(-1, command='spam')
# command
with self.assertRaises(TypeError):
# missing
Response(9, command=None)
with self.assertRaises(TypeError):
# missing
Response(9, command='')
class Other1(Response):
COMMAND = 'eggs'
with self.assertRaises(ValueError):
# does not match
Other1(9, command='spam')
# body
class Other2(Response):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
ERROR_BODY = BODY
with self.assertRaises(ValueError):
# unexpected
Response(9, command='spam', body=11)
with self.assertRaises(TypeError):
# missing (implicitly required)
Other2(9, command='spam')
with self.assertRaises(TypeError):
# missing (explicitly required)
Other2.BODY_REQUIRED = True
Other2(9, command='spam')
with self.assertRaises(ValueError):
# unexpected (error)
Response(9, command='spam', body=11, success=False, message=':(')
with self.assertRaises(TypeError):
# missing (error) (implicitly required)
Other2(9, command='spam', success=False, message=':(')
with self.assertRaises(TypeError):
# missing (error) (explicitly required)
Other2.ERROR_BODY_REQUIRED = True
Other2(9, command='spam', success=False, message=':(')
# message
with self.assertRaises(TypeError):
# missing
Response(9, command='spam', success=False)
def test_repr_minimal(self):
msg = Response(9, command='spam', seq=10)
result = repr(msg)
self.assertEqual(result,
"Response(command='spam', request_seq=9, success=True, seq=10)") # noqa
def test_repr_full(self):
msg = Response(9, command='spam', seq=10)
result = repr(msg)
self.assertEqual(result,
"Response(command='spam', request_seq=9, success=True, seq=10)") # noqa
def test_repr_error_minimal(self):
msg = Response(9, command='spam', success=False, message='oops!',
seq=10)
result = repr(msg)
self.assertEqual(result,
"Response(command='spam', request_seq=9, success=False, message='oops!', seq=10)") # noqa
def test_repr_error_full(self):
msg = Response(9, command='spam', success=False, message='oops!',
seq=10)
result = repr(msg)
self.assertEqual(result,
"Response(command='spam', request_seq=9, success=False, message='oops!', seq=10)") # noqa
def test_repr_subclass_minimal(self):
class SpamResponse(Response):
COMMAND = 'spam'
msg = SpamResponse(9, seq=10)
result = repr(msg)
self.assertEqual(result,
"SpamResponse(request_seq=9, success=True, seq=10)")
def test_repr_subclass_full(self):
class SpamResponse(Response):
COMMAND = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = SpamResponse(9, body={'a': 'b'}, seq=10)
result = repr(msg)
self.assertEqual(result,
"SpamResponse(request_seq=9, success=True, body=BODY(a='b'), seq=10)") # noqa
def test_repr_subclass_error_minimal(self):
class SpamResponse(Response):
COMMAND = 'spam'
msg = SpamResponse(9, success=False, message='oops!', seq=10)
result = repr(msg)
self.assertEqual(result,
"SpamResponse(request_seq=9, success=False, message='oops!', seq=10)") # noqa
def test_repr_subclass_error_full(self):
class SpamResponse(Response):
COMMAND = 'spam'
class ERROR_BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = SpamResponse(9, success=False, message='oops!', body={'a': 'b'},
seq=10)
result = repr(msg)
self.assertEqual(result,
"SpamResponse(request_seq=9, success=False, message='oops!', body=ERROR_BODY(a='b'), seq=10)") # noqa
def test_as_data_minimal(self):
msg = Response(9, command='spam', seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'response',
'seq': 10,
'request_seq': 9,
'command': 'spam',
'success': True,
})
def test_as_data_full(self):
class Spam(Response):
COMMAND = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = Spam(9, body={'a': 'b'}, seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'response',
'seq': 10,
'request_seq': 9,
'command': 'spam',
'success': True,
'body': {'a': 'b'},
})
def test_as_data_error_minimal(self):
msg = Response(9, command='spam', success=False, message='oops!',
seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'response',
'seq': 10,
'request_seq': 9,
'command': 'spam',
'success': False,
'message': 'oops!',
})
def test_as_data_error_full(self):
class Spam(Response):
COMMAND = 'spam'
class ERROR_BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = Spam(9, success=False, body={'a': 'b'}, message='oops!', seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'response',
'seq': 10,
'request_seq': 9,
'command': 'spam',
'success': False,
'message': 'oops!',
'body': {'a': 'b'},
})
class EventTests(unittest.TestCase):
def test_from_data_without_body(self):
data = {
'type': 'event',
'seq': 10,
'event': 'spam',
}
msg = Event.from_data(**data)
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.event, 'spam')
self.assertIsNone(msg.body)
def test_from_data_with_body(self):
class Spam(Event):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
data = {
'type': 'event',
'seq': 10,
'event': 'spam',
'body': {'a': 'b'},
}
msg = Spam.from_data(**data)
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.event, 'spam')
self.assertEqual(msg.body, {'a': 'b'})
def test_defaults(self): # no args
class Spam(Event, FakeMsg):
SEQ = 10
EVENT = 'spam'
msg = Spam()
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.event, 'spam')
self.assertIsNone(msg.body)
def test_all_args(self):
class Spam(Event):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
msg = Spam(event='spam', body={'a': 'b'}, seq=10, type='event')
self.assertEqual(msg.type, 'event')
self.assertEqual(msg.seq, 10)
self.assertEqual(msg.event, 'spam')
self.assertEqual(msg.body, {'a': 'b'})
def test_no_body_not_required(self):
class Spam(Event):
EVENT = 'spam'
BODY = True
BODY_REQUIRED = False
msg = Spam()
self.assertIsNone(msg.body)
def test_no_args(self):
with self.assertRaises(TypeError):
Event()
def test_coercion_body(self):
class Spam(Event):
EVENT = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
body = [('a', 'b')]
msg = Spam(body=body)
self.assertEqual(msg.body, {'a': 'b'})
with self.assertRaises(TypeError):
Spam(event='spam', body=11)
def test_validation(self):
# event
with self.assertRaises(TypeError):
# missing
Event(event=None)
with self.assertRaises(TypeError):
# missing
Event(event='')
class Other1(Event):
EVENT = 'eggs'
with self.assertRaises(ValueError):
# does not match
Other1(event='spam')
# body
class Other2(Event):
class BODY(FieldsNamespace):
FIELDS = [
Field('a'),
]
with self.assertRaises(ValueError):
# unexpected
Event(event='spam', body=11)
with self.assertRaises(TypeError):
# missing (implicitly required)
Other2(9, command='spam')
with self.assertRaises(TypeError):
# missing (explicitly required)
Other2.BODY_REQUIRED = True
Other2(9, command='spam')
def test_repr_minimal(self):
msg = Event(event='spam', seq=10)
result = repr(msg)
self.assertEqual(result, "Event(event='spam', seq=10)")
def test_repr_full(self):
msg = Event(event='spam', seq=10)
result = repr(msg)
self.assertEqual(result, "Event(event='spam', seq=10)")
def test_repr_subclass_minimal(self):
class SpamEvent(Event):
EVENT = 'spam'
msg = SpamEvent(seq=10)
result = repr(msg)
self.assertEqual(result, 'SpamEvent(seq=10)')
def test_repr_subclass_full(self):
class SpamEvent(Event):
EVENT = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = SpamEvent(body={'a': 'b'}, seq=10)
result = repr(msg)
self.assertEqual(result, "SpamEvent(body=BODY(a='b'), seq=10)")
def test_as_data_minimal(self):
msg = Event(event='spam', seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'event',
'seq': 10,
'event': 'spam',
})
def test_as_data_full(self):
class Spam(Event):
EVENT = 'spam'
class BODY(FieldsNamespace): # noqa
FIELDS = [
Field('a'),
]
msg = Spam(body={'a': 'b'}, seq=10)
data = msg.as_data()
self.assertEqual(data, {
'type': 'event',
'seq': 10,
'event': 'spam',
'body': {'a': 'b'},
})