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 TimeZoneAdjustmentExtension { public static DateTime ToOffsetAdjustedUniversalTime(this DateTime dtIn) { //Date filtering tests MUST use a relative time zone adjusted to users's forced user options time zone //or the tests that cross dst / pst boundaries will fail //See servers spec doc core-locale-currency-numbers-time-and-dates.txt for details //####################################################################################################### double TIME_ZONE_ADJUSTMENT = -7;// <---=== SET THIS TO SAME AS GENERATED DATA OFFSET AT SERVER //####################################################################################################### return DateTime.SpecifyKind(dtIn.AddHours(((TIME_ZONE_ADJUSTMENT) * -1)), DateTimeKind.Utc); } } 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 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 authDict = new ConcurrentDictionary();//10,32 private static AutoId Auto { get; } = new AutoId(0); //DateTime adjustment methods for relative date filter testing // 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 DateTime ToOffsetAdjustedUniversalTime(DateTime dtIn) // { // Double TimeZoneAdjustment = ((double)TIME_ZONE_ADJUSTMENT) * -1; // return dtIn.AddHours(TimeZoneAdjustment).ToUniversalTime(); // } public static string Uniquify(string s) { // return s + " " + Auto.GetNext().ToString(); return s + " " + (Auto.GetNext() + ((DateTimeOffset)DateTime.Now).ToUnixTimeMilliseconds()).ToString(); } public async static Task GetTokenAsync(string login, string password = null) { return await CredRepo.GetTokenAsync(login, password); // 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(); // } // 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; } /// /// Get the path to the server root with route appended /// this is used to fetch non-api docs etc /// /// /// 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 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 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 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 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 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 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 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 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 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) }; } /// /// /// /// /// public 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); } /// /// validate a not found response /// /// 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().Should().Be(2010); } /// /// validate a concurrency error /// /// 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().Should().Be(2005); } /// /// validate that the call violates referential integrity /// /// 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().Should().Be(2200); } /// /// validate a bad ModelState response /// /// 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().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().Should().Be(CheckFirstTargetExists); } } // public enum ValidationErrorType // { // RequiredPropertyEmpty = 1, // LengthExceeded = 2, // NotUnique = 3, // StartDateMustComeBeforeEndDate = 4, // InvalidValue = 5 // } /// /// assert contains validation target and error code /// /// /// /// 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( z => z["target"].Value() == target && z["error"].Value() == error && z["message"].Value().Contains(shouldContainInMessage)); } else { a.ObjectResponse["error"]["details"].Should().Contain(z => z["target"].Value() == target && z["error"].Value() == error); } } /// /// validate server exception response /// /// 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().Should().Be(2002); } /// /// Validate an expected api error code and http code response /// /// /// /// 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().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(); } /// /// Generate a custom fields string that satisfies the rules in the sample test data /// /// 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(); } /// /// Generate a custom fields string that satisfies the rules in this project (formCustom) /// /// 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(); } //// [{fld:"COLUMN UNIQUE KEY ID",sort:"-" or "+",filter:{any:true/false,items:[{FILTER OBJECT SEE BELOW}]} }, {key:"second column unique key"},{...etc...}] public static object BuildSimpleFilterDataListViewColumn(string fieldKey, string filterOp, object filterValue, string sort = null) { dynamic d = new JObject(); d.fld = fieldKey; if (sort != null) d.sort = sort; dynamic filter = new JObject(); dynamic items = new JArray(); dynamic fitem = new JObject(); fitem.op = filterOp; fitem.value = filterValue; items.Add(fitem); filter.items = items; d.filter = filter; return d; } public static object BuildSimpleSortDataListViewColumn(string fieldKey, string sort) { dynamic d = new JObject(); d.fld = fieldKey; d.sort = sort; return d; } //t(dfilter, dsortarray, 999, 0, false, "UserDataList")); // public static string BuildDataListRequest(object p = null, object o = null, int limit = 999, int offset = 0, bool bWhatever=false, string dataListKey = "TestWidgetDataList") // { // throw new System.NotImplementedException(); // } public static string BuildDataListRequestEx(dynamic dListView = null, int limit = 999, int offset = 0, string dataListKey = "TestWidgetDataList") { /* { "offset": 0, "limit": 3, "dataListKey": "TestWidgetDataList", "listView": "" } */ dynamic d = new JObject(); d.offset = offset; d.limit = limit; d.dataListKey = dataListKey; if (dListView != null) d.ListView = dListView.ToString(Newtonsoft.Json.Formatting.None); return d.ToString(Newtonsoft.Json.Formatting.None); } //--------- }//eoc }//eons