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 using System;
27 using System.Collections.Generic;
28 #if !NETFX_CORE
29 using NUnit.Framework;
30 #else
31 using Microsoft.VisualStudio.TestTools.UnitTesting;
32 using TestFixture = Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute;
33 using Test = Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute;
34 #endif
35 using Newtonsoft.Json.Schema;
36 using Newtonsoft.Json.Linq;
37 using System.IO;
38 using Newtonsoft.Json.Tests.TestObjects;
39 #if !(SILVERLIGHT || NETFX_CORE)
40 using System.Data;
41 #endif
42 
43 namespace Newtonsoft.Json.Tests.Schema
44 {
45   [TestFixture]
46   public class ExtensionsTests : TestFixtureBase
47   {
48     [Test]
IsValid()49     public void IsValid()
50     {
51       JsonSchema schema = JsonSchema.Parse("{'type':'integer'}");
52       JToken stringToken = JToken.FromObject("pie");
53       JToken integerToken = JToken.FromObject(1);
54 
55       IList<string> errorMessages;
56       Assert.AreEqual(true, integerToken.IsValid(schema));
57       Assert.AreEqual(true, integerToken.IsValid(schema, out errorMessages));
58       Assert.AreEqual(0, errorMessages.Count);
59 
60       Assert.AreEqual(false, stringToken.IsValid(schema));
61       Assert.AreEqual(false, stringToken.IsValid(schema, out errorMessages));
62       Assert.AreEqual(1, errorMessages.Count);
63       Assert.AreEqual("Invalid type. Expected Integer but got String.", errorMessages[0]);
64     }
65 
66     [Test]
ValidateWithEventHandler()67     public void ValidateWithEventHandler()
68     {
69       JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}");
70       JToken stringToken = JToken.FromObject("pie lol");
71 
72       List<string> errors = new List<string>();
73       stringToken.Validate(schema, (sender, args) => errors.Add(args.Message));
74       Assert.AreEqual(0, errors.Count);
75 
76       stringToken = JToken.FromObject("pie");
77 
78       stringToken.Validate(schema, (sender, args) => errors.Add(args.Message));
79       Assert.AreEqual(1, errors.Count);
80 
81       Assert.AreEqual("String 'pie' does not match regex pattern 'lol'.", errors[0]);
82     }
83 
84     [Test]
ValidateWithOutEventHandlerFailure()85     public void ValidateWithOutEventHandlerFailure()
86     {
87       ExceptionAssert.Throws<JsonSchemaException>(@"String 'pie' does not match regex pattern 'lol'.",
88       () =>
89       {
90         JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}");
91         JToken stringToken = JToken.FromObject("pie");
92         stringToken.Validate(schema);
93       });
94     }
95 
96     [Test]
ValidateWithOutEventHandlerSuccess()97     public void ValidateWithOutEventHandlerSuccess()
98     {
99       JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}");
100       JToken stringToken = JToken.FromObject("pie lol");
101       stringToken.Validate(schema);
102     }
103 
104     [Test]
ValidateFailureWithOutLineInfoBecauseOfEndToken()105     public void ValidateFailureWithOutLineInfoBecauseOfEndToken()
106     {
107       JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'required':true}}}");
108       JObject o = JObject.Parse("{}");
109 
110       List<string> errors = new List<string>();
111       o.Validate(schema, (sender, args) => errors.Add(args.Message));
112 
113       Assert.AreEqual("Required properties are missing from object: lol.", errors[0]);
114       Assert.AreEqual(1, errors.Count);
115     }
116 
117     [Test]
ValidateFailureWithLineInfo()118     public void ValidateFailureWithLineInfo()
119     {
120       JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'type':'string'}}}");
121       JObject o = JObject.Parse("{'lol':1}");
122 
123       List<string> errors = new List<string>();
124       o.Validate(schema, (sender, args) => errors.Add(args.Path + " - " + args.Message));
125 
126       Assert.AreEqual("lol - Invalid type. Expected String but got Integer. Line 1, position 8.", errors[0]);
127       Assert.AreEqual("1", o.SelectToken("lol").ToString());
128       Assert.AreEqual(1, errors.Count);
129     }
130 
131     [Test]
Blog()132     public void Blog()
133     {
134       string schemaJson = @"
135 {
136   ""description"": ""A person schema"",
137   ""type"": ""object"",
138   ""properties"":
139   {
140     ""name"": {""type"":""string""},
141     ""hobbies"": {
142       ""type"": ""array"",
143       ""items"": {""type"":""string""}
144     }
145   }
146 }
147 ";
148 
149       //JsonSchema schema;
150 
151       //using (JsonTextReader reader = new JsonTextReader(new StringReader(schemaJson)))
152       //{
153       //  JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver());
154       //  schema = builder.Parse(reader);
155       //}
156 
157       JsonSchema schema = JsonSchema.Parse(schemaJson);
158 
159       JObject person = JObject.Parse(@"{
160         ""name"": ""James"",
161         ""hobbies"": ["".NET"", ""Blogging"", ""Reading"", ""Xbox"", ""LOLCATS""]
162       }");
163 
164       bool valid = person.IsValid(schema);
165       // true
166     }
167 
GenerateSchemaAndSerializeFromType(T value)168     private void GenerateSchemaAndSerializeFromType<T>(T value)
169     {
170       JsonSchemaGenerator generator = new JsonSchemaGenerator();
171       generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseAssemblyQualifiedName;
172       JsonSchema typeSchema = generator.Generate(typeof (T));
173       string schema = typeSchema.ToString();
174 
175       string json = JsonConvert.SerializeObject(value, Formatting.Indented);
176       JToken token = JToken.ReadFrom(new JsonTextReader(new StringReader(json)));
177 
178       List<string> errors = new List<string>();
179 
180       token.Validate(typeSchema, (sender, args) =>
181                                    {
182                                      errors.Add(args.Message);
183                                    });
184 
185       if (errors.Count > 0)
186         Assert.Fail("Schema generated for type '{0}' is not valid." + Environment.NewLine + string.Join(Environment.NewLine, errors.ToArray()), typeof(T));
187     }
188 
189     [Test]
GenerateSchemaAndSerializeFromTypeTests()190     public void GenerateSchemaAndSerializeFromTypeTests()
191     {
192       GenerateSchemaAndSerializeFromType(new List<string> { "1", "Two", "III" });
193       GenerateSchemaAndSerializeFromType(new List<int> { 1 });
194       GenerateSchemaAndSerializeFromType(new Version("1.2.3.4"));
195       GenerateSchemaAndSerializeFromType(new Store());
196       GenerateSchemaAndSerializeFromType(new Person());
197       GenerateSchemaAndSerializeFromType(new PersonRaw());
198       GenerateSchemaAndSerializeFromType(new CircularReferenceClass() { Name = "I'm required" });
199       GenerateSchemaAndSerializeFromType(new CircularReferenceWithIdClass());
200       GenerateSchemaAndSerializeFromType(new ClassWithArray());
201       GenerateSchemaAndSerializeFromType(new ClassWithGuid());
202 #if !NET20 && !PocketPC
203       GenerateSchemaAndSerializeFromType(new NullableDateTimeTestClass());
204 #endif
205 #if !(SILVERLIGHT || NETFX_CORE)
206       GenerateSchemaAndSerializeFromType(new DataSet());
207 #endif
208       GenerateSchemaAndSerializeFromType(new object());
209       GenerateSchemaAndSerializeFromType(1);
210       GenerateSchemaAndSerializeFromType("Hi");
211       GenerateSchemaAndSerializeFromType(new DateTime(2000, 12, 29, 23, 59, 0, DateTimeKind.Utc));
212       GenerateSchemaAndSerializeFromType(TimeSpan.FromTicks(1000000));
213 #if !NETFX_CORE
214       GenerateSchemaAndSerializeFromType(DBNull.Value);
215 #endif
216       GenerateSchemaAndSerializeFromType(new JsonPropertyWithHandlingValues());
217     }
218 
219     [Test]
UndefinedPropertyOnNoPropertySchema()220     public void UndefinedPropertyOnNoPropertySchema()
221     {
222       JsonSchema schema = JsonSchema.Parse(@"{
223   ""description"": ""test"",
224   ""type"": ""object"",
225   ""additionalProperties"": false,
226   ""properties"": {
227   }
228 }");
229 
230       JObject o = JObject.Parse("{'g':1}");
231 
232       List<string> errors = new List<string>();
233       o.Validate(schema, (sender, args) => errors.Add(args.Message));
234 
235       Assert.AreEqual(1, errors.Count);
236       Assert.AreEqual("Property 'g' has not been defined and the schema does not allow additional properties. Line 1, position 5.", errors[0]);
237     }
238 
239     [Test]
ExclusiveMaximum_Int()240     public void ExclusiveMaximum_Int()
241     {
242       ExceptionAssert.Throws<JsonSchemaException>("Integer 10 equals maximum value of 10 and exclusive maximum is true.",
243       () =>
244       {
245         JsonSchema schema = new JsonSchema();
246         schema.Maximum = 10;
247         schema.ExclusiveMaximum = true;
248 
249         JValue v = new JValue(10);
250         v.Validate(schema);
251       });
252     }
253 
254     [Test]
ExclusiveMaximum_Float()255     public void ExclusiveMaximum_Float()
256     {
257       ExceptionAssert.Throws<JsonSchemaException>("Float 10.1 equals maximum value of 10.1 and exclusive maximum is true.",
258       () =>
259       {
260         JsonSchema schema = new JsonSchema();
261         schema.Maximum = 10.1;
262         schema.ExclusiveMaximum = true;
263 
264         JValue v = new JValue(10.1);
265         v.Validate(schema);
266       });
267     }
268 
269     [Test]
ExclusiveMinimum_Int()270     public void ExclusiveMinimum_Int()
271     {
272       ExceptionAssert.Throws<JsonSchemaException>("Integer 10 equals minimum value of 10 and exclusive minimum is true.",
273       () =>
274       {
275         JsonSchema schema = new JsonSchema();
276         schema.Minimum = 10;
277         schema.ExclusiveMinimum = true;
278 
279         JValue v = new JValue(10);
280         v.Validate(schema);
281       });
282     }
283 
284     [Test]
ExclusiveMinimum_Float()285     public void ExclusiveMinimum_Float()
286     {
287       ExceptionAssert.Throws<JsonSchemaException>("Float 10.1 equals minimum value of 10.1 and exclusive minimum is true.",
288       () =>
289       {
290         JsonSchema schema = new JsonSchema();
291         schema.Minimum = 10.1;
292         schema.ExclusiveMinimum = true;
293 
294         JValue v = new JValue(10.1);
295         v.Validate(schema);
296       });
297     }
298 
299     [Test]
DivisibleBy_Int()300     public void DivisibleBy_Int()
301     {
302       ExceptionAssert.Throws<JsonSchemaException>("Integer 10 is not evenly divisible by 3.",
303       () =>
304       {
305         JsonSchema schema = new JsonSchema();
306         schema.DivisibleBy = 3;
307 
308         JValue v = new JValue(10);
309         v.Validate(schema);
310       });
311     }
312   }
313 }