private void ChecksForMessage(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult, bool allowSubset) { var dataType = propertySchemaObject.GetDataType(); if (dataType == DataType.Integer) { CheckIntegerType(propertySchemaObject, kv, testMessageResult); } if (dataType == DataType.Number) { CheckNumberType(propertySchemaObject, kv, testMessageResult); } if (dataType == DataType.Boolean) { CheckBooleanType(propertySchemaObject, kv, testMessageResult); } if (dataType == DataType.String) { CheckStringType(propertySchemaObject, kv, testMessageResult); } if (dataType == DataType.Array) { CheckArrayType(propertySchemaObject, kv, testMessageResult); } if (dataType == DataType.Object) { CheckObjectType(propertySchemaObject, kv, testMessageResult, allowSubset); } }
private static void CheckIntegerType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult) { var isInt32 = int.TryParse(kv.Value.ToString(), out _); var isInt64 = long.TryParse(kv.Value.ToString(), out _); // Validate integer data type if (!isInt32 && !isInt64) { AddTypeError(testMessageResult, kv.Value.ToString(), kv.Key, propertySchemaObject.Type); } if (propertySchemaObject.Format != null) { // Validate specific integer formats if (propertySchemaObject.Format.EqualsCaseInsensitive("int32") && !isInt32) { AddFormatError(testMessageResult, kv.Value.ToString(), kv.Key, Format.Int32); } else if (propertySchemaObject.Format.EqualsCaseInsensitive("int64") && !isInt64) { AddFormatError(testMessageResult, kv.Value.ToString(), kv.Key, Format.Int64); } } }
private static void CheckStringType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult) { if (kv.Value != null) { var notGuid = (!Guid.TryParse(kv.Value.ToString(), out _)); var notDateTime = (!DateTime.TryParse(kv.Value.ToString(), out _)); if (propertySchemaObject.Format != null) { var format = propertySchemaObject.GetFormat(); // Validate specific string formats if (format == Format.DateTime && notDateTime) { AddFormatError(testMessageResult, kv.Value.ToString(), kv.Key, Format.DateTime); } } if (notGuid && propertySchemaObject.Reference == "Guid") { AddFormatError(testMessageResult, kv.Value.ToString(), kv.Key, Format.Guid); } } else if (kv.Value == null && propertySchemaObject.Reference == "Guid") { AddFormatError(testMessageResult, null, kv.Key, Format.Guid); } }
private static void CheckNumberType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult) { var stringVal = kv.Value.ToString(); var isDouble = double.TryParse(stringVal, out var doubleVal) && double.IsFinite(doubleVal); var isFloat = float.TryParse(stringVal, out var floatVal) && float.IsFinite(floatVal); // Validate number data type if (!isDouble && !isFloat) { AddTypeError(testMessageResult, stringVal, kv.Key, propertySchemaObject.Type); } if (propertySchemaObject.Format != null) { var format = propertySchemaObject.GetFormat(); // Validate specific number formats if (format == Format.Float && !isFloat) { AddFormatError(testMessageResult, stringVal, kv.Key, Format.Float); } else if (format == Format.Double && !isDouble) { AddFormatError(testMessageResult, stringVal, kv.Key, Format.Double); } } }
private void CheckObjectType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult, bool allowSubset) { CaseInsensitiveDictionary <object> innerObject = null; try { var json = JsonConvert.SerializeObject(kv.Value); innerObject = JsonConvert.DeserializeObject <CaseInsensitiveDictionary <object> >(json); } catch (Exception) { AddTypeError(testMessageResult, kv.Value.ToString(), kv.Key, "Object"); } if (propertySchemaObject.Properties != null) { foreach (var innerProperty in propertySchemaObject.Properties) { if (innerObject != null && innerObject.ContainsKey(innerProperty.Key)) { ChecksForMessage(innerProperty.Value, new KeyValuePair <string, object>($"{kv.Key}{ParentOfSymbol}{innerProperty.Key}", innerObject[innerProperty.Key]), testMessageResult, allowSubset); } else { if (!allowSubset) { AddNotFoundError(testMessageResult, $"{kv.Key}{ParentOfSymbol}{innerProperty.Key}"); } } } } }
private static void AddItemTypeError(TestMessageResult result, string key, DataType dataType, Format format = null) { var modifier = format?.Value ?? dataType.Value; result.IsMessageValid = false; result.MessageErrors.Add( $"An item in the Items array for {key} does not match the required data type ({modifier})."); }
private static void CheckBooleanType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult) { var isBool = bool.TryParse(kv.Value.ToString(), out _); // Validate number data type if (!isBool) { AddTypeError(testMessageResult, kv.Value.ToString(), kv.Key, propertySchemaObject.Type); } }
private void CheckArrayType(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, TestMessageResult testMessageResult) { if (propertySchemaObject.Items == null) { AddRequiredError(testMessageResult, kv.Key, "Array", "Items"); } else { var itemSchema = propertySchemaObject.Items; dynamic itemArray = JsonConvert.DeserializeObject(kv.Value.ToString()); var dataType = itemSchema.GetDataType(); if (itemArray is IEnumerable array) { var count = array.Cast <object>().Count(); if (propertySchemaObject.MinItems != 0 && count < propertySchemaObject.MinItems) { AddArrayMinLengthError(testMessageResult, kv.Key, propertySchemaObject.MinItems); } if (propertySchemaObject.MaxItems != 0 && count > propertySchemaObject.MaxItems) { AddArrayMaxLengthError(testMessageResult, kv.Key, propertySchemaObject.MaxItems); } if (dataType == DataType.String) { TryParseStringArray(propertySchemaObject, kv, itemArray, testMessageResult, itemSchema.Reference == "Guid"); } if (dataType == DataType.Integer) { TryParseIntegerArray(propertySchemaObject, kv, itemArray, testMessageResult); } if (dataType == DataType.Number) { TryParseNumberArray(propertySchemaObject, kv, itemArray, testMessageResult); } } else { AddTypeError(testMessageResult, kv.Value.ToString(), kv.Key, DataType.Array.Value); } if (dataType == DataType.Boolean) { TryParseBooleanArray(propertySchemaObject, kv, itemArray, testMessageResult); } } }
public TestMessageResult AreAllElementsInMessageContainedInContract(CaseInsensitiveDictionary <object> messageKeyDictionary, CaseInsensitiveDictionary <SchemaObject> contractDictionary) { var result = new TestMessageResult(); foreach (var kv in messageKeyDictionary) { if (contractDictionary.Keys.Contains(kv.Key, StringComparer.InvariantCultureIgnoreCase)) { continue; } result.IsMessageValid = true; result.Warnings.Add($"Message property \"{kv.Key}\" is not part of the contract."); } return(result); }
public TestMessageResult AreAllElementsInContractContainedInMessage(CaseInsensitiveDictionary <object> messageKeyDictionary, CaseInsensitiveDictionary <SchemaObject> contractDictionary) { var result = new TestMessageResult(); foreach (var kv in contractDictionary) { if (messageKeyDictionary.Keys.Contains(kv.Key, StringComparer.InvariantCultureIgnoreCase)) { continue; } result.IsMessageValid = false; result.MessageErrors.Add($"Message is missing expected property \"{kv.Key}\"."); } return(result); }
public TestMessageResult DoAllMessageValuesMatchDataTypes(CaseInsensitiveDictionary <object> messageKeyDictionary, CaseInsensitiveDictionary <SchemaObject> contractDictionary, bool allowSubset = false) { var testMessageResult = new TestMessageResult(); // loop through each property in the message Dictionary - trying to parse the values into the data type in the contract foreach (KeyValuePair <string, object> kv in messageKeyDictionary) { // if the contract doesn't supply the datatype for the parameter -> then the message doesn't match the contract. contractDictionary.TryGetValue(kv.Key, out var propertySchemaObject); if (propertySchemaObject != null) { ChecksForMessage(propertySchemaObject, kv, testMessageResult, allowSubset); } } return(testMessageResult); }
private static void TryParseIntegerArray(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, dynamic itemArray, TestMessageResult testMessageResult) { var itemFormat = propertySchemaObject.Items.GetFormat(); foreach (var item in itemArray) { var isInt32 = int.TryParse(item.ToString(), out int _); var isInt64 = long.TryParse(item.ToString(), out long _); // Validate integer data type, which is int32. if (itemFormat == null && !isInt32) { AddItemTypeError(testMessageResult, kv.Key, DataType.Integer); break; } if (itemFormat != null) { // Validate specific integer formats if (itemFormat == Format.Int32 && !isInt32) { AddItemTypeError(testMessageResult, kv.Key, DataType.Integer, Format.Int32); break; } if (itemFormat == Format.Int64 && !isInt64) { AddItemTypeError(testMessageResult, kv.Key, DataType.Integer, Format.Int64); break; } } } }
private static void TryParseStringArray(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, dynamic itemArray, TestMessageResult testMessageResult, bool itemIsGuid) { var itemFormat = propertySchemaObject.Items.GetFormat(); if (itemFormat == Format.DateTime) { foreach (var item in itemArray) { if (!DateTime.TryParse(item.ToString(), out DateTime _)) { AddItemTypeError(testMessageResult, kv.Key, DataType.String, Format.DateTime); break; } } } else if (itemIsGuid) { foreach (var item in itemArray) { if (!Guid.TryParse(item.ToString(), out Guid _)) { AddItemTypeError(testMessageResult, kv.Key, DataType.String, Format.Guid); break; } } } }
private static void AddArrayMaxLengthError(TestMessageResult result, string key, int max) { result.IsMessageValid = false; result.MessageErrors.Add( $"The Items array for {key} has greater than the maximum number ({max}) of items allowed."); }
private static void AddArrayMinLengthError(TestMessageResult result, string key, int min) { result.IsMessageValid = false; result.MessageErrors.Add( $"The Items array for {key} does not have the minimum number ({min}) of items required."); }
private static void AddRequiredError(TestMessageResult result, string key, string type, string requiredProperty) { result.IsMessageValid = false; result.MessageErrors.Add( $"\"{key}\" does not have the required property({requiredProperty}) for type({type})."); }
private static void AddNotFoundError(TestMessageResult result, string property) { result.IsMessageValid = false; result.MessageErrors.Add( $"The value for field \"{property}\" was not found."); }
private static void AddFormatError(TestMessageResult result, string value, string key, Format format) { result.IsMessageValid = false; result.MessageErrors.Add( $"\"{value}\" does not match the required format for {key} ({format.DisplayName})."); }
private static void AddTypeError(TestMessageResult result, string value, string key, string type) { result.IsMessageValid = false; result.MessageErrors.Add( $"\"{value}\" does not match the required data type for {key} ({type})."); }
private static void TryParseNumberArray(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, dynamic itemArray, TestMessageResult testMessageResult) { var itemFormat = propertySchemaObject.Items.GetFormat(); foreach (var item in itemArray) { var isFloat = float.TryParse(item.ToString(), out float _); var isDouble = double.TryParse(item.ToString(), out double _); // Validate integer data type, which is int32. if (itemFormat == null && !isFloat) { AddItemTypeError(testMessageResult, kv.Key, DataType.Number); break; } if (itemFormat != null) { // Validate specific integer formats if (itemFormat == Format.Float && !isFloat) { AddItemTypeError(testMessageResult, kv.Key, DataType.Number, Format.Float); break; } if (itemFormat == Format.Double && !isDouble) { AddItemTypeError(testMessageResult, kv.Key, DataType.Number, Format.Double); break; } } } }
public TestMessageResult Execute(string contract, string message, bool allowSubset = false) { var schemaDictionary = SchemaObject.BuildSchemaDictionary(contract, HandleReferenceError, HandleFailure); if (!_isContractValid) { var contractInvalidMessage = new TestMessageResult { IsMessageValid = false, MessageErrors = new List <string>() { _contractErrorMessage } }; _isContractValid = true; _contractErrorMessage = ""; return(contractInvalidMessage); } if (!TryParseJSON(message, out var messageJson)) { return(new TestMessageResult { IsMessageValid = false, MessageErrors = new List <string> { "Message contains invalid JSON." } }); } var messageDictionary = JsonConvert.DeserializeObject <CaseInsensitiveDictionary <object> >(messageJson.ToString()); if (messageDictionary.Count <= 0) { return new TestMessageResult { IsMessageValid = false, MessageErrors = new List <string> { "Message contains empty JSON." } } } ; var contractDictionary = schemaDictionary.TryGetValue("Contract", out _) ? schemaDictionary["Contract"].Properties : null; if (contractDictionary != null && _isContractValid) { TestCases.Clear(); TestCases.Add(AreAllElementsInMessageContainedInContract(messageDictionary, contractDictionary)); if (!allowSubset) { TestCases.Add(AreAllElementsInContractContainedInMessage(messageDictionary, contractDictionary)); } TestCases.Add(DoAllMessageValuesMatchDataTypes(messageDictionary, contractDictionary, allowSubset)); } return(new TestMessageResult { IsMessageValid = TestCases.All(x => x.IsMessageValid), Warnings = TestCases.SelectMany(x => x.Warnings).ToList(), MessageErrors = TestCases.SelectMany(x => x.MessageErrors).ToList() }); }
private void TryParseBooleanArray(SchemaObject propertySchemaObject, KeyValuePair <string, object> kv, dynamic itemArray, TestMessageResult testMessageResult) { var itemFormat = propertySchemaObject.Items.GetFormat(); foreach (var item in itemArray) { var isBool = bool.TryParse(item.ToString(), out bool _); // Validate boolean data type if (itemFormat == null && !isBool) { AddItemTypeError(testMessageResult, kv.Key, DataType.Boolean); break; } } }
private static void AddSchemaNotFoundError(TestMessageResult result, string property) { result.IsMessageValid = false; result.MessageErrors.Add( $"The schema for \"{property}\" was not found in the contract definition."); }