Adding web app tests (#13)

* Adding web app tests

Signed-off-by: Denis Makogon <denys.makogon@oracle.com>

* addressing review comments

Signed-off-by: Denis Makogon <denys.makogon@oracle.com>
This commit is contained in:
Denis Makogon 2019-01-17 02:26:55 +02:00 committed by GitHub
parent 5cd67e4122
commit 7070e5124a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 277 additions and 159 deletions

View File

@ -21,8 +21,13 @@ class Converter(object):
TYPE = None TYPE = None
def read(self, event, headers: dict, body: typing.IO, def read(
data_unmarshaller: typing.Callable) -> base.BaseEvent: self,
event,
headers: dict,
body: typing.IO,
data_unmarshaller: typing.Callable
) -> base.BaseEvent:
raise Exception("not implemented") raise Exception("not implemented")
def event_supported(self, event: object) -> bool: def event_supported(self, event: object) -> bool:
@ -31,6 +36,9 @@ class Converter(object):
def can_read(self, content_type: str) -> bool: def can_read(self, content_type: str) -> bool:
raise Exception("not implemented") raise Exception("not implemented")
def write(self, event: base.BaseEvent, def write(
data_marshaller: typing.Callable) -> (dict, typing.IO): self,
event: base.BaseEvent,
data_marshaller: typing.Callable
) -> (dict, object):
raise Exception("not implemented") raise Exception("not implemented")

View File

@ -23,7 +23,7 @@ from cloudevents.sdk.event import v02
class BinaryHTTPCloudEventConverter(base.Converter): class BinaryHTTPCloudEventConverter(base.Converter):
TYPE = "binary" TYPE = "binary"
SUPPORTED_VERSIONS = [v02.Event, ] SUPPORTED_VERSIONS = [v02.Event]
def can_read(self, content_type: str) -> bool: def can_read(self, content_type: str) -> bool:
return True return True
@ -31,17 +31,21 @@ class BinaryHTTPCloudEventConverter(base.Converter):
def event_supported(self, event: object) -> bool: def event_supported(self, event: object) -> bool:
return type(event) in self.SUPPORTED_VERSIONS return type(event) in self.SUPPORTED_VERSIONS
def read(self, def read(
event: event_base.BaseEvent, self,
headers: dict, body: typing.IO, event: event_base.BaseEvent,
data_unmarshaller: typing.Callable) -> event_base.BaseEvent: headers: dict,
body: typing.IO,
data_unmarshaller: typing.Callable,
) -> event_base.BaseEvent:
if type(event) not in self.SUPPORTED_VERSIONS: if type(event) not in self.SUPPORTED_VERSIONS:
raise exceptions.UnsupportedEvent(type(event)) raise exceptions.UnsupportedEvent(type(event))
event.UnmarshalBinary(headers, body, data_unmarshaller) event.UnmarshalBinary(headers, body, data_unmarshaller)
return event return event
def write(self, event: event_base.BaseEvent, def write(
data_marshaller: typing.Callable) -> (dict, typing.IO): self, event: event_base.BaseEvent, data_marshaller: typing.Callable
) -> (dict, typing.IO):
return event.MarshalBinary(data_marshaller) return event.MarshalBinary(data_marshaller)

View File

@ -30,17 +30,20 @@ class JSONHTTPCloudEventConverter(base.Converter):
# structured format supported by both spec 0.1 and 0.2 # structured format supported by both spec 0.1 and 0.2
return True return True
def read(self, event: event_base.BaseEvent, def read(
headers: dict, self,
body: typing.IO, event: event_base.BaseEvent,
data_unmarshaller: typing.Callable) -> event_base.BaseEvent: headers: dict,
body: typing.IO,
data_unmarshaller: typing.Callable,
) -> event_base.BaseEvent:
event.UnmarshalJSON(body, data_unmarshaller) event.UnmarshalJSON(body, data_unmarshaller)
return event return event
def write(self, def write(
event: event_base.BaseEvent, self, event: event_base.BaseEvent, data_marshaller: typing.Callable
data_marshaller: typing.Callable) -> (dict, typing.IO): ) -> (dict, typing.IO):
http_headers = {'content-type': self.MIME_TYPE} http_headers = {"content-type": self.MIME_TYPE}
return http_headers, event.MarshalJSON(data_marshaller) return http_headers, event.MarshalJSON(data_marshaller)

View File

@ -18,7 +18,6 @@ import typing
class EventGetterSetter(object): class EventGetterSetter(object):
def CloudEventVersion(self) -> str: def CloudEventVersion(self) -> str:
raise Exception("not implemented") raise Exception("not implemented")
@ -76,18 +75,13 @@ class EventGetterSetter(object):
class BaseEvent(EventGetterSetter): class BaseEvent(EventGetterSetter):
def Properties(self, with_nullable=False) -> dict: def Properties(self, with_nullable=False) -> dict:
props = dict() props = dict()
for name, value in self.__dict__.items(): for name, value in self.__dict__.items():
if str(name).startswith("ce__"): if str(name).startswith("ce__"):
v = value.get() v = value.get()
if v is not None or with_nullable: if v is not None or with_nullable:
props.update( props.update({str(name).replace("ce__", ""): value.get()})
{
str(name).replace("ce__", ""): value.get()
}
)
return props return props
@ -119,33 +113,38 @@ class BaseEvent(EventGetterSetter):
props["data"] = data_marshaller(props.get("data")) props["data"] = data_marshaller(props.get("data"))
return io.BytesIO(json.dumps(props).encode("utf-8")) return io.BytesIO(json.dumps(props).encode("utf-8"))
def UnmarshalJSON(self, b: typing.IO, def UnmarshalJSON(self, b: typing.IO, data_unmarshaller: typing.Callable):
data_unmarshaller: typing.Callable):
raw_ce = json.load(b) raw_ce = json.load(b)
for name, value in raw_ce.items(): for name, value in raw_ce.items():
if name == "data": if name == "data":
value = data_unmarshaller(value) value = data_unmarshaller(value)
self.Set(name, value) self.Set(name, value)
def UnmarshalBinary(self, headers: dict, body: typing.IO, def UnmarshalBinary(
data_unmarshaller: typing.Callable): self,
BINARY_MAPPING = { headers: dict,
'content-type': 'contenttype', body: typing.IO,
data_unmarshaller: typing.Callable
):
binary_mapping = {
"content-type": "contenttype",
# TODO(someone): add Distributed Tracing. It's not clear # TODO(someone): add Distributed Tracing. It's not clear
# if this is one extension or two. # if this is one extension or two.
# https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md # https://github.com/cloudevents/spec/blob/master/extensions/distributed-tracing.md
} }
for header, value in headers.items(): for header, value in headers.items():
header = header.lower() header = header.lower()
if header in BINARY_MAPPING: if header in binary_mapping:
self.Set(BINARY_MAPPING[header], value) self.Set(binary_mapping[header], value)
elif header.startswith("ce-"): elif header.startswith("ce-"):
self.Set(header[3:], value) self.Set(header[3:], value)
self.Set("data", data_unmarshaller(body)) self.Set("data", data_unmarshaller(body))
def MarshalBinary( def MarshalBinary(
self, data_marshaller: typing.Callable) -> (dict, object): self,
data_marshaller: typing.Callable
) -> (dict, object):
headers = {} headers = {}
if self.ContentType(): if self.ContentType():
headers["content-type"] = self.ContentType() headers["content-type"] = self.ContentType()
@ -159,5 +158,4 @@ class BaseEvent(EventGetterSetter):
headers["ce-{0}".format(key)] = value headers["ce-{0}".format(key)] = value
data, _ = self.Get("data") data, _ = self.Get("data")
return headers, io.BytesIO( return headers, data_marshaller(data)
str(data_marshaller(data)).encode("utf-8"))

View File

@ -14,7 +14,6 @@
class Option(object): class Option(object):
def __init__(self, name, value, is_required): def __init__(self, name, value, is_required):
self.name = name self.name = name
self.value = value self.value = value
@ -25,7 +24,9 @@ class Option(object):
if self.is_required and is_none: if self.is_required and is_none:
raise ValueError( raise ValueError(
"Attribute value error: '{0}', " "Attribute value error: '{0}', "
"invalid new value.".format(self.name)) "" "invalid new value."
.format(self.name)
)
self.value = new_value self.value = new_value

View File

@ -12,25 +12,62 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from cloudevents.sdk.event import opt
from cloudevents.sdk.event import base from cloudevents.sdk.event import base
from cloudevents.sdk.event import opt
class Event(base.BaseEvent): class Event(base.BaseEvent):
def __init__(self): def __init__(self):
self.ce__cloudEventsVersion = opt.Option( self.ce__cloudEventsVersion = opt.Option(
"cloudEventsVersion", "0.1", True) "cloudEventsVersion",
self.ce__eventType = opt.Option("eventType", None, True) "0.1",
True
)
self.ce__eventType = opt.Option(
"eventType",
None,
True
)
self.ce__eventTypeVersion = opt.Option( self.ce__eventTypeVersion = opt.Option(
"eventTypeVersion", None, False) "eventTypeVersion",
self.ce__source = opt.Option("source", None, True) None,
self.ce__eventID = opt.Option("eventID", None, True) False
self.ce__eventTime = opt.Option("eventTime", None, True) )
self.ce__schemaURL = opt.Option("schemaURL", None, False) self.ce__source = opt.Option(
self.ce__contentType = opt.Option("contentType", None, False) "source",
self.ce__data = opt.Option("data", None, False) None,
self.ce__extensions = opt.Option("extensions", dict(), False) True
)
self.ce__eventID = opt.Option(
"eventID",
None,
True
)
self.ce__eventTime = opt.Option(
"eventTime",
None,
True
)
self.ce__schemaURL = opt.Option(
"schemaURL",
None,
False
)
self.ce__contentType = opt.Option(
"contentType",
None,
False
)
self.ce__data = opt.Option(
"data",
None,
False
)
self.ce__extensions = opt.Option(
"extensions",
dict(),
False
)
def CloudEventVersion(self) -> str: def CloudEventVersion(self) -> str:
return self.ce__cloudEventsVersion.get() return self.ce__cloudEventsVersion.get()

View File

@ -12,12 +12,11 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from cloudevents.sdk.event import opt
from cloudevents.sdk.event import base from cloudevents.sdk.event import base
from cloudevents.sdk.event import opt
class Event(base.BaseEvent): class Event(base.BaseEvent):
def __init__(self): def __init__(self):
self.ce__specversion = opt.Option("specversion", "0.2", True) self.ce__specversion = opt.Option("specversion", "0.2", True)
self.ce__type = opt.Option("type", None, True) self.ce__type = opt.Option("type", None, True)

View File

@ -14,34 +14,34 @@
class UnsupportedEvent(Exception): class UnsupportedEvent(Exception):
def __init__(self, event_class): def __init__(self, event_class):
super().__init__("Invalid CloudEvent class: " super().__init__(
"'{0}'".format(event_class)) "Invalid CloudEvent class: '{0}'".format(event_class)
)
class InvalidDataUnmarshaller(Exception): class InvalidDataUnmarshaller(Exception):
def __init__(self): def __init__(self):
super().__init__( super().__init__("Invalid data unmarshaller, is not a callable")
"Invalid data unmarshaller, is not a callable")
class InvalidDataMarshaller(Exception): class InvalidDataMarshaller(Exception):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
"Invalid data marshaller, is not a callable") "Invalid data marshaller, is not a callable"
)
class NoSuchConverter(Exception): class NoSuchConverter(Exception):
def __init__(self, converter_type): def __init__(self, converter_type):
super().__init__( super().__init__(
"No such converter {0}".format(converter_type)) "No such converter {0}".format(converter_type)
)
class UnsupportedEventConverter(Exception): class UnsupportedEventConverter(Exception):
def __init__(self, content_type): def __init__(self, content_type):
super().__init__( super().__init__(
"Unable to identify valid event converter " "Unable to identify valid event converter "
"for content-type: '{0}'".format(content_type)) "for content-type: '{0}'".format(content_type)
)

