568 lines
24 KiB
C#
568 lines
24 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using Newtonsoft.Json.Linq;
|
|
using FluentAssertions;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace raven_integration
|
|
{
|
|
public static class Util
|
|
{
|
|
//#######################################################################################################
|
|
public static string API_BASE_URL = "http://localhost:7575/api/v8/";
|
|
//public static string API_BASE_URL = "https://test.helloayanova.com/api/v8.0/";
|
|
public static string TEST_DATA_FOLDER = @"..\..\..\testdata\";
|
|
|
|
public const decimal TIME_ZONE_ADJUSTMENT = -7;
|
|
//#######################################################################################################
|
|
|
|
|
|
|
|
|
|
public const string OpEquality = "=";
|
|
public const string OpGreaterThan = ">";
|
|
public const string OpGreaterThanOrEqualTo = ">=";
|
|
public const string OpLessThan = "<";
|
|
public const string OpLessThanOrEqualTo = "<=";
|
|
public const string OpNotEqual = "!=";
|
|
public const string OpNotLike = "!%";
|
|
public const string OpStartsWith = "%-";
|
|
public const string OpEndsWith = "-%";
|
|
public const string OpContains = "-%-";
|
|
public const string OpNotContains = "!-%-";
|
|
|
|
private static HttpClient client { get; } = new HttpClient();
|
|
|
|
|
|
|
|
public static ConcurrentDictionary<string, string> authDict = new ConcurrentDictionary<string, string>();//10,32
|
|
|
|
private static AutoId Auto { get; } = new AutoId(0);
|
|
|
|
//DateTime stuff
|
|
|
|
public static DateTime RelativeToday()
|
|
{
|
|
Double TimeZoneAdjustment = ((double)TIME_ZONE_ADJUSTMENT) * -1;
|
|
return DateTime.Today.AddHours(TimeZoneAdjustment);
|
|
}
|
|
|
|
public static DateTime RelativeNow()
|
|
{
|
|
Double TimeZoneAdjustment = ((double)TIME_ZONE_ADJUSTMENT) * -1;
|
|
return DateTime.Now.AddHours(TimeZoneAdjustment);
|
|
}
|
|
|
|
public static string Uniquify(string s)
|
|
{
|
|
// return s + " " + Auto.GetNext().ToString();
|
|
return s + " " + (Auto.GetNext() + ((DateTimeOffset)DateTime.Now).ToUnixTimeMilliseconds()).ToString();
|
|
}
|
|
|
|
public async static Task<string> GetTokenAsync(string login, string password = null)
|
|
{
|
|
|
|
if (password == null)
|
|
password = login;
|
|
|
|
if (!authDict.ContainsKey(login))
|
|
{
|
|
dynamic creds = new JObject();
|
|
creds.login = login;
|
|
creds.password = password;
|
|
|
|
ApiResponse a = await Util.PostAsync("Auth", null, creds.ToString());
|
|
//Put this in when having concurrency issue during auth and old style dl token creation during login
|
|
ValidateDataReturnResponseOk(a);
|
|
|
|
authDict[login] = a.ObjectResponse["data"]["token"].Value<string>();
|
|
}
|
|
return authDict[login];
|
|
}
|
|
|
|
|
|
|
|
static bool bInitialized = false;
|
|
private static void init()
|
|
{
|
|
if (bInitialized) return;
|
|
if (!System.IO.Directory.Exists(TEST_DATA_FOLDER))
|
|
throw new ArgumentOutOfRangeException($"Test data folder {TEST_DATA_FOLDER} not found, current folder is {System.AppDomain.CurrentDomain.BaseDirectory}");
|
|
|
|
client.DefaultRequestHeaders.Accept.Clear();
|
|
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
|
bInitialized = true;
|
|
}
|
|
|
|
public static string CleanApiRoute(string route)
|
|
{
|
|
route = route.TrimStart('/');
|
|
return API_BASE_URL + route;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the path to the server root with route appended
|
|
/// this is used to fetch non-api docs etc
|
|
/// </summary>
|
|
/// <param name="route"></param>
|
|
/// <returns></returns>
|
|
public static string CleanNonApiRoute(string route)
|
|
{
|
|
// public static string API_BASE_URL = "http://localhost:7575/api/v8/";
|
|
//public static string API_BASE_URL = "https://test.helloayanova.com/api/v8.0/";
|
|
|
|
|
|
if (!route.StartsWith('/'))
|
|
{
|
|
route = "/" + route;
|
|
}
|
|
|
|
return API_BASE_URL.Split("/api/")[0] + route;
|
|
}
|
|
|
|
public async static Task<ApiResponse> GetAsync(string route, string authToken = null, string bodyJsonData = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
if (!string.IsNullOrWhiteSpace(bodyJsonData))
|
|
requestMessage.Content = new StringContent(bodyJsonData, System.Text.Encoding.UTF8, "application/json");
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
|
|
public async static Task<ApiTextResponse> GetTextResultAsync(string route, string authToken = null, string bodyJsonData = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
if (!string.IsNullOrWhiteSpace(bodyJsonData))
|
|
requestMessage.Content = new StringContent(bodyJsonData, System.Text.Encoding.UTF8, "application/json");
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiTextResponse() { HttpResponse = response, TextResponse = responseAsString };
|
|
}
|
|
|
|
|
|
public async static Task<ApiTextResponse> GetNonApiPageAsync(string route)
|
|
{
|
|
init();
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, CleanNonApiRoute(route));
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
return new ApiTextResponse() { HttpResponse = response, TextResponse = responseAsString };
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task<HttpResponseMessage> DownloadFileAsync(string route, string authToken = null)
|
|
{
|
|
|
|
init();
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Get, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
return response;
|
|
// if (response.IsSuccessStatusCode)
|
|
// {
|
|
// return await response.Content.ReadAsByteArrayAsync();
|
|
// }
|
|
|
|
// return null;
|
|
}
|
|
|
|
|
|
public async static Task<ApiResponse> PostAsync(string route, string authToken = null, string postJson = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
if (!string.IsNullOrWhiteSpace(postJson))
|
|
requestMessage.Content = new StringContent(postJson, System.Text.Encoding.UTF8, "application/json");
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
public async static Task<ApiResponse> PostFormDataAsync(string route, MultipartFormDataContent formContent, string authToken = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
requestMessage.Content = formContent;
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
|
|
public async static Task<ApiResponse> PutAsync(string route, string authToken = null, string putJson = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Put, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
if (!string.IsNullOrWhiteSpace(putJson))
|
|
requestMessage.Content = new StringContent(putJson, System.Text.Encoding.UTF8, "application/json");
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
|
|
public async static Task<ApiResponse> PatchAsync(string route, string authToken = null, string patchJson = null)
|
|
{
|
|
init();
|
|
|
|
var method = new HttpMethod("PATCH");
|
|
|
|
var requestMessage = new HttpRequestMessage(method, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
if (!string.IsNullOrWhiteSpace(patchJson))
|
|
requestMessage.Content = new StringContent(patchJson, System.Text.Encoding.UTF8, "application/json-patch+json");
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
|
|
public async static Task<ApiResponse> DeleteAsync(string route, string authToken = null)
|
|
{
|
|
init();
|
|
|
|
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, CleanApiRoute(route));
|
|
if (!string.IsNullOrWhiteSpace(authToken))
|
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
|
|
|
HttpResponseMessage response = await client.SendAsync(requestMessage);
|
|
var responseAsString = await response.Content.ReadAsStringAsync();
|
|
|
|
return new ApiResponse() { HttpResponse = response, ObjectResponse = Parse(responseAsString) };
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="jsonString"></param>
|
|
/// <returns></returns>
|
|
private static JObject Parse(string jsonString)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(jsonString))
|
|
{
|
|
return null;
|
|
}
|
|
return JObject.Parse(jsonString);
|
|
}
|
|
|
|
//https://www.newtonsoft.com/json/help/html/FromObject.htm
|
|
private static string ObjectToJsonString(object o)
|
|
{
|
|
JObject j = (JObject)JToken.FromObject(o);
|
|
return j.ToString();
|
|
}
|
|
|
|
public static void ValidateDataReturnResponseOk(ApiResponse a)
|
|
{
|
|
a.ObjectResponse.Should().NotBeNull("No response was found at all?! Nothing to validate");
|
|
|
|
var ErrorMessage = string.Empty;
|
|
var ERR = a.ObjectResponse["error"];
|
|
if (ERR != null)
|
|
{
|
|
ErrorMessage = a.ObjectResponse.ToString();
|
|
// var ecode = ERR["code"];
|
|
// if (ecode != null)
|
|
// ErrorMessage += $"CODE: {ecode} ";
|
|
|
|
// var emsg = ERR["message"];
|
|
// if (emsg != null)
|
|
// ErrorMessage += $"MESSAGE: {emsg} ";
|
|
|
|
// var etarget = ERR["target"];
|
|
// if (etarget != null)
|
|
// ErrorMessage += $"TARGET: {etarget} ";
|
|
}
|
|
|
|
a.ObjectResponse["error"].Should().BeNull("because there should not be an error on an api call, error result was: {0}", ErrorMessage);
|
|
a.ObjectResponse["data"].Should().NotBeNull("A result should be returned");
|
|
}
|
|
|
|
public static void ValidateNoErrorInResponse(ApiResponse a)
|
|
{
|
|
a.ObjectResponse["error"].Should().BeNull("There should not be an error on an api call");
|
|
}
|
|
|
|
public static void ValidateHTTPStatusCode(ApiResponse a, int DesiredStatusCode)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(DesiredStatusCode);
|
|
}
|
|
|
|
public static void ValidateHTTPStatusCode(ApiTextResponse t, int DesiredStatusCode)
|
|
{
|
|
((int)t.HttpResponse.StatusCode).Should().Be(DesiredStatusCode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// validate a not found response
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
public static void ValidateResponseNotFound(ApiResponse a)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(404);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(2010);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// validate a concurrency error
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
public static void ValidateConcurrencyError(ApiResponse a)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(409);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(2005);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// validate that the call violates referential integrity
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
public static void ValidateViolatesReferentialIntegrityError(ApiResponse a)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(400);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(2200);
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// validate a bad ModelState response
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
public static void ValidateBadModelStateResponse(ApiResponse a, string CheckFirstTargetExists = null)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(400);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(2200);
|
|
a.ObjectResponse["error"]["details"].Should().NotBeNull("There should be error details on the api call");
|
|
if (!string.IsNullOrWhiteSpace(CheckFirstTargetExists))
|
|
{
|
|
a.ObjectResponse["error"]["details"][0]["target"].Value<string>().Should().Be(CheckFirstTargetExists);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// public enum ValidationErrorType
|
|
// {
|
|
// RequiredPropertyEmpty = 1,
|
|
// LengthExceeded = 2,
|
|
// NotUnique = 3,
|
|
// StartDateMustComeBeforeEndDate = 4,
|
|
// InvalidValue = 5
|
|
|
|
// }
|
|
|
|
/// <summary>
|
|
/// assert contains validation target and error code
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
/// <param name="target"></param>
|
|
/// <param name="error"></param>
|
|
public static void ShouldContainValidationError(ApiResponse a, string target, string error, string shouldContainInMessage = null)
|
|
{
|
|
a.ObjectResponse["error"]["details"].Should().NotBeNull("There should be Details on the api call");
|
|
if (shouldContainInMessage != null)
|
|
{
|
|
a.ObjectResponse["error"]["details"].Should().Contain(
|
|
m => m["target"].Value<string>() == target &&
|
|
m["error"].Value<string>() == error &&
|
|
m["message"].Value<string>().Contains(shouldContainInMessage));
|
|
}
|
|
else
|
|
{
|
|
a.ObjectResponse["error"]["details"].Should().Contain(m => m["target"].Value<string>() == target && m["error"].Value<string>() == error);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// validate server exception response
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
public static void ValidateServerExceptionResponse(ApiResponse a)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(500);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(2002);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Validate an expected api error code and http code response
|
|
/// </summary>
|
|
/// <param name="a"></param>
|
|
/// <param name="apiErrorCode"></param>
|
|
/// <param name="httpStatusCode"></param>
|
|
public static void ValidateErrorCodeResponse(ApiResponse a, int apiErrorCode, int httpStatusCode)
|
|
{
|
|
((int)a.HttpResponse.StatusCode).Should().Be(httpStatusCode);
|
|
a.ObjectResponse["error"].Should().NotBeNull("There should be an error on the api call");
|
|
a.ObjectResponse["error"]["code"].Value<int>().Should().Be(apiErrorCode);
|
|
}
|
|
|
|
|
|
public static string GenerateCustomFieldsJsonString(
|
|
string c1 = null, string c2 = null, string c3 = null, string c4 = null,
|
|
string c5 = null, string c6 = null, string c7 = null, string c8 = null, string c9 = null,
|
|
string c10 = null, string c11 = null, string c12 = null, string c13 = null,
|
|
string c14 = null, string c15 = null, string c16 = null
|
|
)
|
|
{
|
|
|
|
//Custom fields
|
|
//"[{c1:"blah"}]
|
|
dynamic dCustomField = new JObject();
|
|
dCustomField.c1 = c1;
|
|
dCustomField.c2 = c2;
|
|
dCustomField.c3 = c3;
|
|
dCustomField.c4 = c4;
|
|
dCustomField.c5 = c5;
|
|
dCustomField.c6 = c6;
|
|
dCustomField.c7 = c7;
|
|
dCustomField.c8 = c8;
|
|
dCustomField.c9 = c9;
|
|
dCustomField.c10 = c10;
|
|
dCustomField.c11 = c11;
|
|
dCustomField.c12 = c12;
|
|
dCustomField.c13 = c13;
|
|
dCustomField.c14 = c14;
|
|
dCustomField.c15 = c15;
|
|
dCustomField.c16 = c16;
|
|
return dCustomField.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a custom fields string that satisfies the rules in the sample test data
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static string WidgetRequiredCustomFieldsJsonString()
|
|
{/*CURRENT TEMPLATE DEFINITION FOR WIDGET CUSTOM FIELDS, FROM SEEDER.CS SERVER PROJECT
|
|
var fc = new FormCustom()
|
|
{
|
|
FormKey = ObjectFields.WIDGET_KEY,
|
|
Template = @"[
|
|
{
|
|
""fld"": ""WidgetNotes"",
|
|
""required"": true
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom1"",
|
|
""required"": false,
|
|
""type"": 1
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom2"",
|
|
""required"": true,
|
|
""type"": 4
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom3"",
|
|
""required"": false,
|
|
""type"": 5
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom4"",
|
|
""required"": false,
|
|
""type"": 6
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom5"",
|
|
""required"": false,
|
|
""type"": 8
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom6"",
|
|
""required"": false,
|
|
""type"": 2
|
|
},
|
|
{
|
|
""fld"": ""WidgetCustom7"",
|
|
""required"": false,
|
|
""type"": 3
|
|
}
|
|
]"
|
|
};
|
|
*/
|
|
|
|
//Custom fields
|
|
//"[{c1:"blah"}]
|
|
dynamic dCustomField = new JObject();
|
|
dCustomField.c1 = "2019-02-08T06:31:48.0019809Z";
|
|
dCustomField.c2 = "c2 text";
|
|
dCustomField.c3 = "333";
|
|
dCustomField.c4 = "true";
|
|
dCustomField.c5 = "5.55";
|
|
return dCustomField.ToString();
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Generate a custom fields string that satisfies the rules in this project (formCustom)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static string UserRequiredCustomFieldsJsonString()
|
|
{
|
|
//Note c1 is only required custom, c2 is defined but is not required so skipping it here
|
|
dynamic dCustomField = new JObject();
|
|
dCustomField.c1 = "UnitTestUtilSampleText";
|
|
return dCustomField.ToString();
|
|
}
|
|
|
|
}//eoc
|
|
}//eons
|