Skip to content

Commit 5d91ff2

Browse files
committed
JSON file deserialization throws errors if unknown properties are encountered.
Implements #15
1 parent 5113cc3 commit 5d91ff2

File tree

3 files changed

+104
-9
lines changed

3 files changed

+104
-9
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Xunit;
5+
using netmockery;
6+
using Newtonsoft.Json;
7+
using System.Linq;
8+
9+
namespace UnitTests
10+
{
11+
public class TestJSONAdditionalData
12+
{
13+
[Fact]
14+
public void NoAdditionalDataGivesNullInJSONEndpoint()
15+
{
16+
var endpoint = JsonConvert.DeserializeObject<JSONEndpoint>("{\"name\": \"foobar\"}");
17+
Assert.Equal("foobar", endpoint.name);
18+
Assert.Null(endpoint.AdditionalData);
19+
20+
// check no exception
21+
endpoint.ThrowExceptionIfAdditionalData();
22+
}
23+
24+
[Fact]
25+
public void AdditionalDataIsDeserializedJSONEndpoint()
26+
{
27+
var endpoint = JsonConvert.DeserializeObject<JSONEndpoint>("{\"name\": \"foobar\", \"nmae\": \"foobar\"}");
28+
Assert.Equal("foobar", endpoint.name);
29+
Assert.NotNull(endpoint.AdditionalData);
30+
Assert.Equal("nmae", endpoint.AdditionalData.Keys.Single());
31+
}
32+
33+
[Fact]
34+
public void AdditionalDataIsNotValid()
35+
{
36+
var endpoint = JsonConvert.DeserializeObject<JSONEndpoint>("{\"name\": \"foobar\", \"nmae\": \"foobar\"}");
37+
Assert.Equal("foobar", endpoint.name);
38+
Assert.NotNull(endpoint.AdditionalData);
39+
Assert.Equal("nmae", endpoint.AdditionalData.Keys.Single());
40+
41+
var exception = Assert.Throws<ArgumentException>(() => endpoint.ThrowExceptionIfAdditionalData());
42+
}
43+
44+
45+
[Fact]
46+
public void NullAdditionalDataIsNotSerialized()
47+
{
48+
var endpoint = JsonConvert.DeserializeObject<JSONEndpoint>("{\"name\": \"foobar\"}");
49+
Assert.Null(endpoint.AdditionalData);
50+
51+
var as_str = JsonConvert.SerializeObject(endpoint);
52+
Assert.Equal("{\"name\":\"foobar\",\"pathregex\":null,\"responses\":null}", as_str);
53+
}
54+
}
55+
}

netmockery/EndpointCollectionReader.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ static public EndpointCollection ReadFromDirectory(string directoryName)
2020
JsonConvert.DeserializeObject<JSONDefaults>(File.ReadAllText(globalDefaultsFile))
2121
:
2222
null;
23+
if (globalDefaults != null)
24+
{
25+
globalDefaults.ThrowExceptionIfAdditionalData();
26+
}
2327

