1 #region License 2 // Copyright (c) 2007 James Newton-King 3 // 4 // Permission is hereby granted, free of charge, to any person 5 // obtaining a copy of this software and associated documentation 6 // files (the "Software"), to deal in the Software without 7 // restriction, including without limitation the rights to use, 8 // copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the 10 // Software is furnished to do so, subject to the following 11 // conditions: 12 // 13 // The above copyright notice and this permission notice shall be 14 // included in all copies or substantial portions of the Software. 15 // 16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 // OTHER DEALINGS IN THE SOFTWARE. 24 #endregion 25 26 #pragma warning disable 618 27 using System; 28 using System.Collections.Generic; 29 #if NETFX_CORE 30 using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; 31 using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; 32 using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; 33 #elif DNXCORE50 34 using Xunit; 35 using Test = Xunit.FactAttribute; 36 using Assert = Newtonsoft.Json.Tests.XUnitAssert; 37 #else 38 using NUnit.Framework; 39 #endif 40 using Newtonsoft.Json.Schema; 41 using Newtonsoft.Json.Linq; 42 using System.IO; 43 using Newtonsoft.Json.Tests.TestObjects; 44 #if !(NETFX_CORE || DNXCORE50) 45 using System.Data; 46 47 #endif 48 49 namespace Newtonsoft.Json.Tests.Schema 50 { 51 [TestFixture] 52 public class ExtensionsTests : TestFixtureBase 53 { 54 [Test] IsValid()55 public void IsValid() 56 { 57 JsonSchema schema = JsonSchema.Parse("{'type':'integer'}"); 58 JToken stringToken = JToken.FromObject("pie"); 59 JToken integerToken = JToken.FromObject(1); 60 61 IList<string> errorMessages; 62 Assert.AreEqual(true, integerToken.IsValid(schema)); 63 Assert.AreEqual(true, integerToken.IsValid(schema, out errorMessages)); 64 Assert.AreEqual(0, errorMessages.Count); 65 66 Assert.AreEqual(false, stringToken.IsValid(schema)); 67 Assert.AreEqual(false, stringToken.IsValid(schema, out errorMessages)); 68 Assert.AreEqual(1, errorMessages.Count); 69 Assert.AreEqual("Invalid type. Expected Integer but got String.", errorMessages[0]); 70 } 71 72 [Test] ValidateWithEventHandler()73 public void ValidateWithEventHandler() 74 { 75 JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); 76 JToken stringToken = JToken.FromObject("pie lol"); 77 78 List<string> errors = new List<string>(); 79 stringToken.Validate(schema, (sender, args) => errors.Add(args.Message)); 80 Assert.AreEqual(0, errors.Count); 81 82 stringToken = JToken.FromObject("pie"); 83 84 stringToken.Validate(schema, (sender, args) => errors.Add(args.Message)); 85 Assert.AreEqual(1, errors.Count); 86 87 Assert.AreEqual("String 'pie' does not match regex pattern 'lol'.", errors[0]); 88 } 89 90 [Test] ValidateWithOutEventHandlerFailure()91 public void ValidateWithOutEventHandlerFailure() 92 { 93 ExceptionAssert.Throws<JsonSchemaException>(() => 94 { 95 JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); 96 JToken stringToken = JToken.FromObject("pie"); 97 stringToken.Validate(schema); 98 }, @"String 'pie' does not match regex pattern 'lol'."); 99 } 100 101 [Test] ValidateWithOutEventHandlerSuccess()102 public void ValidateWithOutEventHandlerSuccess() 103 { 104 JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); 105 JToken stringToken = JToken.FromObject("pie lol"); 106 stringToken.Validate(schema); 107 } 108 109 [Test] ValidateFailureWithOutLineInfoBecauseOfEndToken()110 public void ValidateFailureWithOutLineInfoBecauseOfEndToken() 111 { 112 // changed in 6.0.6 to now include line info! 113 JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'required':true}}}"); 114 JObject o = JObject.Parse("{}"); 115 116 List<string> errors = new List<string>(); 117 o.Validate(schema, (sender, args) => errors.Add(args.Message)); 118 119 Assert.AreEqual("Required properties are missing from object: lol. Line 1, position 1.", errors[0]); 120 Assert.AreEqual(1, errors.Count); 121 } 122 123 [Test] ValidateRequiredFieldsWithLineInfo()124 public void ValidateRequiredFieldsWithLineInfo() 125 { 126 JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'type':'string'}}}"); 127 JObject o = JObject.Parse("{'lol':1}"); 128 129 List<string> errors = new List<string>(); 130 o.Validate(schema, (sender, args) => errors.Add(args.Path + " - " + args.Message)); 131 132 Assert.AreEqual("lol - Invalid type. Expected String but got Integer. Line 1, position 8.", errors[0]); 133 Assert.AreEqual("1", o.SelectToken("lol").ToString()); 134 Assert.AreEqual(1, errors.Count); 135 } 136 137 [Test] Blog()138 public void Blog() 139 { 140 string schemaJson = @" 141 { 142 ""description"": ""A person schema"", 143 ""type"": ""object"", 144 ""properties"": 145 { 146 ""name"": {""type"":""string""}, 147 ""hobbies"": { 148 ""type"": ""array"", 149 ""items"": {""type"":""string""} 150 } 151 } 152 } 153 "; 154 155 //JsonSchema schema; 156 157 //using (JsonTextReader reader = new JsonTextReader(new StringReader(schemaJson))) 158 //{ 159 // JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); 160 // schema = builder.Parse(reader); 161 //} 162 163 JsonSchema schema = JsonSchema.Parse(schemaJson); 164 165 JObject person = JObject.Parse(@"{ 166 ""name"": ""James"", 167 ""hobbies"": ["".NET"", ""Blogging"", ""Reading"", ""Xbox"", ""LOLCATS""] 168 }"); 169 170 bool valid = person.IsValid(schema); 171 // true 172 } 173 GenerateSchemaAndSerializeFromType(T value)174 private void GenerateSchemaAndSerializeFromType<T>(T value) 175 { 176 JsonSchemaGenerator generator = new JsonSchemaGenerator(); 177 generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseAssemblyQualifiedName; 178 JsonSchema typeSchema = generator.Generate(typeof(T)); 179 string schema = typeSchema.ToString(); 180 181 string json = JsonConvert.SerializeObject(value, Formatting.Indented); 182 JToken token = JToken.ReadFrom(new JsonTextReader(new StringReader(json))); 183 184 List<string> errors = new List<string>(); 185 186 token.Validate(typeSchema, (sender, args) => { errors.Add(args.Message); }); 187 188 if (errors.Count > 0) 189 { 190 Assert.Fail("Schema generated for type '{0}' is not valid." + Environment.NewLine + string.Join(Environment.NewLine, errors.ToArray()), typeof(T)); 191 } 192 } 193 194 [Test] GenerateSchemaAndSerializeFromTypeTests()195 public void GenerateSchemaAndSerializeFromTypeTests() 196 { 197 GenerateSchemaAndSerializeFromType(new List<string> { "1", "Two", "III" }); 198 GenerateSchemaAndSerializeFromType(new List<int> { 1 }); 199 GenerateSchemaAndSerializeFromType(new Version("1.2.3.4")); 200 GenerateSchemaAndSerializeFromType(new Store()); 201 GenerateSchemaAndSerializeFromType(new Person()); 202 GenerateSchemaAndSerializeFromType(new PersonRaw()); 203 GenerateSchemaAndSerializeFromType(new CircularReferenceClass() { Name = "I'm required" }); 204 GenerateSchemaAndSerializeFromType(new CircularReferenceWithIdClass()); 205 GenerateSchemaAndSerializeFromType(new ClassWithArray()); 206 GenerateSchemaAndSerializeFromType(new ClassWithGuid()); 207 #if !NET20 208 GenerateSchemaAndSerializeFromType(new NullableDateTimeTestClass()); 209 #endif 210 #if !(NETFX_CORE || PORTABLE || DNXCORE50 || PORTABLE40) 211 GenerateSchemaAndSerializeFromType(new DataSet()); 212 #endif 213 GenerateSchemaAndSerializeFromType(new object()); 214 GenerateSchemaAndSerializeFromType(1); 215 GenerateSchemaAndSerializeFromType("Hi"); 216 GenerateSchemaAndSerializeFromType(new DateTime(2000, 12, 29, 23, 59, 0, DateTimeKind.Utc)); 217 GenerateSchemaAndSerializeFromType(TimeSpan.FromTicks(1000000)); 218 #if !(NETFX_CORE || PORTABLE || DNXCORE50 || PORTABLE40) 219 GenerateSchemaAndSerializeFromType(DBNull.Value); 220 #endif 221 GenerateSchemaAndSerializeFromType(new JsonPropertyWithHandlingValues()); 222 } 223 224 [Test] UndefinedPropertyOnNoPropertySchema()225 public void UndefinedPropertyOnNoPropertySchema() 226 { 227 JsonSchema schema = JsonSchema.Parse(@"{ 228 ""description"": ""test"", 229 ""type"": ""object"", 230 ""additionalProperties"": false, 231 ""properties"": { 232 } 233 }"); 234 235 JObject o = JObject.Parse("{'g':1}"); 236 237 List<string> errors = new List<string>(); 238 o.Validate(schema, (sender, args) => errors.Add(args.Message)); 239 240 Assert.AreEqual(1, errors.Count); 241 Assert.AreEqual("Property 'g' has not been defined and the schema does not allow additional properties. Line 1, position 5.", errors[0]); 242 } 243 244 [Test] ExclusiveMaximum_Int()245 public void ExclusiveMaximum_Int() 246 { 247 ExceptionAssert.Throws<JsonSchemaException>(() => 248 { 249 JsonSchema schema = new JsonSchema(); 250 schema.Maximum = 10; 251 schema.ExclusiveMaximum = true; 252 253 JValue v = new JValue(10); 254 v.Validate(schema); 255 }, "Integer 10 equals maximum value of 10 and exclusive maximum is true."); 256 } 257 258 [Test] ExclusiveMaximum_Float()259 public void ExclusiveMaximum_Float() 260 { 261 ExceptionAssert.Throws<JsonSchemaException>(() => 262 { 263 JsonSchema schema = new JsonSchema(); 264 schema.Maximum = 10.1; 265 schema.ExclusiveMaximum = true; 266 267 JValue v = new JValue(10.1); 268 v.Validate(schema); 269 }, "Float 10.1 equals maximum value of 10.1 and exclusive maximum is true."); 270 } 271 272 [Test] ExclusiveMinimum_Int()273 public void ExclusiveMinimum_Int() 274 { 275 ExceptionAssert.Throws<JsonSchemaException>(() => 276 { 277 JsonSchema schema = new JsonSchema(); 278 schema.Minimum = 10; 279 schema.ExclusiveMinimum = true; 280 281 JValue v = new JValue(10); 282 v.Validate(schema); 283 }, "Integer 10 equals minimum value of 10 and exclusive minimum is true."); 284 } 285 286 [Test] ExclusiveMinimum_Float()287 public void ExclusiveMinimum_Float() 288 { 289 ExceptionAssert.Throws<JsonSchemaException>(() => 290 { 291 JsonSchema schema = new JsonSchema(); 292 schema.Minimum = 10.1; 293 schema.ExclusiveMinimum = true; 294 295 JValue v = new JValue(10.1); 296 v.Validate(schema); 297 }, "Float 10.1 equals minimum value of 10.1 and exclusive minimum is true."); 298 } 299 300 [Test] DivisibleBy_Int()301 public void DivisibleBy_Int() 302 { 303 ExceptionAssert.Throws<JsonSchemaException>(() => 304 { 305 JsonSchema schema = new JsonSchema(); 306 schema.DivisibleBy = 3; 307 308 JValue v = new JValue(10); 309 v.Validate(schema); 310 }, "Integer 10 is not evenly divisible by 3."); 311 } 312 313 [Test] DivisibleBy_Approx()314 public void DivisibleBy_Approx() 315 { 316 JsonSchema schema = new JsonSchema(); 317 schema.DivisibleBy = 0.01; 318 319 JValue v = new JValue(20.49); 320 v.Validate(schema); 321 } 322 323 [Test] UniqueItems_SimpleUnique()324 public void UniqueItems_SimpleUnique() 325 { 326 JsonSchema schema = new JsonSchema(); 327 schema.UniqueItems = true; 328 329 JArray a = new JArray(1, 2, 3); 330 Assert.IsTrue(a.IsValid(schema)); 331 } 332 333 [Test] UniqueItems_SimpleDuplicate()334 public void UniqueItems_SimpleDuplicate() 335 { 336 JsonSchema schema = new JsonSchema(); 337 schema.UniqueItems = true; 338 339 JArray a = new JArray(1, 2, 3, 2, 2); 340 IList<string> errorMessages; 341 Assert.IsFalse(a.IsValid(schema, out errorMessages)); 342 Assert.AreEqual(2, errorMessages.Count); 343 Assert.AreEqual("Non-unique array item at index 3.", errorMessages[0]); 344 Assert.AreEqual("Non-unique array item at index 4.", errorMessages[1]); 345 } 346 347 [Test] UniqueItems_ComplexDuplicate()348 public void UniqueItems_ComplexDuplicate() 349 { 350 JsonSchema schema = new JsonSchema(); 351 schema.UniqueItems = true; 352 353 JArray a = new JArray(1, new JObject(new JProperty("value", "value!")), 3, 2, new JObject(new JProperty("value", "value!")), 4, 2, new JObject(new JProperty("value", "value!"))); 354 IList<string> errorMessages; 355 Assert.IsFalse(a.IsValid(schema, out errorMessages)); 356 Assert.AreEqual(3, errorMessages.Count); 357 Assert.AreEqual("Non-unique array item at index 4.", errorMessages[0]); 358 Assert.AreEqual("Non-unique array item at index 6.", errorMessages[1]); 359 Assert.AreEqual("Non-unique array item at index 7.", errorMessages[2]); 360 } 361 362 [Test] UniqueItems_NestedDuplicate()363 public void UniqueItems_NestedDuplicate() 364 { 365 JsonSchema schema = new JsonSchema(); 366 schema.UniqueItems = true; 367 schema.Items = new List<JsonSchema> 368 { 369 new JsonSchema 370 { 371 UniqueItems = true 372 } 373 }; 374 schema.PositionalItemsValidation = false; 375 376 JArray a = new JArray( 377 new JArray(1, 2), 378 new JArray(1, 1), 379 new JArray(3, 4), 380 new JArray(1, 2), 381 new JArray(1, 1) 382 ); 383 IList<string> errorMessages; 384 Assert.IsFalse(a.IsValid(schema, out errorMessages)); 385 Assert.AreEqual(4, errorMessages.Count); 386 Assert.AreEqual("Non-unique array item at index 1.", errorMessages[0]); 387 Assert.AreEqual("Non-unique array item at index 3.", errorMessages[1]); 388 Assert.AreEqual("Non-unique array item at index 1.", errorMessages[2]); 389 Assert.AreEqual("Non-unique array item at index 4.", errorMessages[3]); 390 } 391 392 [Test] Enum_Properties()393 public void Enum_Properties() 394 { 395 JsonSchema schema = new JsonSchema(); 396 schema.Properties = new Dictionary<string, JsonSchema> 397 { 398 { 399 "bar", 400 new JsonSchema 401 { 402 Enum = new List<JToken> 403 { 404 new JValue(1), 405 new JValue(2) 406 } 407 } 408 } 409 }; 410 411 JObject o = new JObject( 412 new JProperty("bar", 1) 413 ); 414 IList<string> errorMessages; 415 Assert.IsTrue(o.IsValid(schema, out errorMessages)); 416 Assert.AreEqual(0, errorMessages.Count); 417 418 o = new JObject( 419 new JProperty("bar", 3) 420 ); 421 Assert.IsFalse(o.IsValid(schema, out errorMessages)); 422 Assert.AreEqual(1, errorMessages.Count); 423 } 424 425 [Test] UniqueItems_Property()426 public void UniqueItems_Property() 427 { 428 JsonSchema schema = new JsonSchema(); 429 schema.Properties = new Dictionary<string, JsonSchema> 430 { 431 { 432 "bar", 433 new JsonSchema 434 { 435 UniqueItems = true 436 } 437 } 438 }; 439 440 JObject o = new JObject( 441 new JProperty("bar", new JArray(1, 2, 3, 3)) 442 ); 443 IList<string> errorMessages; 444 Assert.IsFalse(o.IsValid(schema, out errorMessages)); 445 Assert.AreEqual(1, errorMessages.Count); 446 } 447 448 [Test] Items_Positional()449 public void Items_Positional() 450 { 451 JsonSchema schema = new JsonSchema(); 452 schema.Items = new List<JsonSchema> 453 { 454 new JsonSchema { Type = JsonSchemaType.Object }, 455 new JsonSchema { Type = JsonSchemaType.Integer } 456 }; 457 schema.PositionalItemsValidation = true; 458 459 JArray a = new JArray(new JObject(), 1); 460 IList<string> errorMessages; 461 Assert.IsTrue(a.IsValid(schema, out errorMessages)); 462 Assert.AreEqual(0, errorMessages.Count); 463 } 464 } 465 } 466 467 #pragma warning restore 618