View File

@ -35,14 +35,16 @@ class HTTPMarshaller(object):
:param converters: a list of HTTP-to-CloudEvent-to-HTTP constructors :param converters: a list of HTTP-to-CloudEvent-to-HTTP constructors
:type converters: typing.List[base.Converter] :type converters: typing.List[base.Converter]
""" """
self.__converters = (c for c in converters) self.__converters = [c for c in converters]
self.__converters_by_type = {c.TYPE: c for c in converters} self.__converters_by_type = {c.TYPE: c for c in converters}
def FromRequest(self, event: event_base.BaseEvent, def FromRequest(
headers: dict, self,
body: typing.IO, event: event_base.BaseEvent,
data_unmarshaller: headers: dict,
typing.Callable) -> event_base.BaseEvent: body: typing.IO,
data_unmarshaller: typing.Callable,
) -> event_base.BaseEvent:
""" """
Reads a CloudEvent from an HTTP headers and request body Reads a CloudEvent from an HTTP headers and request body
:param event: CloudEvent placeholder :param event: CloudEvent placeholder
@ -59,8 +61,7 @@ class HTTPMarshaller(object):
if not isinstance(data_unmarshaller, typing.Callable): if not isinstance(data_unmarshaller, typing.Callable):
raise exceptions.InvalidDataUnmarshaller() raise exceptions.InvalidDataUnmarshaller()
content_type = headers.get( content_type = headers.get("content-type", headers.get("Content-Type"))
"content-type", headers.get("Content-Type"))
for cnvrtr in self.__converters: for cnvrtr in self.__converters:
if cnvrtr.can_read(content_type) and cnvrtr.event_supported(event): if cnvrtr.can_read(content_type) and cnvrtr.event_supported(event):
@ -68,11 +69,16 @@ class HTTPMarshaller(object):
raise exceptions.UnsupportedEventConverter( raise exceptions.UnsupportedEventConverter(
"No registered marshaller for {0} in {1}".format( "No registered marshaller for {0} in {1}".format(
content_type, self.__converters)) content_type, self.__converters
)
)
def ToRequest(self, event: event_base.BaseEvent, def ToRequest(
converter_type: str, self,
data_marshaller: typing.Callable) -> (dict, typing.IO): event: event_base.BaseEvent,
converter_type: str,
data_marshaller: typing.Callable,
) -> (dict, typing.IO):
""" """
Writes a CloudEvent into a HTTP-ready form of headers and request body Writes a CloudEvent into a HTTP-ready form of headers and request body
:param event: CloudEvent :param event: CloudEvent
@ -101,14 +107,17 @@ def NewDefaultHTTPMarshaller() -> HTTPMarshaller:
:return: an instance of HTTP marshaller :return: an instance of HTTP marshaller
:rtype: cloudevents.sdk.marshaller.HTTPMarshaller :rtype: cloudevents.sdk.marshaller.HTTPMarshaller
""" """
return HTTPMarshaller([ return HTTPMarshaller(
structured.NewJSONHTTPCloudEventConverter(), [
binary.NewBinaryHTTPCloudEventConverter(), structured.NewJSONHTTPCloudEventConverter(),
]) binary.NewBinaryHTTPCloudEventConverter(),
]
)
def NewHTTPMarshaller( def NewHTTPMarshaller(
converters: typing.List[base.Converter]) -> HTTPMarshaller: converters: typing.List[base.Converter]
) -> HTTPMarshaller:
""" """
Creates the default HTTP marshaller with both Creates the default HTTP marshaller with both
structured and binary converters structured and binary converters

