class TestJsonValidate(unittest.TestCase):
- def test_empty_object(self):
+ def test_string_empty(self):
+ actual = order_json_keys('', {"type": "string"})
+ self.assertEqual('', actual)
+
+ def test_string_valid(self):
+ actual = order_json_keys('äüß', {"type": "string"})
+ self.assertEqual('äüß', actual)
+
+ def test_string_wrong_types(self):
+ for value in [True, False, None, 0, 1, 0.5, [], ['abc'], {}, {'a': 'b'}]:
+ with self.subTest(f'Try invalid value {value}.', value=value):
+ with self.assertRaises(ValueError):
+ order_json_keys(value, {"type": "string"})
+
+ def test_number_valid(self):
+ for value in [0, 1, -10, 8.8, 0.0, -1024.9]:
+ with self.subTest(f'Valid number {value}.', value=value):
+ actual = order_json_keys(value, {"type": "number"})
+ self.assertEqual(value, actual)
+
+ def test_number_wrong_types(self):
+ for value in [True, False, None, '', 'abc', [], [0], [1], {}, {'a': 1}]:
+ with self.subTest(f'Try invalid value {value}.', value=value):
+ with self.assertRaises(ValueError):
+ order_json_keys(value, {"type": "number"})
+
+ def test_object_empty(self):
schema = schema_object.copy()
del schema['required']
actual = order_json_keys({}, schema)
schema['additionalProperties'] = False
with self.assertRaises(ValueError):
order_json_keys(value, schema)
+
+ def test_array(self):
+ raise NotImplementedError()
+
+ def test_boolean_valid(self):
+ for value in [False, True]:
+ with self.subTest(f'Valid boolean {value}.', value=value):
+ actual = order_json_keys(value, {"type": "boolean"})
+ self.assertEqual(value, actual)
+
+ def test_boolean_wrong_types(self):
+ for value in [0, 1, -3, 10, 5.5, None, '', 'True', [], [True], [False], {}, {'a': True}]:
+ with self.subTest(f'Try invalid value {value}.', value=value):
+ with self.assertRaises(ValueError):
+ order_json_keys(value, {"type": "boolean"})
+
+ def test_null_valid(self):
+ actual = order_json_keys(None, {"type": "null"})
+ self.assertEqual(None, actual)
+
+ def test_null_wrong_types(self):
+ for value in [True, False, 0, 1, -3, 10, 5.5, '', 'abc', [], [0], [1], {}, {'a': 1}]:
+ with self.subTest(f'Try invalid value {value}.', value=value):
+ with self.assertRaises(ValueError):
+ order_json_keys(value, {"type": "null"})
pass
-def _order_json_keys(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> JsonTypes:
- if sub_schema['type'] == 'object':
- if not isinstance(sub_value, dict):
- raise ValidationError(f'Type of {"".join(path)} needs to be object (Python dict).')
- v = sub_value.copy()
- p = sub_schema['properties']
- result = {}
- for key in p:
- if key in v:
- result[key] = _order_json_keys(v.pop(key), p[key], path + [f"['{key}']"])
- else:
- if key in sub_schema.get('required', []):
- raise ValidationError(f'Required key "{key}" not present ({"".join(path)}).')
- if len(v) > 0:
- if sub_schema.get('additionalProperties', True):
- # strictly speaking additionalProperties could be more complicated than boolean
- result.update(v)
- else:
- raise ValidationError(f'Keys not allowed in {"".join(path)}: {", ".join(v)}')
- return result
+def _order_json_keys_string(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> str:
+ if not isinstance(sub_value, str):
+ raise ValidationError(f'Type of {"".join(path)} needs to be string (Python str).')
+ return sub_value
+
+
+def _order_json_keys_number(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> Union[int, float]:
+ if not isinstance(sub_value, (int, float)) or isinstance(sub_value, bool):
+ raise ValidationError(f'Type of {"".join(path)} needs to be number (Python int or float).')
+ return sub_value
+
+
+def _order_json_keys_object(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> Dict:
+ if not isinstance(sub_value, dict):
+ raise ValidationError(f'Type of {"".join(path)} needs to be object (Python dict).')
+ v = sub_value.copy()
+ p = sub_schema['properties']
+ result = {}
+ for key in p:
+ if key in v:
+ result[key] = _order_json_keys(v.pop(key), p[key], path + [f"['{key}']"])
+ else:
+ if key in sub_schema.get('required', []):
+ raise ValidationError(f'Required key "{key}" not present ({"".join(path)}).')
+ if len(v) > 0:
+ if sub_schema.get('additionalProperties', True):
+ # strictly speaking additionalProperties could be more complicated than boolean
+ result.update(v)
+ else:
+ raise ValidationError(f'Keys not allowed in {"".join(path)}: {", ".join(v)}')
+ return result
+
+
+def _order_json_keys_array(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> List:
+ if not isinstance(sub_value, list):
+ raise ValidationError(f'Type of {"".join(path)} needs to be array (Python list).')
return sub_value
+def _order_json_keys_boolean(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> bool:
+ if not isinstance(sub_value, bool):
+ raise ValidationError(f'Type of {"".join(path)} needs to be boolean (Python bool).')
+ return sub_value
+
+
+def _order_json_keys_null(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> None:
+ if sub_value is not None:
+ raise ValidationError(f'Type of {"".join(path)} needs to be null (Python None).')
+ return sub_value
+
+
+def _order_json_keys(sub_value: JsonTypes, sub_schema: JsonTypes, path: List[str]) -> JsonTypes:
+ return {
+ 'string': _order_json_keys_string,
+ 'number': _order_json_keys_number,
+ 'object': _order_json_keys_object,
+ 'array': _order_json_keys_array,
+ 'boolean': _order_json_keys_boolean,
+ 'null': _order_json_keys_null,
+ }[sub_schema['type']](sub_value, sub_schema, path)
+
+
def order_json_keys(value: JsonTypes, schema: JsonTypes) -> JsonTypes:
return _order_json_keys(value, schema, ['value'])