Skip to content

Commit

Permalink
100% test-coverage rule added to tox (#109)
Browse files Browse the repository at this point in the history
* version bump

Signed-off-by: Curtis Mason <cumason@google.com>

* adding tests for marshaller

Signed-off-by: Curtis Mason <cumason@google.com>

* marshaller 100% test-coverage

Signed-off-by: Curtis Mason <cumason@bu.edu>

* bricked some tests

Signed-off-by: Curtis Mason <cumason@bu.edu>

* additional error handling

Signed-off-by: Curtis Mason <cumason@bu.edu>

* 100% test-coverage

Signed-off-by: Curtis Mason <cumason@bu.edu>

* handles empty data and capitalized headers

Signed-off-by: Curtis Mason <cumason@bu.edu>

* 1.1.0 version bump

Signed-off-by: Curtis Mason <cumason@bu.edu>
  • Loading branch information
cumason123 authored Aug 17, 2020
1 parent d95b130 commit 3c26bcf
Show file tree
Hide file tree
Showing 19 changed files with 479 additions and 21 deletions.
2 changes: 1 addition & 1 deletion cloudevents/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.1"
__version__ = "1.1.0"
8 changes: 8 additions & 0 deletions cloudevents/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ class CloudEventMissingRequiredFields(Exception):

class CloudEventTypeErrorRequiredFields(Exception):
pass


class InvalidStructuredJSON(Exception):
pass


class InvalidHeadersFormat(Exception):
pass
4 changes: 2 additions & 2 deletions cloudevents/http/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ def __init__(

if self._attributes["specversion"] not in _required_by_version:
raise cloud_exceptions.CloudEventMissingRequiredFields(
f"Invalid specversion: {self._attributes['specversion']}"
f"Invalid specversion: {self._attributes['specversion']}. "
)
# There is no good way to default 'source' and 'type', so this
# checks for those (or any new required attributes).
required_set = _required_by_version[self._attributes["specversion"]]
if not required_set <= self._attributes.keys():
raise cloud_exceptions.CloudEventMissingRequiredFields(
f"Missing required keys: {required_set - attributes.keys()}"
f"Missing required keys: {required_set - self._attributes.keys()}. "
)

def __eq__(self, other):
Expand Down
26 changes: 21 additions & 5 deletions cloudevents/http/http_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


def from_http(
data: typing.Union[str, bytes],
data: typing.Union[str, bytes, None],
headers: typing.Dict[str, str],
data_unmarshaller: types.UnmarshallerType = None,
):
Expand All @@ -24,6 +24,16 @@ def from_http(
e.g. lambda x: x or lambda x: json.loads(x)
:type data_unmarshaller: types.UnmarshallerType
"""
if data is None:
data = ""

if not isinstance(data, (str, bytes, bytearray)):
raise cloud_exceptions.InvalidStructuredJSON(
"Expected json of type (str, bytes, bytearray), "
f"but instead found {type(data)}. "
)

headers = {key.lower(): value for key, value in headers.items()}
if data_unmarshaller is None:
data_unmarshaller = _json_or_string

Expand All @@ -32,19 +42,25 @@ def from_http(
if is_binary(headers):
specversion = headers.get("ce-specversion", None)
else:
raw_ce = json.loads(data)
try:
raw_ce = json.loads(data)
except json.decoder.JSONDecodeError:
raise cloud_exceptions.InvalidStructuredJSON(
"Failed to read fields from structured event. "
f"The following can not be parsed as json: {data}. "
)
specversion = raw_ce.get("specversion", None)

if specversion is None:
raise cloud_exceptions.CloudEventMissingRequiredFields(
"could not find specversion in HTTP request"
"Specversion was set to None in HTTP request. "
)

event_handler = _obj_by_version.get(specversion, None)

if event_handler is None:
raise cloud_exceptions.CloudEventTypeErrorRequiredFields(
f"found invalid specversion {specversion}"
f"Found invalid specversion {specversion}. "
)

event = marshall.FromRequest(
Expand Down Expand Up @@ -78,7 +94,7 @@ def _to_http(

if event._attributes["specversion"] not in _obj_by_version:
raise cloud_exceptions.CloudEventTypeErrorRequiredFields(
f"Unsupported specversion: {event._attributes['specversion']}"
f"Unsupported specversion: {event._attributes['specversion']}. "
)

event_handler = _obj_by_version[event._attributes["specversion"]]()
Expand Down
2 changes: 1 addition & 1 deletion cloudevents/http/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def default_marshaller(content: any):


def _json_or_string(content: typing.Union[str, bytes]):
if len(content) == 0:
if content is None or len(content) == 0:
return None
try:
return json.loads(content)
Expand Down
2 changes: 1 addition & 1 deletion cloudevents/sdk/event/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# TODO(slinkydeveloper) is this really needed?


class EventGetterSetter(object):
class EventGetterSetter(object): # pragma: no cover

# ce-specversion
def CloudEventVersion(self) -> str:
Expand Down
24 changes: 20 additions & 4 deletions cloudevents/sdk/event/v03.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ def ContentType(self) -> str:
def ContentEncoding(self) -> str:
return self.ce__datacontentencoding.get()

@property
def datacontentencoding(self):
return self.ContentEncoding()

def SetEventType(self, eventType: str) -> base.BaseEvent:
self.Set("type", eventType)
return self
Expand Down Expand Up @@ -119,6 +115,26 @@ def SetContentEncoding(self, contentEncoding: str) -> base.BaseEvent:
self.Set("datacontentencoding", contentEncoding)
return self

@property
def datacontentencoding(self):
return self.ContentEncoding()

@datacontentencoding.setter
def datacontentencoding(self, value: str):
self.SetContentEncoding(value)

@property
def subject(self) -> str:
return self.Subject()

@subject.setter
def subject(self, value: str):
self.SetSubject(value)

@property
def schema_url(self) -> str:
return self.SchemaURL()

@schema_url.setter
def schema_url(self, value: str):
self.SetSchemaURL(value)
16 changes: 16 additions & 0 deletions cloudevents/sdk/event/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,19 @@ def SetData(self, data: object) -> base.BaseEvent:
def SetExtensions(self, extensions: dict) -> base.BaseEvent:
self.Set("extensions", extensions)
return self

@property
def schema(self) -> str:
return self.Schema()

@schema.setter
def schema(self, value: str):
self.SetSchema(value)

@property
def subject(self) -> str:
return self.Subject()

@subject.setter
def subject(self, value: str):
self.SetSubject(value)
33 changes: 33 additions & 0 deletions cloudevents/tests/test_base_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 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.
import pytest

import cloudevents.exceptions as cloud_exceptions
from cloudevents.sdk.event import v1, v03


@pytest.mark.parametrize("event_class", [v1.Event, v03.Event])
def test_unmarshall_binary_missing_fields(event_class):
event = event_class()
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields) as e:
event.UnmarshalBinary({}, "", lambda x: x)
assert "Missing required attributes: " in str(e.value)


@pytest.mark.parametrize("event_class", [v1.Event, v03.Event])
def test_get_nonexistent_optional(event_class):
event = event_class()
event.SetExtensions({"ext1": "val"})
res = event.Get("ext1")
assert res[0] == "val" and res[1] == True
41 changes: 41 additions & 0 deletions cloudevents/tests/test_converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 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.
import pytest

from cloudevents.sdk import exceptions
from cloudevents.sdk.converters import base, binary, structured


def test_binary_converter_raise_unsupported():
with pytest.raises(exceptions.UnsupportedEvent):
cnvtr = binary.BinaryHTTPCloudEventConverter()
cnvtr.read(None, {}, None, None)


def test_base_converters_raise_exceptions():
with pytest.raises(Exception):
cnvtr = base.Converter()
cnvtr.event_supported(None)

with pytest.raises(Exception):
cnvtr = base.Converter()
cnvtr.can_read(None)

with pytest.raises(Exception):
cnvtr = base.Converter()
cnvtr.write(None, None)

with pytest.raises(Exception):
cnvtr = base.Converter()
cnvtr.read(None, None, None, None)
1 change: 0 additions & 1 deletion cloudevents/tests/test_data_encaps_refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ def test_general_structured_properties(event_class):
if key == "content-type":
assert new_headers[key] == http_headers[key]
continue
assert key in copy_of_ce

# Test setters
new_type = str(uuid4())
Expand Down
1 change: 0 additions & 1 deletion cloudevents/tests/test_event_to_request_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,3 @@ def test_structured_event_to_request_upstream(event_class):
if key == "content-type":
assert new_headers[key] == http_headers[key]
continue
assert key in copy_of_ce
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

import cloudevents.exceptions as cloud_exceptions
from cloudevents.http import CloudEvent


Expand Down Expand Up @@ -69,3 +70,47 @@ def test_http_cloudevent_mutates_equality(specversion):
event3.data = '{"name":"paul"}'
assert event2 == event3
assert event1 != event2 and event3 != event1


def test_cloudevent_missing_specversion():
attributes = {"specversion": "0.2", "source": "s", "type": "t"}
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields) as e:
event = CloudEvent(attributes, None)
assert "Invalid specversion: 0.2" in str(e.value)


def test_cloudevent_missing_minimal_required_fields():
attributes = {"type": "t"}
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields) as e:
event = CloudEvent(attributes, None)
assert f"Missing required keys: {set(['source'])}" in str(e.value)

attributes = {"source": "s"}
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields) as e:
event = CloudEvent(attributes, None)
assert f"Missing required keys: {set(['type'])}" in str(e.value)


def test_cloudevent_general_overrides():
event = CloudEvent(
{
"source": "my-source",
"type": "com.test.overrides",
"subject": "my-subject",
},
None,
)
expected_attributes = [
"time",
"source",
"id",
"specversion",
"type",
"subject",
]

assert len(event) == 6
for attribute in expected_attributes:
assert attribute in event
del event[attribute]
assert len(event) == 0
Loading

0 comments on commit 3c26bcf

Please sign in to comment.