-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test logics for app insight (#735)
* add test logics for app insight correct app id name add waiting for traces test api key add envs used by agent test update envs referecing clean up test version update nightly e2e tests pipeline * update agent version - fix wrong copy path
- Loading branch information
Showing
8 changed files
with
278 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
...ions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/AppInsight/AppInsightHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Net.Http.Headers; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using E2ETestCases.Utils; | ||
using Xunit; | ||
|
||
namespace Azure.Functions.Java.Tests.E2E.Helpers.AppInsight | ||
{ | ||
public class AppInsightHelper | ||
{ | ||
private const string QueryEndpoint = "https://api.applicationinsights.io/v1/apps/{0}/query?{1}"; | ||
// Trace data will be triggered as soon as node/java app starts. | ||
// Therefore, larger timespan is used here in case traces data will be tested last. | ||
// 15min is estimated on test suite max timeout plus some time buffer. | ||
private const string TracesParameter = "query=traces%7C%20where%20timestamp%20%20%3E%20ago(20min)%7C%20order%20by%20timestamp%20desc%20"; | ||
|
||
public static bool ValidateData(QueryType queryType, String currentSdkVersion) | ||
{ | ||
int loopCount = 1; | ||
List<QueryResultRow> data = QueryAzureMonitorTelemetry(queryType).Result; | ||
|
||
while (data == null || data.Count == 0) | ||
{ | ||
if (loopCount > 0) | ||
{ | ||
// Waiting for 60s for traces showing up in application insight portal | ||
// TODO: is there a more elegant way to wait? | ||
System.Threading.Thread.Sleep(60*1000); | ||
loopCount--; | ||
} | ||
else { | ||
throw new Exception("No Application Insights telemetry available"); | ||
} | ||
} | ||
bool dataFound = false; | ||
foreach (QueryResultRow row in data) | ||
{ | ||
if (row.sdkVersion.IndexOf(currentSdkVersion) >= 0) | ||
{ | ||
dataFound = true; | ||
break; | ||
// TODO: Add extra checks when test apps generate similar telemetry, validate SDK version only for now | ||
} | ||
} | ||
return dataFound; | ||
} | ||
|
||
public static async Task<List<QueryResultRow>> QueryAzureMonitorTelemetry(QueryType queryType, bool isInitialCheck = false) | ||
{ | ||
List<QueryResultRow> aiResult; | ||
Func<List<QueryResultRow>, bool> retryCheck = (result) => result == null; | ||
string queryParemeter = ""; | ||
switch (queryType) | ||
{ | ||
//TODO: test other type of data as well, for now only test trace. | ||
//case QueryType.exceptions: | ||
// queryParemeter = ExceptionsParameter; | ||
// break; | ||
//case QueryType.dependencies: | ||
// queryParemeter = DependenciesParameter; | ||
// break; | ||
//case QueryType.requests: | ||
// queryParemeter = RequestParameter; | ||
// break; | ||
case QueryType.traces: | ||
queryParemeter = TracesParameter; | ||
break; | ||
//case QueryType.statsbeat: | ||
// queryParemeter = StatsbeatParameter; | ||
// break; | ||
} | ||
|
||
aiResult = await QueryMonitorLogWithRestApi(queryType, queryParemeter); | ||
return aiResult; | ||
} | ||
|
||
private static async Task<List<QueryResultRow>> QueryMonitorLogWithRestApi(QueryType queryType, string parameterString) | ||
{ | ||
try | ||
{ | ||
HttpClient client = new HttpClient(); | ||
client.DefaultRequestHeaders.Accept.Add( | ||
new MediaTypeWithQualityHeaderValue("application/json")); | ||
|
||
var apiKeyVal = Constants.ApplicationInsightAPIKey; | ||
var appIdVal = Constants.ApplicationInsightAPPID; | ||
|
||
client.DefaultRequestHeaders.Add("x-api-key", apiKeyVal); | ||
var req = string.Format(QueryEndpoint, appIdVal, parameterString); | ||
var table = await GetHttpResponse(client, req); | ||
return GetJsonObjectFromQuery(table); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine("Failed to query Azure Motinor Ex:" + ex.Message); | ||
} | ||
return null; | ||
} | ||
|
||
// Get http request response | ||
public static async Task<string> GetHttpResponse(HttpClient client, string url) | ||
{ | ||
Uri uri = new Uri(url); | ||
HttpResponseMessage response = null; | ||
try | ||
{ | ||
response = client.GetAsync(uri).Result; | ||
} | ||
catch (Exception e) | ||
{ | ||
Console.WriteLine("GetXhrResponse Error " + e.Message); return null; | ||
} | ||
if (response == null) | ||
{ | ||
Console.WriteLine("GetXhrResponse Error: No Response"); return null; | ||
} | ||
if (!response.IsSuccessStatusCode) | ||
{ | ||
Console.WriteLine($"Response failed. Status code {response.StatusCode}"); | ||
} | ||
return await response.Content.ReadAsStringAsync(); | ||
} | ||
|
||
// Get latest query object with RestApiJsonSerializeRow format | ||
// prerequisite: query parameter is ordered by timestamp desc | ||
private static List<QueryResultRow> GetJsonObjectFromQuery(string table) | ||
{ | ||
if (!(table?.Length > 0)) { return null; } | ||
RestApiJsonSerializeTable SerializeTable = Newtonsoft.Json.JsonConvert.DeserializeObject<RestApiJsonSerializeTable>(table); | ||
if (!(SerializeTable?.Tables?.Count > 0)) { return null; } | ||
List<QueryResultRow> kustoObjectList = new List<QueryResultRow>(); | ||
List<RestApiJsonSerializeCols> columnObjects = SerializeTable.Tables[0].Columns; | ||
List<string[]> rows = SerializeTable.Tables[0].Rows; | ||
if (!(rows?.Count > 0)) { return null; } | ||
foreach (string[] row in rows) | ||
{ | ||
QueryResultRow item = new QueryResultRow(columnObjects, row); | ||
kustoObjectList.Add(item); | ||
} | ||
return kustoObjectList; | ||
} | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/AppInsight/Enums.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace E2ETestCases.Utils | ||
{ | ||
// Query result types | ||
public enum QueryType | ||
{ requests, dependencies, exceptions, traces, statsbeat } | ||
} |
99 changes: 99 additions & 0 deletions
99
....Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Helpers/AppInsight/RestApiJsonSerialize.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace E2ETestCases.Utils | ||
{ | ||
public class RestApiJsonSerializeCols | ||
{ | ||
public string Name { get; set; } | ||
public string Type { get; set; } | ||
} | ||
|
||
public class RestApiJsonSerializeObject | ||
{ | ||
public string Name { get; set; } | ||
public List<RestApiJsonSerializeCols> Columns { get; set; } | ||
public List<string[]> Rows { get; set; } | ||
} | ||
|
||
public class RestApiJsonSerializeTable | ||
{ | ||
public List<RestApiJsonSerializeObject> Tables { get; set; } | ||
} | ||
|
||
//TODO: add unit tests | ||
public class QueryResultRow | ||
{ | ||
Dictionary<string, string> data = new Dictionary<string, string>(); | ||
public QueryResultRow(List<RestApiJsonSerializeCols> cols, string[] rows) | ||
{ | ||
for (int i = 0; i < rows.Length; i++) | ||
{ | ||
data.Add(cols[i].Name, rows[i]); | ||
} | ||
} | ||
// Here are all fields might be used by Monitor log query results. | ||
// Only those fields in Dictionary(data) will be parsed. | ||
public string timestamp { get => getData("timestamp"); } | ||
public string id { get => getData("id"); } | ||
public string source { get => getData("source"); } | ||
public string name { get => getData("name"); } | ||
public string url { get => getData("url"); } | ||
public string target { get => getData("target"); } | ||
public string success { get => getData("success"); } | ||
public string resultCode { get => getData("resultCode"); } | ||
public string duration { get => getData("duration"); } | ||
public string performanceBucket { get => getData("performanceBucket"); } | ||
public string itemType { get => getData("itemType"); } | ||
public dynamic customDimensions { get => getData("customDimensions"); } | ||
public dynamic customMeasurements { get => getData("customMeasurements"); } | ||
public string operation_Name { get => getData("operation_Name"); } | ||
public string operation_Id { get => getData("operation_Id"); } | ||
public string operation_ParentId { get => getData("operation_ParentId"); } | ||
public string operation_SyntheticSource { get => getData("operation_SyntheticSource"); } | ||
public string session_Id { get => getData("session_Id"); } | ||
public string user_Id { get => getData("user_Id"); } | ||
public string user_AuthenticatedId { get => getData("user_AuthenticatedId"); } | ||
public string user_AccountId { get => getData("user_AccountId"); } | ||
public string application_Version { get => getData("application_Version"); } | ||
public string client_Type { get => getData("client_Type"); } | ||
public string client_Model { get => getData("client_Model"); } | ||
public string client_OS { get => getData("client_OS"); } | ||
public string client_IP { get => getData("client_IP"); } | ||
public string client_City { get => getData("client_City"); } | ||
public string client_StateOrProvince { get => getData("client_StateOrProvince"); } | ||
public string client_CountryOrRegion { get => getData("client_CountryOrRegion"); } | ||
public string client_Browser { get => getData("client_Browser"); } | ||
public string cloud_RoleName { get => getData("cloud_RoleName"); } | ||
public string cloud_RoleInstance { get => getData("cloud_RoleInstance"); } | ||
public string appId { get => getData("appId"); } | ||
public string appName { get => getData("appName"); } | ||
public string iKey { get => getData("iKey"); } | ||
public string sdkVersion { get => getData("sdkVersion"); } | ||
public string itemId { get => getData("itemId"); } | ||
public string itemCount { get => getData("itemCount"); } | ||
public string _ResourceId { get => getData("_ResourceId"); } | ||
public string problemId { get => getData("problemId"); } | ||
public string handledAt { get => getData(" handledAt"); } | ||
public string type { get => getData(" type"); } | ||
public string message { get => getData("message"); } | ||
public string assembly { get => getData("assembly"); } | ||
public string method { get => getData("method"); } | ||
public string outerType { get => getData("outerType"); } | ||
public string outerMessage { get => getData("outerMessage"); } | ||
public string outerAssembly { get => getData("outerAssembly"); } | ||
public string outerMethod { get => getData("outerMethod"); } | ||
public string innermostType { get => getData("innermostType"); } | ||
public string innermostMessage { get => getData("innermostMessage"); } | ||
public string innermostAssembly { get => getData("innermostAssembly"); } | ||
public string innermostMethod { get => getData("innermostMethod"); } | ||
public string severityLevel { get => getData("severityLevel"); } | ||
public string details { get => getData("details"); } | ||
|
||
private string getData(string name) | ||
{ | ||
return data.ContainsKey(name) ? data[name] : null; | ||
} | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters