Skip to content

Commit 90161ca

Browse files
authored
Add support for nested comparison (#741)
* Add support for nested comparison * Add a reasonable debug message
1 parent 5435570 commit 90161ca

File tree

3 files changed

+102
-29
lines changed

3 files changed

+102
-29
lines changed

src/rpdk/core/contract/resource_client.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,29 @@ def generate_invalid_update_example(self, create_model):
341341
example = override_properties(self.invalid_strategy.example(), overrides)
342342
return {**create_model, **example}
343343

344+
def compare(self, inputs, outputs):
345+
assertion_error_message = (
346+
"All properties specified in the request MUST "
347+
"be present in the model returned, and they MUST"
348+
" match exactly, with the exception of properties"
349+
" defined as writeOnlyProperties in the resource schema"
350+
)
351+
try:
352+
for key in inputs:
353+
if isinstance(inputs[key], dict):
354+
self.compare(inputs[key], outputs[key])
355+
elif isinstance(inputs[key], list):
356+
assert len(inputs[key]) == len(outputs[key])
357+
self.compare_list(inputs[key], outputs[key])
358+
else:
359+
assert inputs[key] == outputs[key], assertion_error_message
360+
except KeyError as e:
361+
raise AssertionError(assertion_error_message) from e
362+
363+
def compare_list(self, inputs, outputs):
364+
for index in range(len(inputs)): # pylint: disable=C0200
365+
self.compare(inputs[index], outputs[index])
366+
344367
@staticmethod
345368
def key_error_safe_traverse(resource_model, write_only_property):
346369
try:

src/rpdk/core/contract/suite/handler_commons.py

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -163,32 +163,7 @@ def test_input_equals_output(resource_client, input_model, output_model):
163163
pruned_output_model = prune_properties_if_not_exist_in_path(
164164
pruned_output_model, pruned_input_model, resource_client.create_only_paths
165165
)
166-
167-
assertion_error_message = (
168-
"All properties specified in the request MUST "
169-
"be present in the model returned, and they MUST"
170-
" match exactly, with the exception of properties"
171-
" defined as writeOnlyProperties in the resource schema"
172-
)
173-
# only comparing properties in input model to those in output model and
174-
# ignoring extraneous properties that maybe present in output model.
175-
try:
176-
for key in pruned_input_model:
177-
if key in resource_client.properties_without_insertion_order:
178-
assert test_unordered_list_match(
179-
pruned_input_model[key], pruned_output_model[key]
180-
)
181-
else:
182-
assert (
183-
pruned_input_model[key] == pruned_output_model[key]
184-
), assertion_error_message
185-
except KeyError as e:
186-
raise AssertionError(assertion_error_message) from e
187-
188-
189-
def test_unordered_list_match(inputs, outputs):
190-
assert len(inputs) == len(outputs)
191-
try:
192-
assert all(input in outputs for input in inputs)
193-
except KeyError as exception:
194-
raise AssertionError("lists do not match") from exception
166+
resource_client.compare(
167+
pruned_input_model,
168+
pruned_output_model,
169+
)

tests/contract/test_resource_client.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@
6868
"writeOnlyProperties": ["/properties/d"],
6969
}
7070

71+
SCHEMA_WITH_NESTED_PROPERTIES = {
72+
"properties": {
73+
"a": {"type": "string"},
74+
"g": {"type": "number"},
75+
"b": {"$ref": "#/definitions/c"},
76+
"f": {
77+
"type": "array",
78+
"items": {"$ref": "#/definitions/c"},
79+
},
80+
"h": {
81+
"type": "array",
82+
"insertionOrder": "false",
83+
"items": {"$ref": "#/definitions/c"},
84+
},
85+
},
86+
"definitions": {
87+
"c": {
88+
"type": "object",
89+
"properties": {"d": {"type": "integer"}, "e": {"type": "integer"}},
90+
}
91+
},
92+
"readOnlyProperties": ["/properties/a"],
93+
"primaryIdentifier": ["/properties/a"],
94+
"writeOnlyProperties": ["/properties/g"],
95+
}
96+
7197
SCHEMA_WITH_COMPOSITE_KEY = {
7298
"properties": {
7399
"a": {"type": "number"},
@@ -1223,3 +1249,52 @@ def test_generate_update_example_with_composite_key(
12231249
created_resource
12241250
)
12251251
assert updated_resource == {"a": 1, "c": 2, "d": 3}
1252+
1253+
1254+
def test_compare_should_pass(resource_client):
1255+
resource_client._update_schema(SCHEMA_WITH_NESTED_PROPERTIES)
1256+
inputs = {"b": {"d": 1}, "f": [{"d": 1}], "h": [{"d": 1}, {"d": 2}]}
1257+
1258+
outputs = {
1259+
"b": {"d": 1, "e": 3},
1260+
"f": [{"d": 1, "e": 2}],
1261+
"h": [{"d": 1, "e": 3}, {"d": 2}],
1262+
}
1263+
resource_client.compare(inputs, outputs)
1264+
1265+
1266+
def test_compare_should_throw_exception(resource_client):
1267+
resource_client._update_schema(SCHEMA_WITH_NESTED_PROPERTIES)
1268+
inputs = {"b": {"d": 1}, "f": [{"d": 1}], "h": [{"d": 1}], "z": 1}
1269+
1270+
outputs = {
1271+
"b": {"d": 1, "e": 2},
1272+
"f": [{"d": 1}],
1273+
"h": [{"d": 1}],
1274+
}
1275+
try:
1276+
resource_client.compare(inputs, outputs)
1277+
except AssertionError:
1278+
logging.debug("This test expects Assertion Exception to be thrown")
1279+
1280+
1281+
def test_compare_should_throw_key_error(resource_client):
1282+
resource_client._update_schema(SCHEMA_WITH_NESTED_PROPERTIES)
1283+
inputs = {"b": {"d": 1}, "f": [{"d": 1}], "h": [{"d": 1}]}
1284+
1285+
outputs = {"b": {"d": 1, "e": 2}, "f": [{"d": 1, "e": 2}, {"d": 2, "e": 3}]}
1286+
try:
1287+
resource_client.compare(inputs, outputs)
1288+
except AssertionError:
1289+
logging.debug("This test expects Assertion Exception to be thrown")
1290+
1291+
1292+
def test_compare_ordered_list_throws_assertion_exception(resource_client):
1293+
resource_client._update_schema(SCHEMA_WITH_NESTED_PROPERTIES)
1294+
inputs = {"b": {"d": 1}, "f": [{"d": 1}], "h": [{"d": 1}]}
1295+
1296+
outputs = {"b": {"d": 1, "e": 2}, "f": [{"e": 2}, {"d": 2, "e": 3}]}
1297+
try:
1298+
resource_client.compare(inputs, outputs)
1299+
except AssertionError:
1300+
logging.debug("This test expects Assertion Exception to be thrown")

0 commit comments

Comments
 (0)