2428
foreach (var subdirectory in Directory.GetDirectories(directoryName))
2529
{

netmockery/JSONReader.cs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using System.Text;
99
using System.Text.RegularExpressions;
1010
using System.Net;
11-
11+
using Newtonsoft.Json.Linq;
1212

1313
namespace netmockery
1414
{
@@ -20,14 +20,16 @@ public static Endpoint ReadEndpoint(string jsonString, string rootDir, JSONDefau
2020
}
2121
}
2222

23-
public class JSONParam
23+
public class JSONParam : JSONObjectWithAdditionalData
2424
{
2525
public string name;
2626
public string @default;
2727
public string description;
2828

2929
public JSONParam Validated()
3030
{
31+
ThrowExceptionIfAdditionalData();
32+
3133
if (name == null)
3234
{
3335
throw new ArgumentException($"Parameter missing name");
@@ -52,7 +54,7 @@ public JSONParam Validated()
5254
}
5355
}
5456

55-
public class JSONTest
57+
public class JSONTest : JSONObjectWithAdditionalData
5658
{
5759
public string name;
5860
public string method;
@@ -69,6 +71,8 @@ public class JSONTest
6971

7072
public JSONTest Validated()
7173
{
74+
ThrowExceptionIfAdditionalData();
75+
7276
if (requestpath == null)
7377
{
7478
throw new ArgumentNullException(nameof(requestpath));
@@ -111,7 +115,7 @@ public NetmockeryTestCase CreateTestCase(string directory)
111115
}
112116
}
113117

114-
public class JSONResponse
118+
public class JSONResponse : JSONObjectWithAdditionalData
115119
{
116120
public JSONRequestMatcher match;
117121

@@ -133,10 +137,20 @@ public class JSONResponse
133137

134138
public JSONResponse Validated()
135139
{
140+
ThrowExceptionIfAdditionalData();
136141
if (match == null)
137142
{
138143
throw new ArgumentException("match must be specified");
139144
}
145+
146+
if (replacements != null)
147+
{
148+
foreach (var replacement in replacements)
149+
{
150+
replacement.ThrowExceptionIfAdditionalData();
151+
}
152+
}
153+
140154
var mutuallyExclusive = new[] { literal, file, script, forward };
141155
var mutExWithValues = from value in mutuallyExclusive where value != null select value;
142156
if (mutExWithValues.Count() != 1)
@@ -207,7 +221,7 @@ from jsonreplacement in replacements
207221

208222
}
209223

210-
public class JSONRequestMatcher
224+
public class JSONRequestMatcher : JSONObjectWithAdditionalData
211225
{
212226
public string methods;
213227
public string xpath;
@@ -216,6 +230,8 @@ public class JSONRequestMatcher
216230

217231
public RequestMatcher CreateRequestMatcher()
218232
{
233+
ThrowExceptionIfAdditionalData();
234+
219235
RequestMatcher retval;
220236
if (xpath != null)
221237
{
@@ -224,6 +240,7 @@ public RequestMatcher CreateRequestMatcher()
224240
{
225241
foreach (var jsonNs in namespaces)
226242
{
243+
jsonNs.ThrowExceptionIfAdditionalData();
227244
xpathMatcher.AddNamespace(jsonNs.prefix, jsonNs.ns);
228245
}
229246
}
@@ -245,19 +262,33 @@ public RequestMatcher CreateRequestMatcher()
245262
}
246263
}
247264

248-
public class JSONXPathNamespace
265+
public class JSONXPathNamespace : JSONObjectWithAdditionalData
249266
{
250267
public string prefix;
251268
public string ns;
252269
}
253270

254-
public class JSONReplacement
271+
public class JSONReplacement : JSONObjectWithAdditionalData
255272
{
256273
public string search;
257274
public string replace;
258275
}
259276

260-
public class JSONEndpoint
277+
public class JSONObjectWithAdditionalData
278+
{
279+
[JsonExtensionData]
280+
public IDictionary<string, JToken> AdditionalData;
281+
282+
public void ThrowExceptionIfAdditionalData()
283+
{
284+
if (AdditionalData != null)
285+
{
286+
throw new ArgumentException($"Unknown JSON attributes: " + string.Join(", ", from key in AdditionalData.Keys select $"'{key}'"));
287+
}
288+
}
289+
}
290+
291+
public class JSONEndpoint : JSONObjectWithAdditionalData
261292
{
262293
public string name;
263294
public string pathregex;
@@ -266,6 +297,7 @@ public class JSONEndpoint
266297

267298
public Endpoint CreateEndpoint(string rootDir, JSONDefaults globalDefaults)
268299
{
300+
ThrowExceptionIfAdditionalData();
269301
var endpoint = new Endpoint(name, pathregex)
270302
{
271303
Directory = rootDir
@@ -278,6 +310,10 @@ public Endpoint CreateEndpoint(string rootDir, JSONDefaults globalDefaults)
278310
JsonConvert.DeserializeObject<JSONDefaults>(File.ReadAllText(endpointDefaultsFile))
279311
:
280312
null;
313+
if (endpointDefaults != null)
314+
{
315+
endpointDefaults.ThrowExceptionIfAdditionalData();
316+
}
281317

282318
foreach (var jsonResponse in responses)
283319
{
@@ -330,7 +366,7 @@ private void applyDefaults(JSONDefaults defaults, JSONResponse jsonResponse)
330366
}
331367
}
332368

333-
public class JSONDefaults
369+
public class JSONDefaults : JSONObjectWithAdditionalData
334370
{
335371
public string contenttype;
336372
public string charset;

0 commit comments

Comments
 (0)