View File

@ -25,7 +25,7 @@ headers = {
"ce-id": ce_id, "ce-id": ce_id,
"ce-time": eventTime, "ce-time": eventTime,
"ce-source": source, "ce-source": source,
"Content-Type": contentType "Content-Type": contentType,
} }
ce = { ce = {
"specversion": specversion, "specversion": specversion,
@ -33,5 +33,5 @@ ce = {
"id": ce_id, "id": ce_id,
"time": eventTime, "time": eventTime,
"source": source, "source": source,
"contenttype": contentType "contenttype": contentType,
} }

View File

@ -30,10 +30,7 @@ from cloudevents.tests import data
def test_binary_converter_upstream(): def test_binary_converter_upstream():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [binary.NewBinaryHTTPCloudEventConverter()])
binary.NewBinaryHTTPCloudEventConverter()
]
)
event = m.FromRequest(v02.Event(), data.headers, None, lambda x: x) event = m.FromRequest(v02.Event(), data.headers, None, lambda x: x)
assert event is not None assert event is not None
assert event.Get("type") == (data.ce_type, True) assert event.Get("type") == (data.ce_type, True)
@ -42,15 +39,12 @@ def test_binary_converter_upstream():
def test_structured_converter_upstream(): def test_structured_converter_upstream():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [structured.NewJSONHTTPCloudEventConverter()])
structured.NewJSONHTTPCloudEventConverter()
]
)
event = m.FromRequest( event = m.FromRequest(
v02.Event(), v02.Event(),
{"Content-Type": "application/cloudevents+json"}, {"Content-Type": "application/cloudevents+json"},
io.StringIO(json.dumps(data.ce)), io.StringIO(json.dumps(data.ce)),
lambda x: x.read() lambda x: x.read(),
) )
assert event is not None assert event is not None
@ -60,41 +54,40 @@ def test_structured_converter_upstream():
def test_binary_converter_v01(): def test_binary_converter_v01():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [binary.NewBinaryHTTPCloudEventConverter()])
binary.NewBinaryHTTPCloudEventConverter()
]
)
pytest.raises( pytest.raises(
exceptions.UnsupportedEventConverter, exceptions.UnsupportedEventConverter,
m.FromRequest, m.FromRequest,
v01.Event, {}, None, lambda x: x) v01.Event,
{},
None,
lambda x: x,
)
def test_unsupported_converter_v01(): def test_unsupported_converter_v01():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [structured.NewJSONHTTPCloudEventConverter()])
structured.NewJSONHTTPCloudEventConverter()
]
)
pytest.raises( pytest.raises(
exceptions.UnsupportedEventConverter, exceptions.UnsupportedEventConverter,
m.FromRequest, m.FromRequest,
v01.Event, {}, None, lambda x: x) v01.Event,
{},
None,
lambda x: x,
)
def test_structured_converter_v01(): def test_structured_converter_v01():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [structured.NewJSONHTTPCloudEventConverter()])
structured.NewJSONHTTPCloudEventConverter()
]
)
event = m.FromRequest( event = m.FromRequest(
v01.Event(), v01.Event(),
{"Content-Type": "application/cloudevents+json"}, {"Content-Type": "application/cloudevents+json"},
io.StringIO(json.dumps(data.ce)), io.StringIO(json.dumps(data.ce)),
lambda x: x.read() lambda x: x.read(),
) )
assert event is not None assert event is not None
@ -109,7 +102,7 @@ def test_default_http_marshaller_with_structured():
v02.Event(), v02.Event(),
{"Content-Type": "application/cloudevents+json"}, {"Content-Type": "application/cloudevents+json"},
io.StringIO(json.dumps(data.ce)), io.StringIO(json.dumps(data.ce)),
lambda x: x.read() lambda x: x.read(),
) )
assert event is not None assert event is not None
assert event.Get("type") == (data.ce_type, True) assert event.Get("type") == (data.ce_type, True)
@ -120,8 +113,7 @@ def test_default_http_marshaller_with_binary():
m = marshaller.NewDefaultHTTPMarshaller() m = marshaller.NewDefaultHTTPMarshaller()
event = m.FromRequest( event = m.FromRequest(
v02.Event(), v02.Event(), data.headers,
data.headers,
io.StringIO(json.dumps(data.body)), io.StringIO(json.dumps(data.body)),
json.load json.load
) )
@ -133,17 +125,14 @@ def test_default_http_marshaller_with_binary():
def test_unsupported_event_configuration(): def test_unsupported_event_configuration():
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller(
[ [binary.NewBinaryHTTPCloudEventConverter()])
binary.NewBinaryHTTPCloudEventConverter()
]
)
pytest.raises( pytest.raises(
exceptions.UnsupportedEventConverter, exceptions.UnsupportedEventConverter,
m.FromRequest, m.FromRequest,
v01.Event(), v01.Event(),
{"Content-Type": "application/cloudevents+json"}, {"Content-Type": "application/cloudevents+json"},
io.StringIO(json.dumps(data.ce)), io.StringIO(json.dumps(data.ce)),
lambda x: x.read() lambda x: x.read(),
) )
@ -152,12 +141,12 @@ def test_invalid_data_unmarshaller():
pytest.raises( pytest.raises(
exceptions.InvalidDataUnmarshaller, exceptions.InvalidDataUnmarshaller,
m.FromRequest, m.FromRequest,
v01.Event(), {}, None, None) v01.Event(), {}, None, None
)
def test_invalid_data_marshaller(): def test_invalid_data_marshaller():
m = marshaller.NewDefaultHTTPMarshaller() m = marshaller.NewDefaultHTTPMarshaller()
pytest.raises( pytest.raises(
exceptions.InvalidDataMarshaller, exceptions.InvalidDataMarshaller, m.ToRequest, v01.Event(), "blah", None
m.ToRequest, )
v01.Event(), "blah", None)

