def test_validate_function_catches_value_errors_and_raises_invalid(self): func = Mock(side_effect=ValueError) with self.assertRaises(Invalid) as cm: Schema._validate_function(func, "abc", []) self.assertEqual(str(cm.exception), "Invalid value given") self.assertEqual(cm.exception.data, "abc") with self.assertRaises(Invalid) as cm: Schema._validate_function(func, "def", ["a"]) self.assertEqual(str(cm.exception), "Invalid value given @ data[a]") self.assertEqual(cm.exception.data, "def")
def test_validate_function_catches_and_raises_invalid(self): invalid = Invalid("abc", 4) func = Mock(side_effect=invalid) with self.assertRaises(Invalid) as cm: Schema._validate_function(func, 2, []) self.assertEqual(str(cm.exception), "abc") self.assertEqual(cm.exception.data, 4) with self.assertRaises(Invalid) as cm: Schema._validate_function(func, 3, [1, "a"]) self.assertEqual(str(cm.exception), "abc @ data[1][a]") self.assertEqual(cm.exception.data, 4)
def test_validate_dict_passes_undefined_if_a_value_isnt_in_data(self): mocked = Mock(return_value={}) with patch.object(Schema, "_validate", mocked): data = {"a": 1, "c": 2} schema = {"a": int, "b": int, "c": int, "d": int} Schema._validate_dict(schema, data, []) mocked.assert_has_calls([ call(int, 1, ["a"]), call(int, Undefined, ["b"]), call(int, 2, ["c"]), call(int, Undefined, ["d"]) ], any_order=True)
def test_validate_dict_calls_validate_once_for_each_key(self): # TODO: Should we be checking this, or just checking the output? mocked = Mock(return_value={}) with patch.object(Schema, "_validate", mocked): schema = {"a": unicode, "c": int, "e": unicode, "g": bool} data = {"a": "b", "c": 4, "e": "f", "g": True} Schema._validate_dict(schema, data, []) mocked.assert_has_calls([ call(unicode, "b", ["a"]), call(int, 4, ["c"]), call(unicode, "f", ["e"]), call(bool, True, ["g"]) ], any_order=True)
def test_validate_list_calls_validate_for_schema_values_as_necessary(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("") return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): Schema._validate_list([int, unicode], [], []) self.assertFalse(mocked.called) mocked.reset_mock() Schema._validate_list([int, unicode], ["a"], []) mocked.assert_has_calls([ call(int, "a", [0]), call(unicode, "a", [0]) ]) mocked.reset_mock() Schema._validate_list([int, unicode], [1], []) mocked.assert_called_once_with(int, 1, [0]) mocked.reset_mock() with self.assertRaises(InvalidGroup): Schema._validate_list([int, unicode], [None], []) mocked.assert_has_calls([ call(int, None, [0]), call(unicode, None, [0]) ])
def test_calling_a_schema_raises_invalid_group_when_data_is_invalid(self): invalid = Invalid("abc") mocked = Mock(side_effect=invalid) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: instance = Schema({"a": int}) instance({"a": 1}) self.assertEqual(map(str, cm.exception.errors), ["abc"]) invalid = InvalidGroup("", [Invalid("def"), Invalid("ghi")]) mocked = Mock(side_effect=invalid) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: instance = Schema({"a": int}) instance({"a": 1}) self.assertEqual(map(str, cm.exception.errors), ["def", "ghi"])
def test_validate_function_catches_and_raises_invalid_group(self): invalid_group = InvalidGroup( "", [Invalid("abc", 8), Invalid("def", 9, [3])], 4) func = Mock(side_effect=invalid_group) with self.assertRaises(InvalidGroup) as cm: Schema._validate_function(func, 2, []) ex = cm.exception self.assertEqual(map(str, ex.errors), ["abc", "def @ data[3]"]) self.assertEqual([e.data for e in ex.errors], [8, 9]) self.assertEqual(ex.data, 4) with self.assertRaises(InvalidGroup) as cm: Schema._validate_function(func, 3, [1, "a"]) ex = cm.exception self.assertEqual(map(str, ex.errors), ["abc @ data[1][a]", "def @ data[1][a][3]"]) self.assertEqual([e.data for e in ex.errors], [8, 9]) self.assertEqual(ex.data, 4)
def test_validate_function_catches_and_raises_invalid_group(self): invalid_group = InvalidGroup( "", [Invalid("abc", 8), Invalid("def", 9, [3])], 4) func = Mock(side_effect=invalid_group) with self.assertRaises(InvalidGroup) as cm: Schema._validate_function(func, 2, []) ex = cm.exception self.assertEqual(map(str, ex.errors), ["abc", "def @ data[3]"]) self.assertEqual([e.data for e in ex.errors], [8, 9]) self.assertEqual(ex.data, 4) with self.assertRaises(InvalidGroup) as cm: Schema._validate_function(func, 3, [1, "a"]) ex = cm.exception self.assertEqual( map(str, ex.errors), ["abc @ data[1][a]", "def @ data[1][a][3]"]) self.assertEqual([e.data for e in ex.errors], [8, 9]) self.assertEqual(ex.data, 4)
def test_validate_dict_returns_all_data_if_schema_is_empty(self): self.assertEqual(Schema._validate_dict({}, { "a": 1, "b": None }, []), { "a": 1, "b": None })
def test_validate_dict_returns_defined_values_from_validation(self): def mock_return(schema, data, path): return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": 1, "c": 2, "d": 3} self.assertEqual(Schema._validate_dict(schema, data, []), data)
def test_validate_list_raises_invalid_group_on_invalid_data(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("err", data, path) return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: schema = [unicode, int] data = [1, "a", 1.2, "b", 2, None] Schema._validate_list(schema, data, ["a", 2]) ex = cm.exception self.assertEqual(map(str, ex.errors), ["err @ data[a][2][2]", "err @ data[a][2][5]"]) self.assertEqual([e.data for e in ex.errors], [1.2, None]) self.assertEqual(ex.data, [1, "a", ex.errors[0], "b", 2, ex.errors[1]])
def test_validate_list_raises_invalid_group_on_invalid_data(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("err", data, path) return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: schema = [unicode, int] data = [1, "a", 1.2, "b", 2, None] Schema._validate_list(schema, data, ["a", 2]) ex = cm.exception self.assertEqual( map(str, ex.errors), ["err @ data[a][2][2]", "err @ data[a][2][5]"]) self.assertEqual([e.data for e in ex.errors], [1.2, None]) self.assertEqual( ex.data, [1, "a", ex.errors[0], "b", 2, ex.errors[1]])
def test_validate_list_calls_validate_for_each_schema_data_combo(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("") return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): Schema._validate_list([int, unicode], ["a", "b"], []) mocked.assert_has_calls([ call(int, "a", [0]), call(unicode, "a", [0]), call(int, "b", [1]), call(unicode, "b", [1]) ]) mocked.reset_mock() with self.assertRaises(InvalidGroup): Schema._validate_list([int, unicode], ["a", 1, None], []) mocked.assert_has_calls([ call(int, "a", [0]), call(unicode, "a", [0]), call(int, 1, [1]), call(int, None, [2]), call(unicode, None, [2]) ]) mocked.reset_mock() Schema._validate_list([int, unicode], [1, 2], []) mocked.assert_has_calls([ call(int, 1, [0]), call(int, 2, [1]), ])
def test_validate_dict_appends_the_key_to_the_current_path(self): # TODO: Should we be checking this, or just the output? # TODO: If we do decide to keep this, it should be combined with above mocked = Mock(return_value={}) with patch.object(Schema, "_validate", mocked): schema = {"a": int, "b": int, "c": int} data = {"a": 1, "b": 2, "c": 3} Schema._validate_dict(schema, data, []) mocked.assert_has_calls([ call(int, 1, ["a"]), call(int, 2, ["b"]), call(int, 3, ["c"]) ], any_order=True) mocked.reset_mock() Schema._validate_dict(schema, data, ["f"]) mocked.assert_has_calls([ call(int, 1, ["f", "a"]), call(int, 2, ["f", "b"]), call(int, 3, ["f", "c"]) ], any_order=True) mocked.reset_mock() Schema._validate_dict(schema, data, ["f", "g", 1]) mocked.assert_has_calls([ call(int, 1, ["f", "g", 1, "a"]), call(int, 2, ["f", "g", 1, "b"]), call(int, 3, ["f", "g", 1, "c"]) ], any_order=True)
def test_validate_dict_raises_invalid_group_for_invalid_data(self): def mock_return(schema, data, path): if data is Undefined: return Undefined if not isinstance(data, schema): raise Invalid("err", data, path) return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": None, "b": 1, "c": 2} Schema._validate_dict(schema, data, []) self.assertEqual(map(str, cm.exception.errors), ["err @ data[a]"]) self.assertEqual([e.data for e in cm.exception.errors], [None]) with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": 1, "c": 1.1, "d": "abc"} Schema._validate_dict(schema, data, []) self.assertEqual( map(str, cm.exception.errors), ["err @ data[c]", "err @ data[d]"]) self.assertEqual( [e.data for e in cm.exception.errors], [1.1, "abc"]) with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": 1, "b": 2, "c": 2.0, "d": None} Schema._validate_dict(schema, data, ["x", 1]) self.assertEqual( map(str, cm.exception.errors), ["err @ data[x][1][c]", "err @ data[x][1][d]"]) self.assertEqual( [e.data for e in cm.exception.errors], [2.0, None])
def test_validate_dict_raises_invalid_group_for_invalid_data(self): def mock_return(schema, data, path): if data is Undefined: return Undefined if not isinstance(data, schema): raise Invalid("err", data, path) return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": None, "b": 1, "c": 2} Schema._validate_dict(schema, data, []) self.assertEqual(map(str, cm.exception.errors), ["err @ data[a]"]) self.assertEqual([e.data for e in cm.exception.errors], [None]) with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": 1, "c": 1.1, "d": "abc"} Schema._validate_dict(schema, data, []) self.assertEqual(map(str, cm.exception.errors), ["err @ data[c]", "err @ data[d]"]) self.assertEqual([e.data for e in cm.exception.errors], [1.1, "abc"]) with self.assertRaises(InvalidGroup) as cm: schema = {"a": int, "b": int, "c": int, "d": int} data = {"a": 1, "b": 2, "c": 2.0, "d": None} Schema._validate_dict(schema, data, ["x", 1]) self.assertEqual(map(str, cm.exception.errors), ["err @ data[x][1][c]", "err @ data[x][1][d]"]) self.assertEqual([e.data for e in cm.exception.errors], [2.0, None])
def test_validate_list_returns_values_from_validation(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("", data, path) if schema == unicode: return "{0}r".format(data) return data + 1 mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): schema = [unicode, int] data = ["z", 1, "y", 2, "x", 3, "w"] val = Schema._validate_list(schema, data, []) self.assertEqual(val, ["zr", 2, "yr", 3, "xr", 4, "wr"])
def test_basic_integration_test(self): s = Schema({ "abc": All(coerce_to(str), when_empty(Invalid)), "def": All(int, when_empty(Invalid)) }) with self.assertRaises(InvalidGroup) as cm: s({}) self.assertEqual( map(str, cm.exception.errors), [ "A value is required @ data[abc]", "A value is required @ data[def]" ], ) a = s({"abc": 1, "def": 2}) self.assertEqual(a, {"abc": "1", "def": 2}) s = Schema(All({"abc": coerce_to(str), "def": int})) with self.assertRaises(InvalidGroup) as cm: s({"abc": 1, "def": "abc"}) self.assertEqual(map(str, cm.exception.errors), ["Expected int @ data[def]"]) s = Schema( All({ "abc": All(coerce_to(str), when_empty(Invalid)), "def": All(int, when_empty(Invalid)) })) with self.assertRaises(InvalidGroup) as cm: s({}) self.assertEqual( map(str, cm.exception.errors), [ "A value is required @ data[abc]", "A value is required @ data[def]" ], )
def test_validate_type_raises_invalid_on_invalid_data(self): with self.assertRaises(Invalid) as cm: Schema._validate_type(unicode, 1, []) self.assertEqual(str(cm.exception), "Expected unicode") self.assertEqual(cm.exception.data, 1) with self.assertRaises(Invalid) as cm: Schema._validate_type(int, "abc", [1]) self.assertEqual(str(cm.exception), "Expected int @ data[1]") self.assertEqual(cm.exception.data, "abc") with self.assertRaises(Invalid) as cm: Schema._validate_type(bool, None, ["a", 0, "b"]) self.assertEqual(str(cm.exception), "Expected bool @ data[a][0][b]") self.assertEqual(cm.exception.data, None)
def test_validate_dict_raises_invalid_if_not_given_a_dictionary(self): with self.assertRaises(Invalid) as cm: Schema._validate_dict({}, [], []) self.assertEqual(str(cm.exception), "Expected an object") self.assertEqual(cm.exception.data, []) with self.assertRaises(Invalid) as cm: Schema._validate_dict({}, Undefined, [0]) self.assertEqual(str(cm.exception), "Expected an object @ data[0]") self.assertEqual(cm.exception.data, Undefined) with self.assertRaises(Invalid) as cm: Schema._validate_dict({}, None, ["a", "b"]) self.assertEqual(str(cm.exception), "Expected an object @ data[a][b]") self.assertEqual(cm.exception.data, None)
def test_validate_list_raises_invalid_if_not_given_a_list(self): with self.assertRaises(Invalid) as cm: Schema._validate_list([], {}, []) self.assertEqual(str(cm.exception), "Expected a list") self.assertEqual(cm.exception.data, {}) with self.assertRaises(Invalid) as cm: Schema._validate_list([], Undefined, [1]) self.assertEqual(str(cm.exception), "Expected a list @ data[1]") self.assertEqual(cm.exception.data, Undefined) with self.assertRaises(Invalid) as cm: Schema._validate_list([], None, ["a", "b"]) self.assertEqual(str(cm.exception), "Expected a list @ data[a][b]") self.assertEqual(cm.exception.data, None)
def test_validate_list_calls_validate_for_each_data_entry(self): mocked = Mock(return_value=1) with patch.object(Schema, "_validate", mocked): Schema._validate_list([int], [], []) self.assertFalse(mocked.called) mocked.reset_mock() Schema._validate_list([int], [1], []) mocked.assert_called_once_with(int, 1, [0]) mocked.reset_mock() Schema._validate_list([int], [1, 2, 3], []) mocked.assert_has_calls( [call(int, 1, [0]), call(int, 2, [1]), call(int, 3, [2])])
def test_validate_list_calls_validate_for_each_data_entry(self): mocked = Mock(return_value=1) with patch.object(Schema, "_validate", mocked): Schema._validate_list([int], [], []) self.assertFalse(mocked.called) mocked.reset_mock() Schema._validate_list([int], [1], []) mocked.assert_called_once_with(int, 1, [0]) mocked.reset_mock() Schema._validate_list([int], [1, 2, 3], []) mocked.assert_has_calls([ call(int, 1, [0]), call(int, 2, [1]), call(int, 3, [2]) ])
def test_validate_list_calls_validate_for_schema_values_as_necessary(self): def mock_return(schema, data, path): if not isinstance(data, schema): raise Invalid("") return data mocked = Mock(side_effect=mock_return) with patch.object(Schema, "_validate", mocked): Schema._validate_list([int, unicode], [], []) self.assertFalse(mocked.called) mocked.reset_mock() Schema._validate_list([int, unicode], ["a"], []) mocked.assert_has_calls( [call(int, "a", [0]), call(unicode, "a", [0])]) mocked.reset_mock() Schema._validate_list([int, unicode], [1], []) mocked.assert_called_once_with(int, 1, [0]) mocked.reset_mock() with self.assertRaises(InvalidGroup): Schema._validate_list([int, unicode], [None], []) mocked.assert_has_calls( [call(int, None, [0]), call(unicode, None, [0])])
def test_validate_list_appends_the_entry_index_to_the_current_path(self): mocked = Mock(return_value=1) with patch.object(Schema, "_validate", mocked): data = ["a", "b", "c"] Schema._validate_list([unicode], data, []) mocked.assert_has_calls([ call(unicode, "a", [0]), call(unicode, "b", [1]), call(unicode, "c", [2]) ]) mocked.reset_mock() Schema._validate_list([unicode], data, ["a"]) mocked.assert_has_calls([ call(unicode, "a", ["a", 0]), call(unicode, "b", ["a", 1]), call(unicode, "c", ["a", 2]) ]) mocked.reset_mock() Schema._validate_list([unicode], data, ["b", "c", 2]) mocked.assert_has_calls([ call(unicode, "a", ["b", "c", 2, 0]), call(unicode, "b", ["b", "c", 2, 1]), call(unicode, "c", ["b", "c", 2, 2]) ])
def test_validate_function_returns_the_value_from_the_function(self): func = Mock(return_value=1) self.assertEqual(Schema._validate_function(func, "abc", []), 1)
def test_validate_list_returns_unmodified_data_when_schema_is_empty(self): self.assertEqual( Schema._validate_list([], [1, 2, 3], []), [1, 2, 3])
def test_calling_a_schema_returns_data_when_data_is_valid(self): mocked = Mock(return_value=1) with patch.object(Schema, "_validate", mocked): instance = Schema({"a": int}) retval = instance({"a": 1}) self.assertEqual(retval, 1)
def test_validate_calls_validate_type_with_unicode_when_passed_a_str(self): with patch.object(Schema, "_validate_type") as mocked: data = str("a string") Schema._validate(str, data, []) mocked.assert_called_once_with(unicode, data, [])
def test_more_advanced_integration_test(self): def percents_add_to_100(data): total = 0 for project in data: total += project["percentage"] if total != 100: raise Invalid("the percentages must add up to 100") return data def check_required_by(data): return "required_by_type" in data and data[ "required_by_type"] == "later" s = Schema( Chain( { "description": All(str, when_empty(Invalid)), "justification": All(str, when_empty(Invalid)), "is_tech_purchase": All(bool, when_empty(False)), "projects": All([{ "id": All(int, when_empty(Undefined)), "project_id": All(int, when_empty(Invalid)), "percentage": All(int, in_range(1, 100), when_empty(Invalid)) }], percents_add_to_100), "vendor": All(str, when_empty(Invalid)), "vendor_id": All(int, when_empty(Invalid)), "required_by_type": All(str, in_list(["now", "later"])), "date_required_by": All(int, when_empty(Invalid)) }, require_one("vendor_id", "vendor"), require_if("date_required_by", check_required_by))) data = { "description": "abc", "justification": "def", "projects": [{ "project_id": 4, "percentage": 10 }, { "id": 2, "project_id": 5, "percentage": 90 }], "vendor": "ABC co.", "required_by_type": "later", "date_required_by": 1234 } val = s(data) self.assertEqual( val, { "description": "abc", "justification": "def", "is_tech_purchase": False, "projects": [{ "project_id": 4, "percentage": 10 }, { "id": 2, "project_id": 5, "percentage": 90 }], "vendor": "ABC co.", "required_by_type": "later", "date_required_by": 1234 })
def test_validate_list_returns_unmodified_data_when_schema_is_empty(self): self.assertEqual(Schema._validate_list([], [1, 2, 3], []), [1, 2, 3])
def test_validate_dict_returns_all_data_if_schema_is_empty(self): self.assertEqual( Schema._validate_dict({}, {"a": 1, "b": None}, []), {"a": 1, "b": None})
def test_creating_a_schema_saves_the_passed_in_schema(self): schema = {"a": "b", "k": "l", "y": 3} instance = Schema(schema) self.assertEqual(instance.schema, {"a": "b", "k": "l", "y": 3})
def test_validate_type_returns_undefined_when_passed_undefined(self): self.assertEqual(Schema._validate_type(unicode, Undefined, []), Undefined) self.assertEqual(Schema._validate_type(int, Undefined, []), Undefined)
def test_validate_type_returns_the_data_when_valid(self): self.assertEqual(Schema._validate_type(unicode, "", []), "") self.assertEqual(Schema._validate_type(int, 0, []), 0) self.assertEqual(Schema._validate_type(bool, False, []), False)
def test_validate_type_returns_undefined_when_passed_undefined(self): self.assertEqual( Schema._validate_type(unicode, Undefined, []), Undefined) self.assertEqual( Schema._validate_type(int, Undefined, []), Undefined)
def test_validate_calls_validate_list_when_passed_a_list_schema(self): with patch.object(Schema, "_validate_list") as mocked: Schema._validate([], [], []) mocked.assert_called_once_with([], [], [])
def test_validate_function_calls_the_function(self): func = Mock() Schema._validate_function(func, "abc", []) func.assert_called_once_with("abc")
def test_validate_calls_validate_function_when_passed_a_function(self): with patch.object(Schema, "_validate_function") as mocked: mock_function = Mock() Schema._validate(mock_function, "data", []) mocked.assert_called_once_with(mock_function, "data", [])
def test_validate_calls_validate_type_when_passed_a_type_schema(self): with patch.object(Schema, "_validate_type") as mocked: Schema._validate(int, 12, []) mocked.assert_called_once_with(int, 12, []) mocked.reset_mock() Schema._validate(float, 12.1, []) mocked.assert_called_once_with(float, 12.1, []) mocked.reset_mock() Schema._validate(unicode, "a string", []) mocked.assert_called_once_with(unicode, "a string", []) mocked.reset_mock() Schema._validate(long, 12345678901234, []) mocked.assert_called_once_with(long, 12345678901234, []) mocked.reset_mock() Schema._validate(bool, True, []) mocked.assert_called_once_with(bool, True, []) mocked.reset_mock() Schema._validate(None, None, []) mocked.assert_called_once_with(None, None, [])