2using System.Collections.Generic;
3using System.Diagnostics;
6using System.Reflection;
7using System.Runtime.Serialization;
16using Microsoft.VisualStudio.TestTools.UnitTesting;
27 #region GET Generic Tests
35 Assert.Inconclusive(
"RUN_OFFLINE = true");
40 Debug.WriteLine(
"List contains " +
response.meta.total_count +
" items.");
42 Assert.IsFalse(
response.items.Any(),
"Expected no items to be returned.");
68 Assert.Inconclusive(
"RANDOMIZED_TESTS_ENABLED = false");
70 Assert.Inconclusive(
"RUN_OFFLINE = true");
80 Console.WriteLine(
$"Posted a random new {randomizedResource.GetType().NiceTypeName()} " +
81 $"with id {randomizedResource.id}");
105 long offset = 0,
long limit = 100,
112 Assert.Inconclusive(
"RANDOMIZED_TESTS_ENABLED = false");
114 Assert.Inconclusive(
"RUN_OFFLINE = true");
121 Assert.IsFalse(String.IsNullOrWhiteSpace(
item.id),
"The " +
122 item.GetType().NiceTypeName() +
" just posted didn't return a valid id ");
133 collection_source.GetItems(
ids, parameters,
timeout);
144 long total_count =
response.meta.total_count;
147 Assert.AreEqual(limit,
response.meta.limit,
"Limit was not respected by the server.");
148 Assert.AreEqual(offset,
response.meta.offset,
"Offset was not respected by the server.");
149 if (total_count - offset >= limit)
151 $"results to be returned, but only {result_count} items were.");
154 Assert.IsTrue(total_count >=
posted.Count,
$"Expected the collection to contain " +
155 $"at least as many items as were posted ({posted.Count}), but the server " +
156 $"only reports having {total_count} items.");
158 $"Expected all {(total_count - offset)} items in the collection after " +
159 $"offset {offset} to be in our results list, but only {result_count} items " +
160 $"were returned. Server total is {total_count}");
170 "One or more items that were successfully posted " +
171 $"weren't found in the list of {total_count} {typeof(T).NiceTypeName()} " +
172 $"objects retrieved with offset {offset} and limit {limit}:\n" +
174 r =>
$"{r.GetType().NiceTypeName()} with id {r.id}")));
193 Assert.Inconclusive(
"RUN_OFFLINE = true");
202 Assert.Inconclusive(
"RUN_OFFLINE = true");
206 Help_Test_Resource_GET_AllPropertiesRecognized(
212 Assert.Inconclusive(
"The following properties were in the JSON response, " +
213 "but were ignored because they had no corresponding property " +
214 "to deserialize to in the .NET object model:\n " +
216 "\"" +
kvp.Key +
"\": " + (
kvp.Value?.ToString() ??
"(null)"))));
220 private static void Help_Test_Resource_GET_AllPropertiesRecognized(
224 if (String.IsNullOrEmpty(
prefix))
277 string message =
$"The response contained a property \"{jsonProperty.Key}\" " +
278 "that does not correspond to a user-facing deserialized data member, but " +
279 $"which corresponds to the {ignored.ReflectedType.NiceTypeName()} " +
280 $"property {ignored.PropertyType.NiceTypeName()} " +
281 $"{ignored.DeclaringType.NiceTypeName()}.{ignored.Name} ";
283 Console.WriteLine(
$"{message} - but this property is marked as Obsolete, " +
284 "so it is safe to assume it is intentionally being ignored.");
286 Console.WriteLine(
$"{message} - but this property is marked as Internal, " +
287 "so it is safe to assume it is being surfaced to the user via " +
288 "another property or is intentionally not user-facing.");
291 Console.WriteLine(
$"{message} - but this property is not-user facing " +
292 "for reasons that don't seem deliberate. This should be clarified.");
325 string if_error_message =
$"We have a property to capture \"{jsonProperty.Key}\"" +
326 $", but the value didn't match. JSON property value: {jsonProperty.Value} " +
327 $"Deserialized Value {propertyValue}";
331 $"value was as the type {jsonValue.GetType()}. This value was converted " +
332 $"to the type {property.PropertyType} to perform this equivalency test.";
339 #endregion GET Generic Tests
341 #region POST Generic Tests and Assertions
348 Assert.Inconclusive(
"RANDOMIZED_TESTS_ENABLED = false");
352 Debug.WriteLine(
"Successfully posted a new " +
typeof(
T).NiceTypeName() +
353 " instance of type " +
subType.NiceTypeName() +
" with the id: " +
posted.id);
362 Assert.Inconclusive(
"RUN_OFFLINE = true");
366 Console.WriteLine(
$"Posted a new {posted.GetType().NiceTypeName()} with id {posted.id}");
368 "No id returned after posting the " +
posted.GetType().NiceTypeName());
467 #region Private Helper Methods
474 "The POST that was successfully before performing additional actions was:\n" +
475 $"Request: {unposted.Serialize()}\nResponse: {posted.Serialize()}");
483 if (EnvironmentSettings.RUN_OFFLINE)
484 Assert.Inconclusive(
"RUN_OFFLINE = true");
486 if (!EnvironmentSettings.EXECUTING_IN_PARALLEL)
495 AssertApi.ExceptionThrown(() =>
502 asWithData.data.UploadString(data, Samples.UploadParams);
503 else if (data !=
null)
505 $"supplied resource type {toPost.GetType()} does not appear to " +
506 $"implement the {typeof(IAPIResource_WithDataEndpoint)} interface.");
508 $"The following request succeeded (but should have failed):\n{toPost.Serialize(true)}\n" +
509 $"The resulting posted resource was:\n{posted.Serialize(true)}");
512 if (!EnvironmentSettings.EXECUTING_IN_PARALLEL)
524 "After a successful POST, followed by a failed PUT of data, we expected the " +
525 "number of items on the server to either increase by 1, or stay the same " +
526 "(if the resource was already on the server), but the count went from " +
531 #endregion Private Helper Methods
532 #endregion POST Generic Tests and Assertions
534 #region PUT Generic Tests and Assertions
547 Assert.Inconclusive(
"RUN_OFFLINE = true");
550 #endregion PUT Generic Tests and Assertions
552 #region Helper Methods
580 #endregion Private Helper Methods
Exposes sample resource objects, with built-in methods for injecting dependencies.
static Parameters UploadParams
The LargeDataUpload parameters to use when uploading data for test fixtures.
static void LogExceptionDetails(Exception ex)
Creates a useful log of the exception details, and in request/response resulting in the error if any ...
static void MethodIsAllowed(Action request, string methodName, bool methodAllowed=true)
Wrap a request in a tryGet with some formatting for testing purposes.
static Action< APIRequestException > ApiExceptionTest(HttpStatusCode expectedStatusCode)
Generate a function that will test a REST request exception in a standard way.
Retrieve settings from environment variables if they exist, or the project settings file otherwise.
static bool RUN_OFFLINE
Controls whether tests that normally require a connection to the server should be allowed to try to r...
static bool RANDOMIZED_TESTS_ENABLED
If false, tests that involve random generation of resources (which can be unstable) will be skipped.
Generic Unit test implementations that will test REST methods on arbitrary resources.
static void GET_Collection< T >(IResourceCollection< T > collection_source)
Ensures that a GET can be done on the collection.
static void POST_InvalidResource_Fails< T >(T invalidToPost)
Verify that posting the specified resource fails with a HttpStatusCode.BadRequest status code.
static T POST_ValidResource< T >(T toPost)
Verify that posting the specified resource succeeds.
static void ExecuteWithConsoleErrorLogging(Action action, Func< string > additionalContext=null)
Wraps the call to the action with a try/catch that doesn't handle the exception, just logs some usefu...
static T UploadData< T >(T resource, string data)
Performs a data upload against the specified resource. Returns the result of a fresh GET on the data ...
static void GET_ExistingResource_Succeeds< T >(T existingResource)
Verify that getting the specified resource succeeds (after posting it).
static void POST_InvalidResource_Fails< T, TException >(T invalidToPost, Action< TException > exceptionTest)
Verify that posting the specified resource fails.
static void GET_Collection_AllSubtypes< T >(IResourceCollection< T > collection_source, Reflection reflection, long offset=0, long limit=100, RequestParameters additionalParameters=null, Action< ICollectionResponse< T > > additionalActions=null, int? timeout=null)
A Mini test suite that discovers all types that derive from the specified base resource type,...
static void POST_InvalidResourceWithData_Fails< T >(T toPost, string data)
Verify that posting the specified resource with data fails.
static void GET_AllPropertiesRecognized< T >(T existingResource)
static T POST_ThenDoAction< T >(T toPost, Action< T > toExecute)
Post a valid resource under the assumption that it will succeed, then perform an action on the result...
static void POST_AllSubtypes_Succeeds< T >(Reflection reflection)
A Mini test suite that discovers all types that derive from the specified base resource type,...
static T POST_WithData_ThenDoAction< T >(T toPost, string data, Action< T > toExecute)
Post a valid resource under the assumption that it will succeed, then perform an action on the result...
static T Try_POST_Random_Resource< T >(Type subtype, Reflection reflection, int attempts=3)
Try to generate and POSt a random new resource on the server of the specified type.
static T POST_ValidResourceWithData< T >(T toPost, string data)
Verify that posting the specified resource succeeds.
static T Mock< T >(string id)
Create an instance of the specified resource.
static void PUT_Fails< T, TException >(T toPut, Action< TException > exceptionTest=null)
Asserts that executing a PUT on the specified resource fails.
static void POST_InvalidResourceWithData_Fails< T, TException >(T toPost, string data, Action< TException > exceptionTest)
Verify that posting the specified resource with data fails.
A collection of filthy hacks to populate some fields of APIResources objects of any type.
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,...
Describes a collection of resources which can be listed.
Base class used by all persistent resources.
Parameters that can be added to your REST requests to access additional API features.
static RequestParameters Ids(IEnumerable< string > ids)
Can be added to your collection GET requests to return only the set of resources whose id matches one...
static RequestParameters Order(string ordering)
Can be added to collection GET requests to specify the way items should be ordered....
static RequestParameters Page(long offset, long limit)
When getting a collection of items, these parameters can be added to restrict the number of results y...
API methods / requests made available to the user.
static ICollectionResponse< IAPIResource > GetResourceList(Type resourceType, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null)
Get a collection of resources from the server.
This attribute is used on IAPIType classes to indicate members (especially public properties) that ex...
Helper class which makes it easier to build a set of request parameters.
RequestParameters AddParameters(IEnumerable< Parameter > collection)
Adds the specified parameters to this list and returns the list.
Describes an APIResource class that adds a "/data" sub-resource, since this functionality is common t...
Interface for Base class used by all resources.
Base interface for all reference entities.
string ref_id
The id of the object being referred to.