C# Client Library
A C# Client Library for the AnalyzeRe REST API
Loading...
Searching...
No Matches
Meta_TestSuite_Utilities_Client.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Reflection;
5using System.Threading;
6using System.Threading.Tasks;
7
8using AnalyzeRe;
16
17#if MSTEST
18using Microsoft.VisualStudio.TestTools.UnitTesting;
19#elif NUNIT
20using NUnit.Framework;
21using TestClass = NUnit.Framework.TestFixtureAttribute;
22using TestMethod = NUnit.Framework.TestAttribute;
23using TestCategory = NUnit.Framework.CategoryAttribute;
24#endif
25
27{
28 [TestClass]
30 {
31 private const string Category = "Test Utilities";
32
33 [TestMethod, TestCategory(Category)]
35 {
38 Assert.Inconclusive("DELETE is disabled.");
40 Assert.Inconclusive("Cannot DELETE while tests running in parallel.");
41
42 // Hack: If we're going to be calling a live server method,
43 // this should be in a BaseServerTest class.
46 // Test
48 Assert.IsTrue(CleanUp.CheckServerIsClean(), "Server did not end up clean.");
49 }
50
51 [TestMethod, TestCategory(Category)]
53 {
55 {
56 // Test the behaviour when a difference exists at various significant figures.
57 Tuple.Create(true, 1.00000000000001, 1.0), // Difference at 15th
58 Tuple.Create(false, 1.0000000000001, 1.0), // Difference at 14th
59 Tuple.Create(true, 10000000000000.1, 10000000000000.0), // 15th
60 Tuple.Create(false, 1000000000000.1, 1000000000000.0), // 14th
61 Tuple.Create(true, 0.123456789012345, 0.123456789012346), // 15th
62 Tuple.Create(false, 0.12345678912345, 0.12345678912346), // 14th
63 // Same test for very large doubles
64 Tuple.Create(true, 0.123456789012345*1E100, 0.123456789012346*1E100),
65 Tuple.Create(false, 0.12345678912345*1E100, 0.12345678912346*1E100),
66 // Same test for very small doubles
67 Tuple.Create(true, 0.123456789012345/1E100, 0.123456789012346/1E100),
68 Tuple.Create(false, 0.12345678912345/1E100, 0.12345678912346/1E100),
69 Tuple.Create(true, Double.MaxValue, Double.MaxValue - Math.PI),
70 // Real world round trip examples that caused a problem
71 Tuple.Create(true, 817393214.3568030, 817393214.35680345),
72 Tuple.Create(true, 0.408297614850242462, 0.408297614850242018)
73 };
75 {
76 if (test.Item1)
77 {
78 AssertApi.DoublesAreEqual(test.Item2, test.Item3);
79 AssertApi.DoublesAreEqual(test.Item3, test.Item2);
80 }
81 else
82 {
83 Assert.IsFalse(AssertApi.AlmostEquals(test.Item2, test.Item3), "Expected " +
84 Output.AutoFormat(test.Item2) + " and " +
85 Output.AutoFormat(test.Item3) + " to be not equal.");
86 }
87 }
88 }
89
90 #region Test TypeResolver
91 [TestMethod, TestCategory(Category)]
93 {
94 string testURL = "http://localhost:8000/layer_views/ae53378c-9a94-39f4-898a-4d888d43ae73";
96 testURL = "https://dev-master-api.analyzere.net/portfolios/ae53378c-9a94-39f4-898a-4d888d43ae73/";
98 testURL = "https://host/api/loss_sets/dead-beef";
100 }
101
102 [TestMethod, TestCategory(Category)]
118
119 [TestMethod, TestCategory(Category)]
130
131 [TestMethod, TestCategory(Category)]
133 {
134 ILayer[] layers =
135 {
136 new CatXL(), new AggXL(), new Generic(), new SurplusShare(),
138 new Nested(), new Filter()
139 };
141
142 // Resolve all types once to ignore the effect of built in assembly caching
143 Enumerable.Range(0, layers.Length).ToList().ForEach(i =>
144 _ = genericContainer.MakeGenericType(layers[i].GetType()));
145
146 const int samples = 1000000;
147
148 // Test threaded access with caching
149 DateTime start = DateTime.UtcNow;
150 Enumerable.Range(0, samples).AsParallel().ForAll(i =>
151 {
152 Type t = layers[i % layers.Length].GetType();
153 Type _ = genericContainer.MakeGenericTypeFast(t);
154 });
155 TimeSpan cachedReflectionElapsed = DateTime.UtcNow - start;
156 Console.WriteLine(cachedReflectionElapsed.TotalSeconds + " s for caching.");
157
158 // Test threaded access without added caching
159 start = DateTime.UtcNow;
160 Enumerable.Range(0, samples).AsParallel().ForAll(i =>
161 {
162 Type t = layers[i % layers.Length].GetType();
163 Type _ = genericContainer.MakeGenericType(t);
164 });
165 TimeSpan reflectionElapsed = DateTime.UtcNow - start;
166 Console.WriteLine(reflectionElapsed.TotalSeconds + " s for just Reflecting each time.");
167
168 Assert.IsTrue(cachedReflectionElapsed < reflectionElapsed, "Caching is slower " +
169 "than reflecting each time, so it should be removed.");
170 }
171 #endregion Test TypeResolver
172
173 #region Test GetCollectionName
174 [TestMethod, TestCategory(Category)]
176 {
177 Assert.AreEqual("layers", API.GetCollectionName(typeof(Layer),
178 out Type declaringType));
179 Assert.AreEqual(typeof(Layer), declaringType);
180 Assert.AreEqual("portfolios", API.GetCollectionName(typeof(Portfolio),
183 Assert.AreEqual("loss_sets", API.GetCollectionName(typeof(LossSet),
186 }
187
188 [TestMethod, TestCategory(Category)]
190 {
191 Assert.AreEqual("layers", API.GetCollectionName(typeof(CatXL),
192 out Type declaringType));
193 Assert.AreEqual(typeof(Layer), declaringType);
194 Assert.AreEqual("portfolios", API.GetCollectionName(typeof(StaticPortfolio),
197 Assert.AreEqual("loss_sets", API.GetCollectionName(typeof(ELTLossSet),
200 }
201
202 [TestMethod, TestCategory(Category)]
212
213 [TestMethod, TestCategory(Category)]
215 {
216 Assert.AreEqual("layers", API.GetCollectionName(typeof(ILayer),
217 out Type declaringType));
218 Assert.AreEqual(typeof(Layer), declaringType);
221 Assert.AreEqual(typeof(Layer), declaringType);
222 }
223
224 [TestMethod, TestCategory(Category)]
234
235 [TestMethod, TestCategory(Category)]
237 {
238 // Setup a test helper for testing various invalid types
240 {
241 Type runtimeType = typeof(T);
242
243 // Test returning null using the optional argument to avoid throwing
245 Assert.IsNull(declaring);
246
247 string expectedError = $"Invalid type {runtimeType.NiceTypeName()}. " +
248 "Type can be linked to more than one resource";
250 Assert.IsTrue(ex.Message.StartsWith(expectedError), ex.Message);
251 // Test throwing an error using the compile-time-type overloads
252 AssertApi.ExceptionThrown(() => API.GetCollectionName<T>(), exceptionTest);
253 AssertApi.ExceptionThrown(() => API.GetCollectionName<T>(out Type _), exceptionTest);
254 // Test throwing an error using the runtime-type overloads
256 AssertApi.ExceptionThrown(() => API.GetCollectionName(runtimeType, out Type _), exceptionTest);
257 }
258
267
268 // Check a types that doesn't meet boxing requirements against the runtime-type variant
269 // Should still not throw when suppress-throw is set to true.
271 Assert.IsNull(shouldBeNull);
272 // Throws a different error message if suppress-throw is false
273 AssertApi.ExceptionThrown(() => API.GetCollectionName(typeof(IAPIType)),
274 (ArgumentException ex) => Assert.AreEqual(
275 "IAPIType does not derive from IAPIResource.", ex.Message));
276 }
277 #endregion Test GetCollectionName
278
279 #region Test API.PollUntilReady
280 private const int DefaultRetryTime = 10; //ms
281 private const int DefaultQueuePosition = 1;
282 public static int ThrowMockRetryAfter()
283 {
284 return ThrowMockRetryAfter(DefaultRetryTime / 1000d, DefaultQueuePosition);
285 }
286
290 public static int ThrowMockRetryAfter(double? retry_time, int? queue_position)
291 {
292 throw new APIRequestException("Retry", null, MockResponse.RetryAfter(retry_time, queue_position), null);
293 }
294
295 [TestMethod, TestCategory(Category)]
297 {
298 const int returned = 5;
299 int call_counter = 0;
301 // If not supplied any PollingOptions as arguments, PollUntilReady should keep retrying.
302 Assert.AreEqual(returned, API.PollUntilReady(TestFunction, PollingOptions.Default),
303 "Function was not retried as many times as expected.");
304 }
305
306 [TestMethod, TestCategory(Category)]
308 {
309 const int returned = 5;
310 int call_counter = 0;
312 // A NotWaitingException is raised if the wait time is exceeded.
313 AssertApi.ExceptionThrown(() => API.PollUntilReady(TestFunction,
316 {
317 Assert.AreEqual($"Simulation is queued at position {DefaultQueuePosition}. " +
318 $"Server suggests retrying after {DefaultRetryTime / 1000d} " +
319 $"seconds. Please try again later.", ex.Message);
320 Assert.IsNotNull(ex.RetryTime);
321 AssertApi.DoublesAreEqual(DefaultRetryTime / 1000d, ex.RetryTime.Value);
322 });
323
324 // The message includes the maximum wait time if it was supplied
325 call_counter = 0;
326 AssertApi.ExceptionThrown(
327 () => API.PollUntilReady(TestFunction, new PollingOptions(
328 DefaultRetryTime, maxPollTotalTime: DefaultRetryTime / 10)),
330 {
331 Assert.AreEqual($"Simulation is queued at position {DefaultQueuePosition}. " +
332 $"Server suggests retrying after {DefaultRetryTime / 1000d} seconds. " +
333 $"The maximum wait time of {DefaultRetryTime / 10000d} seconds has been exceeded. " +
334 "Please try again later.", ex.Message);
335 Assert.IsNotNull(ex.RetryTime);
336 AssertApi.DoublesAreEqual(DefaultRetryTime / 1000d, ex.RetryTime.Value);
337 });
338 }
339
341 [TestMethod, TestCategory(Category)]
343 {
344 DateTime start = DateTime.UtcNow;
345 const int oneMillisecond = 1;
346 const int timeToWait = 100 * oneMillisecond;
347 const int retryTime = timeToWait * 8 / 10;
348 const int minPollingTime = timeToWait * 1 / 10;
349 const int tolerance = minPollingTime * 2 - oneMillisecond;
350 try
351 {
352 API.PollUntilReady(() => ThrowMockRetryAfter(1, null),
354 Assert.Fail("Expected a NotWaitingException to be thrown.");
355 }
356 catch (NotWaitingException ex)
357 {
358 double elapsed = (DateTime.UtcNow - start).TotalMilliseconds / 1000;
359 Console.WriteLine("PollUntilReady reports time waited was: " + ex.TimeWaited);
360 Console.WriteLine("Time until control was returned to caller: " + elapsed);
361 // The following assertions are millisecond time sensitive and do not work
362 // well in a massive parallel or continuous integration testing environment,
363 // so they are disabled but left to demonstrate the expected behaviour and
364 // permit future debugging.
365 if (ex.TimeWaited - elapsed <= tolerance)
366 {
367 Console.WriteLine("Warning: PollUntilReady took " + (elapsed - ex.TimeWaited) *
368 1000 + " ms longer than desired to stop polling.");
369 //Assert.Fail("PollUntilReady took " + (elapsed - ex.TimeWaited) * 1000 +
370 // " ms longer than desired to stop polling.");
371 }
372 // Assert that we didn't retry for longer than we should have
373 if (ex.TimeWaited > timeToWait + tolerance)
374 {
375 Console.WriteLine("Warning: Waited too long (" + ex.TimeWaited + "s >> " +
376 timeToWait + "s) to give up.");
377 //Assert.Fail("Waited too long (" + ex.TimeWaited + "s >> " +
378 // time_to_wait + "s) to give up.");
379 }
380 // Assert that we didn't give up when we still had time to poll once more.
381 if (ex.TimeWaited < timeToWait - minPollingTime)
382 {
383 Console.WriteLine("Warning: Didn't wait long enough before giving up (" +
384 ex.TimeWaited + "s << " + timeToWait + "s - " + minPollingTime + "s).");
385 //Assert.Fail("Didn't wait long enough before giving up (" +
386 // ex.TimeWaited + "s << " + time_to_wait + "s - " + min_polling_time + "s).");
387 }
388 }
389 }
390
392 [TestMethod, TestCategory(Category)]
394 {
395 const int mockQueuePosition = 5;
396 try
397 {
398 API.PollUntilReady(() => ThrowMockRetryAfter(null, mockQueuePosition), new PollingOptions(maxPollTotalTime: 0));
399 Assert.Fail("Expected a NotWaitingException to be thrown.");
400 }
401 catch (NotWaitingException ex)
402 {
403 Assert.AreEqual(mockQueuePosition, ex.QueuePosition, "Queue position not reported correctly.");
404 Assert.AreEqual($"Simulation is queued at position {mockQueuePosition}. " +
405 "Please try again later.", ex.Message);
406 }
407 }
408
409 [TestMethod, TestCategory(Category)]
411 {
413 // Set up the cancellation token to trigger after roughly 10 retries.
414 canceler.CancelAfter(DefaultRetryTime * 10);
415 // Invoking cancellation should stop the polling with an OperationCanceledException
416 AssertApi.ExceptionThrown<OperationCanceledException>(
417 () => API.PollUntilReady(ThrowMockRetryAfter, new PollingOptions(
418 maxPollInterval: DefaultRetryTime, cancellationToken: canceler.Token)));
419 }
420 #endregion Test API.PollUntilReady
421
422 #region Test AsyncRequestMonitor
423 [TestMethod, TestCategory(Category)]
425 {
426 const int returned = 5;
427 int call_counter = 0;
429
431 new PollingOptions(DefaultRetryTime, DefaultRetryTime)))
432 {
433 monitor.Start();
434 int result = monitor.Wait();
435 Assert.AreEqual(returned, result, "Function was not retried as many times as expected.");
436 Assert.AreEqual(returned, monitor.Result);
437 }
438 }
439
440 [TestMethod, TestCategory(Category)]
442 {
443 // Assert that cancellation using the Stop() method works.
445 new PollingOptions(DefaultRetryTime, DefaultRetryTime)))
446 {
447 monitor.Start();
448 monitor.Stop();
449 AssertApi.ExceptionThrown(() => monitor.Wait(), (AggregateException ex) =>
450 {
451 Assert.IsNotNull(ex.InnerException);
452 Assert.AreEqual(typeof(TaskCanceledException), ex.InnerException.GetType());
453 });
454 Assert.IsNotNull(monitor.ResultException);
455 Assert.AreEqual(typeof(TaskCanceledException), monitor.ResultException.GetType());
456 }
457 }
458
459 [TestMethod, TestCategory(Category)]
461 {
462 // Assert that cancellation using a user-supplied cancellation token works
465 new PollingOptions(DefaultRetryTime, DefaultRetryTime, cancellationToken: canceler.Token)))
466 {
467 monitor.Start();
468 canceler.Cancel();
469 AssertApi.ExceptionThrown(() => monitor.Wait(), (AggregateException ex) =>
470 {
471 Assert.IsNotNull(ex.InnerException);
472 Assert.AreEqual(typeof(TaskCanceledException), ex.InnerException.GetType());
473 });
474 Assert.IsNotNull(monitor.ResultException);
475 Assert.AreEqual(typeof(TaskCanceledException), monitor.ResultException.GetType());
476 }
477
478 // Assert that cancellation using the Stop() method works
479 // when the user supplied their own cancellation token
482 new PollingOptions(DefaultRetryTime, DefaultRetryTime, cancellationToken: canceler.Token)))
483 {
484 monitor.Start();
485 monitor.Stop();
486 AssertApi.ExceptionThrown(() => monitor.Wait(), (AggregateException ex) =>
487 {
488 Assert.IsNotNull(ex.InnerException);
489 Assert.AreEqual(typeof(TaskCanceledException), ex.InnerException.GetType());
490 });
491 Assert.IsNotNull(monitor.ResultException);
492 Assert.AreEqual(typeof(TaskCanceledException), monitor.ResultException.GetType());
493 }
494 }
495 #endregion Test AsyncRequestMonitor
496
497 #region Test Reflection
498 [TestMethod, TestCategory(Category), TestCategory("Randomized")]
500 {
501 Reflection reflection = new Reflection(Samples) { Validation_Enabled = false };
502 DateTime start = DateTime.UtcNow;
503 // Generating a random instance for each type with validation disabled
504 // should not require any server traffic. All references should be invalid.
505 foreach (Type subtype in Samples.GetAPIResourceTypes()
507 {
508 // Generate a random instance of this type
509 object test = reflection.CreateRandomizedInstance(subtype);
511 {
512 Assert.Fail("CreateRandomizedInstance did not generate a valid " +
513 $"{subtype.NiceTypeName()} instance. Result was \"{test ?? "(null)"}\"");
514 continue;
515 }
516
517 Console.WriteLine($"{DateTime.UtcNow:mm:ss.ff} Generated a random " +
518 $"{subtype.NiceTypeName()} with runtime type {test.GetType().NiceTypeName()}");
519 // Check that none of the reference properties are resolved, which confirms that
520 // the reflection utility did not unnecessarily POST any new resources to reference.
522 .GetUserFacingProperties(true, true).ToList();
523 foreach (PropertyInfo prop in writableProperties.Where(p =>
525 {
527 Assert.IsFalse(reference?.resolved ?? false);
528 Console.WriteLine($"{DateTime.UtcNow:mm:ss.ff} Verified that " +
529 $"the reference property {prop.DeclaringType.NiceTypeName()}.{prop.Name} " +
530 "wasn't resolved.");
531 }
532 // Check that every writable property can be re-randomized without issue.
534 {
535 Console.WriteLine($"{DateTime.UtcNow:mm:ss.ff} Randomizing the " +
536 $"{test.GetType().NiceTypeName()} property {prop.PropertyType.NiceTypeName()} " +
537 $"{prop.DeclaringType.NiceTypeName()}.{prop.Name}");
538 reflection.ChangePropertyValueRandomly(testResource, prop);
539 }
540 }
541 // The entire test should have taken well under a second to run if there was truly
542 // no server requests made as is intended when ValidationEnabled is set to false.
543 // We see that it is common now for the test to spend > 2 seconds, especially with the parallel
544 // testing (can be caused by the NUnit), therefore increasing timeout to 10 seconds.
545 TimeSpan elapsed = DateTime.UtcNow - start;
546 Assert.IsTrue(elapsed.TotalSeconds < 10, "Expected this test to take significantly " +
547 "less than 10 seconds to run since it should be making no server requests. " +
548 $"Actual time taken was {elapsed.TotalMilliseconds}ms.");
549 }
550 #endregion Test Reflection
551 }
552}
static Reflection Reflection
Shared instance of a class for generating random resources.
Create a test class that takes care of setting the server URL and cleaning up after each unit test.
static void ApplyConfiguredCredentials()
Apply the current configured API_URL and API_AuthenticationToken credentials to the static API.
Exposes sample resource objects, with built-in methods for injecting dependencies.
Definition Samples.cs:14
static List< Type > GetAPIResourceTypes()
A list of base "production" resource types on the server, (e.g. each with their own collection) used ...
IInjectableResource< BinomialDistribution > Distribution_BinomialDistribution
void Test_API_PollUntilReady_QueuePositionReported()
Test that we don't wait longer than permitted for a response.
void Test_API_PollUntilReady_TimeoutOptionsRespected()
Test that we don't wait longer than permitted for a response.
static int ThrowMockRetryAfter(double? retry_time, int? queue_position)
Throw a server-like retry-after response.
static bool AlmostEquals(double d1, double d2, double tolerance=DEFAULT_DOUBLE_EQUIVALENCE_TOLERANCE)
Determines if two doubles are equivalent within the specified tolerance.
Definition AssertApi.cs:622
static void DoublesAreEqual(double expected, double actual, Func< string > message, double? relative_tolerance=null)
Determines if two doubles are equivalent within the accepted tolerance.
Definition AssertApi.cs:594
Utilities for cleaning up the server.
Definition CleanUp.cs:19
static bool ForceCleanServer(bool ignoreExceptions=false, int? timeout=null)
Invoke the DELETE method on the root of the server. Will wait for the DELETE to complete successfully...
Definition CleanUp.cs:63
static bool CheckServerIsClean(bool ignoreExceptions=false)
Check all known server endpoints and determine whether there are any resources on the server.
Definition CleanUp.cs:23
Retrieve settings from environment variables if they exist, or the project settings file otherwise.
static bool ROOT_DELETE_SUPPORTED
Indicates whether the server currently supports the DELETE method on the server root to wipe out all ...
static bool RESOURCE_DELETE_SUPPORTED
Indicates whether the server currently supports the DELETE method on resources.
static bool EXECUTING_IN_PARALLEL
If true, tests are being run in parallel, so certain assumptions about the state of the server after ...
static MockResponse RetryAfter(double? retry_time, int? queue_position)
Throw a server-like retry-after response.
A series of generated messages and formatted strings for use in unit tests.
Definition Output.cs:13
static string AutoFormat(object obj)
Format a value as a string for output in a message.
Definition Output.cs:16
A collection of filthy hacks to populate some fields of APIResources objects of any type.
Definition Reflection.cs:41
static IEnumerable< Type > GetAllInstantiableSubtypes(Type type)
Creates a list of types that can be derived from a given type.
A custom exception class that includes the RestSharp.IRestResponse that generated the exception,...
Implements the basic reference entity interface, but has no support for resolving references.
Definition Reference.cs:15
Describes a collection of resources which can be listed.
API methods / requests made available to the user.
static string GetCollectionName(Type requestType)
Gets the collection name for the given APIResource type, whether it's instantiable or not.
Abstract representation of a distribution, used for parametric loss sets.
Abstract representation of a layer. This resource type cannot be instantiated instead derived resourc...
Definition Layer.cs:12
Representation of an Aggregate Catastrophe Excess of Loss layer.
Definition AggXL.cs:8
Representation of a Quota Share contract that is limited in annual payouts.
Representation of a Catastrophe Excess of Loss (CatXL) layer.
Definition CatXL.cs:9
Filter is like a layer whose 'terms' are to filter events out of the loss sources....
Definition Filter.cs:13
Representation of an Aggregate Catastrophe Excess of Loss layer with reinstatements.
Definition Generic.cs:10
Representation of an Industry Loss Warranty, which is a layer that triggers a payout (currently expre...
Allows one or more source layers or layer_views to be attached as loss sources to some other layer de...
Definition Nested.cs:22
Representation of a Quota Share contract.
Definition QuotaShare.cs:9
Representation of a Surplus Share contract.
Base class for all LossSet sub-types. A LossSet is a resource that generates sample (trial) losses wh...
Definition LossSet.cs:13
Representation of a single loss set with an associated event loss table.
Definition ELTLossSet.cs:10
Thrown when a request requires additional time to complete, but it exceeds the time we are willing to...
Determines the behaviour of the API when automatically retrying a request whose result is not yet rea...
static PollingOptions Default
The default PollingArguments used when none are specified.
Representation of a portfolio.
Definition Portfolio.cs:12
Representation of a portfolio.
Utility for resolving types given only the types name (Useful for parsing ambiguous JSON objects).
static Type ResolveBaseTypeByURL(string href)
Get the base type of object referenced by the supplied server URL.
static Type GetGenericCovariantBase(Type T)
Returns the most strongly typed covariant base type which is assignable from the specified type T.
T Posted
The posted resource, ready to be referenced.
Interface for Base class used by all resources.
Interface shared by all object types and resources returned by the Analyze Re server.
Definition IAPIType.cs:6
Abstract representation of a layer.
Definition ILayer.cs:7
Base interface for all reference entities.
Abstract representation of a layer with terms.