View File

@ -27,13 +27,13 @@ from cloudevents.tests import data
def test_event_pipeline_upstream(): def test_event_pipeline_upstream():
event = ( event = (
v02.Event(). v02.Event()
SetContentType(data.contentType). .SetContentType(data.contentType)
SetData(data.body). .SetData(data.body)
SetEventID(data.ce_id). .SetEventID(data.ce_id)
SetSource(data.source). .SetSource(data.source)
SetEventTime(data.eventTime). .SetEventTime(data.eventTime)
SetEventType(data.ce_type) .SetEventType(data.ce_type)
) )
m = marshaller.NewDefaultHTTPMarshaller() m = marshaller.NewDefaultHTTPMarshaller()
new_headers, body = m.ToRequest(event, converters.TypeBinary, lambda x: x) new_headers, body = m.ToRequest(event, converters.TypeBinary, lambda x: x)
@ -44,29 +44,25 @@ def test_event_pipeline_upstream():
assert "ce-id" in new_headers assert "ce-id" in new_headers
assert "ce-time" in new_headers assert "ce-time" in new_headers
assert "content-type" in new_headers assert "content-type" in new_headers
assert isinstance(body, io.BytesIO) assert isinstance(body, str)
assert data.body == body.read().decode("utf-8") assert data.body == body
def test_event_pipeline_v01(): def test_event_pipeline_v01():
event = ( event = (
v01.Event(). v01.Event()
SetContentType(data.contentType). .SetContentType(data.contentType)
SetData(data.body). .SetData(data.body)
SetEventID(data.ce_id). .SetEventID(data.ce_id)
SetSource(data.source). .SetSource(data.source)
SetEventTime(data.eventTime). .SetEventTime(data.eventTime)
SetEventType(data.ce_type) .SetEventType(data.ce_type)
)
m = marshaller.NewHTTPMarshaller(
[
structured.NewJSONHTTPCloudEventConverter()
]
) )
m = marshaller.NewHTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()])
_, body = m.ToRequest(event, converters.TypeStructured, lambda x: x) _, body = m.ToRequest(event, converters.TypeStructured, lambda x: x)
assert isinstance(body, io.BytesIO) assert isinstance(body, io.BytesIO)
new_headers = json.load(io.TextIOWrapper(body, encoding='utf-8')) new_headers = json.load(io.TextIOWrapper(body, encoding="utf-8"))
assert new_headers is not None assert new_headers is not None
assert "cloudEventsVersion" in new_headers assert "cloudEventsVersion" in new_headers
assert "eventType" in new_headers assert "eventType" in new_headers

