Skip to content

Commit

Permalink
add Nested schema type tests
Browse files Browse the repository at this point in the history
  • Loading branch information
st3v3nmw committed Jan 19, 2024
1 parent c6fd45a commit e4fd467
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 6 deletions.
2 changes: 1 addition & 1 deletion landscape/client/manager/snapmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def _handle_multiple_snap_tasks(self, message):

# Naively doing this synchronously because each is an HTTP call to the
# snap REST API that returns basically immediately. We poll for their
# completion statuses once they've all been kicked off
# completion statuses once they've all been kicked off.
for snap in snaps:
name = snap["name"]
snap_args = snap.get("args", {})
Expand Down
40 changes: 40 additions & 0 deletions landscape/client/manager/tests/test_snapmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,3 +313,43 @@ def got_result(r):
)

return result.addCallback(got_result)

def test_set_config_sync_error(self):
self.snap_http.set_conf.side_effect = SnapdHttpException(
b'{"result": "whoops"}',
)
self.snap_http.check_changes.return_value = {
"result": [{"id": "1", "status": "Done"}],
}
self.snap_http.list.return_value = SnapdResponse(
"sync", 200, "OK", {"installed": []}
)

result = self.manager.dispatch_message(
{
"type": "set-snap-config",
"operation-id": 123,
"snaps": [
{
"name": "hello",
"config": {"foo": {"bar": "qux", "baz": "quux"}},
}
],
}
)

def got_result(r):
self.assertMessages(
self.broker_service.message_store.get_pending_messages(),
[
{
"type": "operation-result",
"status": FAILED,
"result-text": "{'completed': [], 'errored': [], 'errors': {'hello': "
"'whoops'}}",
"operation-id": 123,
},
],
)

return result.addCallback(got_result)
15 changes: 11 additions & 4 deletions landscape/lib/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,14 @@ def coerce(self, value):


class Nested:
""""""
"""A type that allows nesting of C{list} and C{dict}.
@param schema: A `List`, `Dict`, or `KeyDict` schema.
"""

def __init__(self, schema):
if not isinstance(schema, (List, Dict, KeyDict)):
raise TypeError(f"{schema} does not support nesting")
raise InvalidError(f"{schema} does not support nesting")

self.schema = schema

Expand All @@ -256,7 +259,9 @@ def coerce(self, value):
@coerce.register(list)
def _(self, value):
if not isinstance(self.schema, List):
raise InvalidError(f"{self.schema} must be a list type")
raise InvalidError(
f"{value!r} has type list which doesn't match {self.schema}"
)

new_sequence = []
for subvalue in value:
Expand All @@ -270,7 +275,9 @@ def _(self, value):
@coerce.register(dict)
def _(self, value):
if not isinstance(self.schema, Dict):
raise InvalidError(f"{self.schema} must be a dictionary type")
raise InvalidError(
f"{value!r} has type dict which doesn't match {self.schema}"
)

new_dict = {}
for k, v in value.items():
Expand Down
55 changes: 54 additions & 1 deletion landscape/lib/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from landscape.lib.schema import InvalidError
from landscape.lib.schema import KeyDict
from landscape.lib.schema import List
from landscape.lib.schema import Nested
from landscape.lib.schema import Tuple
from landscape.lib.schema import Unicode

Expand Down Expand Up @@ -171,7 +172,8 @@ def test_key_dict_unknown_key(self):
def test_key_dict_unknown_key_not_strict(self):
self.assertEqual(
KeyDict({"foo": Int()}, strict=False).coerce({"foo": 1, "bar": 2}),
{"foo": 1})
{"foo": 1},
)

def test_key_dict_bad(self):
self.assertRaises(InvalidError, KeyDict({}).coerce, object())
Expand Down Expand Up @@ -224,3 +226,54 @@ def test_dict_inner_bad(self):

def test_dict_wrong_type(self):
self.assertRaises(InvalidError, Dict(Int(), Int()).coerce, 32)

def test_nested_list_correct_types_only(self):
schema = Nested(List(Any(Int(), Unicode())))
data = [[1, "foo"], [[5]]]
self.assertEqual(schema.coerce(data), [[1, "foo"], [[5]]])

def test_nested_list_bad_types_only(self):
schema = Nested(List(Any(Int(), Unicode())))
data = [[5.0]]
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_list_mixed_correct_and_bad_types_only(self):
schema = Nested(List(Any(Int(), Unicode())))
data = [[1.0, "foo"], [[5]]]
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_list_with_dictionary(self):
schema = Nested(List(Any(Int(), Unicode())))
data = {"foo": "bar"}
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_dict_correct_types_only(self):
schema = Nested(Dict(Unicode(), Any(Int(), Unicode())))
data = {"foo": 5, "bar": {"baz": "default"}}
self.assertEqual(
schema.coerce(data), {"foo": 5, "bar": {"baz": "default"}}
)

def test_nested_dict_bad_types_only(self):
schema = Nested(Dict(Unicode(), Any(Int(), Unicode())))
data = {2: 5.0}
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_dict_mixed_correct_and_bad_types_only(self):
schema = Nested(Dict(Unicode(), Any(Int(), Unicode())))
data = {"bar": {"baz": "default"}, 2: 5.0}
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_dict_with_list(self):
schema = Nested(Dict(Unicode(), Any(Int(), Unicode())))
data = []
self.assertRaises(InvalidError, schema.coerce, data)

def test_nested_with_type_that_doesnt_support_nesting(self):
with self.assertRaises(InvalidError):
Nested(Int())

def test_nested_coerce_unsupported_type(self):
schema = Nested(List(Any(Int(), Unicode())))
data = 4
self.assertRaises(InvalidError, schema.coerce, data)

0 comments on commit e4fd467

Please sign in to comment.