View File

@ -33,7 +33,7 @@ def test_binary_event_to_request_upstream():
v02.Event(), v02.Event(),
{"Content-Type": "application/cloudevents+json"}, {"Content-Type": "application/cloudevents+json"},
io.StringIO(json.dumps(data.ce)), io.StringIO(json.dumps(data.ce)),
lambda x: x.read() lambda x: x.read(),
) )
assert event is not None assert event is not None
@ -50,10 +50,7 @@ def test_structured_event_to_request_upstream():
m = marshaller.NewDefaultHTTPMarshaller() m = marshaller.NewDefaultHTTPMarshaller()
http_headers = {"content-type": "application/cloudevents+json"} http_headers = {"content-type": "application/cloudevents+json"}
event = m.FromRequest( event = m.FromRequest(
v02.Event(), v02.Event(), http_headers, io.StringIO(json.dumps(data.ce)), lambda x: x.read()
http_headers,
io.StringIO(json.dumps(data.ce)),
lambda x: x.read()
) )
assert event is not None assert event is not None
assert event.Get("type") == (data.ce_type, True) assert event.Get("type") == (data.ce_type, True)
@ -69,17 +66,10 @@ def test_structured_event_to_request_upstream():
def test_structured_event_to_request_v01(): def test_structured_event_to_request_v01():
copy_of_ce = copy.deepcopy(data.ce) copy_of_ce = copy.deepcopy(data.ce)
m = marshaller.NewHTTPMarshaller( m = marshaller.NewHTTPMarshaller([structured.NewJSONHTTPCloudEventConverter()])
[
structured.NewJSONHTTPCloudEventConverter()
]
)
http_headers = {"content-type": "application/cloudevents+json"} http_headers = {"content-type": "application/cloudevents+json"}
event = m.FromRequest( event = m.FromRequest(
v01.Event(), v01.Event(), http_headers, io.StringIO(json.dumps(data.ce)), lambda x: x.read()
http_headers,
io.StringIO(json.dumps(data.ce)),
lambda x: x.read()
) )
assert event is not None assert event is not None
assert event.Get("type") == (data.ce_type, True) assert event.Get("type") == (data.ce_type, True)

View File

@ -0,0 +1,77 @@
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudevents.sdk import marshaller
from cloudevents.sdk import converters
from cloudevents.sdk.event import v02
from sanic import Sanic
from sanic import response
from cloudevents.tests import data as test_data
m = marshaller.NewDefaultHTTPMarshaller()
app = Sanic(__name__)
@app.route("/is-ok", ["POST"])
async def is_ok(request):
m.FromRequest(
v02.Event(),
dict(request.headers),
request.body,
lambda x: x
)
return response.text("OK")
@app.route("/echo", ["POST"])
async def echo(request):
event = m.FromRequest(
v02.Event(),
dict(request.headers),
request.body,
lambda x: x
)
hs, body = m.ToRequest(event, converters.TypeBinary, lambda x: x)
return response.text(body, headers=hs)
def test_reusable_marshaller():
for i in range(10):
_, r = app.test_client.post(
"/is-ok", headers=test_data.headers, data=test_data.body
)
assert r.status == 200
def test_web_app_integration():
_, r = app.test_client.post(
"/is-ok", headers=test_data.headers, data=test_data.body
)
assert r.status == 200
def test_web_app_echo():
_, r = app.test_client.post("/echo", headers=test_data.headers, data=test_data.body)
assert r.status == 200
event = m.FromRequest(v02.Event(), dict(r.headers), r.body, lambda x: x)
assert event is not None
props = event.Properties()
for key in test_data.headers.keys():
if key == "Content-Type":
assert "contenttype" in props
else:
assert key.lstrip("ce-") in props

View File

@ -1,4 +1,10 @@
flake8<2.7.0,>=2.6.0 flake8
hacking==1.1.0 pep8-naming==0.5.0
flake8-import-order
flake8-print
flake8-strict
pytest==4.0.0 pytest==4.0.0
pytest-cov==2.4.0 pytest-cov==2.4.0
# web app tests
sanic
aiohttp

View File

@ -18,7 +18,8 @@ whitelist_externals = find
go go
docker docker
[testenv:pep8] [testenv:pep8]
commands = flake8 commands =
flake8
[testenv:venv] [testenv:venv]
commands = {posargs} commands = {posargs}
@ -30,6 +31,6 @@ commands = pytest -v -s --tb=long --cov=cloudevents {toxinidir}/cloudevents/test
commands = pytest -v -s --tb=long --cov=cloudevents {toxinidir}/cloudevents/tests commands = pytest -v -s --tb=long --cov=cloudevents {toxinidir}/cloudevents/tests
[flake8] [flake8]
ignore = H405,H404,H403,H401,H306 ignore = H405,H404,H403,H401,H306,S101,N802,N803,N806,I202,I201
show-source = True show-source = True
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,docs,venv,.venv,docs,etc exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,docs,venv,.venv,docs,etc,samples,tests