Skip to content

Commit dd292c4

Browse files
authored
Merge pull request #141 from contentstack/enhc/DX-3908-enhanced-report
Enhc/dx 3908
2 parents 52dc968 + 96475e4 commit dd292c4

22 files changed

+3155
-800
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ packages/
2424
*/mono**
2525
*/appSettings.json
2626
api_referece/*
27-
.sonarqube/
27+
.sonarqube/
28+
*.html
29+
*.cobertura.xml
30+
integration-test-report_*.html

Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
<PackageReference Include="Moq" Version="4.20.72" />
2626
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
2727
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
28+
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
2829
</ItemGroup>
2930

3031
<ItemGroup>
32+
<Folder Include="Helpers\" />
3133
<Folder Include="IntegrationTest\" />
3234
<Folder Include="Model\" />
3335
<Folder Include="Mock\" />

Contentstack.Management.Core.Tests/Contentstack.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
55
using System.Net;
6+
using System.Net.Http;
67
using System.Reflection;
8+
using Contentstack.Management.Core.Tests.Helpers;
79
using Contentstack.Management.Core.Tests.Model;
810
using Microsoft.Extensions.Configuration;
911
using Microsoft.Extensions.Options;
@@ -19,7 +21,9 @@ private static readonly Lazy<ContentstackClient>
1921
new Lazy<ContentstackClient>(() =>
2022
{
2123
ContentstackClientOptions options = Config.GetSection("Contentstack").Get<ContentstackClientOptions>();
22-
return new ContentstackClient(new OptionsWrapper<ContentstackClientOptions>(options));
24+
var handler = new LoggingHttpHandler();
25+
var httpClient = new HttpClient(handler);
26+
return new ContentstackClient(httpClient, options);
2327
});
2428

2529

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
4+
namespace Contentstack.Management.Core.Tests.Helpers
5+
{
6+
public static class AssertLogger
7+
{
8+
public static void IsNotNull(object value, string name = "")
9+
{
10+
bool passed = value != null;
11+
TestOutputLogger.LogAssertion($"IsNotNull({name})", "NotNull", value?.ToString() ?? "null", passed);
12+
Assert.IsNotNull(value);
13+
}
14+
15+
public static void IsNull(object value, string name = "")
16+
{
17+
bool passed = value == null;
18+
TestOutputLogger.LogAssertion($"IsNull({name})", "null", value?.ToString() ?? "null", passed);
19+
Assert.IsNull(value);
20+
}
21+
22+
public static void AreEqual<T>(T expected, T actual, string name = "")
23+
{
24+
bool passed = Equals(expected, actual);
25+
TestOutputLogger.LogAssertion($"AreEqual({name})", expected?.ToString() ?? "null", actual?.ToString() ?? "null", passed);
26+
Assert.AreEqual(expected, actual);
27+
}
28+
29+
public static void AreEqual<T>(T expected, T actual, string message, string name)
30+
{
31+
bool passed = Equals(expected, actual);
32+
TestOutputLogger.LogAssertion($"AreEqual({name})", expected?.ToString() ?? "null", actual?.ToString() ?? "null", passed);
33+
Assert.AreEqual(expected, actual, message);
34+
}
35+
36+
public static void IsTrue(bool condition, string name = "")
37+
{
38+
TestOutputLogger.LogAssertion($"IsTrue({name})", "True", condition.ToString(), condition);
39+
Assert.IsTrue(condition);
40+
}
41+
42+
public static void IsTrue(bool condition, string message, string name)
43+
{
44+
TestOutputLogger.LogAssertion($"IsTrue({name})", "True", condition.ToString(), condition);
45+
Assert.IsTrue(condition, message);
46+
}
47+
48+
public static void IsFalse(bool condition, string name = "")
49+
{
50+
TestOutputLogger.LogAssertion($"IsFalse({name})", "False", condition.ToString(), !condition);
51+
Assert.IsFalse(condition);
52+
}
53+
54+
public static void IsFalse(bool condition, string message, string name)
55+
{
56+
TestOutputLogger.LogAssertion($"IsFalse({name})", "False", condition.ToString(), !condition);
57+
Assert.IsFalse(condition, message);
58+
}
59+
60+
public static void IsInstanceOfType(object value, Type expectedType, string name = "")
61+
{
62+
bool passed = value != null && expectedType.IsInstanceOfType(value);
63+
TestOutputLogger.LogAssertion(
64+
$"IsInstanceOfType({name})",
65+
expectedType?.Name ?? "null",
66+
value?.GetType()?.Name ?? "null",
67+
passed);
68+
Assert.IsInstanceOfType(value, expectedType);
69+
}
70+
71+
public static T ThrowsException<T>(Action action, string name = "") where T : Exception
72+
{
73+
try
74+
{
75+
action();
76+
TestOutputLogger.LogAssertion($"ThrowsException<{typeof(T).Name}>({name})", typeof(T).Name, "NoException", false);
77+
throw new AssertFailedException($"Expected exception {typeof(T).Name} was not thrown.");
78+
}
79+
catch (T ex)
80+
{
81+
TestOutputLogger.LogAssertion($"ThrowsException<{typeof(T).Name}>({name})", typeof(T).Name, typeof(T).Name, true);
82+
return ex;
83+
}
84+
catch (AssertFailedException)
85+
{
86+
throw;
87+
}
88+
catch (Exception ex)
89+
{
90+
TestOutputLogger.LogAssertion($"ThrowsException<{typeof(T).Name}>({name})", typeof(T).Name, ex.GetType().Name, false);
91+
throw new AssertFailedException(
92+
$"Expected exception {typeof(T).Name} but got {ex.GetType().Name}: {ex.Message}", ex);
93+
}
94+
}
95+
96+
public static void Fail(string message)
97+
{
98+
TestOutputLogger.LogAssertion("Fail", "N/A", message ?? "", false);
99+
Assert.Fail(message);
100+
}
101+
102+
public static void Fail(string message, params object[] parameters)
103+
{
104+
TestOutputLogger.LogAssertion("Fail", "N/A", message ?? "", false);
105+
Assert.Fail(message, parameters);
106+
}
107+
108+
public static void Inconclusive(string message)
109+
{
110+
TestOutputLogger.LogAssertion("Inconclusive", "N/A", message ?? "", false);
111+
Assert.Inconclusive(message);
112+
}
113+
}
114+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net.Http;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace Contentstack.Management.Core.Tests.Helpers
10+
{
11+
public class LoggingHttpHandler : DelegatingHandler
12+
{
13+
public LoggingHttpHandler() : base(new HttpClientHandler()) { }
14+
public LoggingHttpHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
15+
16+
protected override async Task<HttpResponseMessage> SendAsync(
17+
HttpRequestMessage request, CancellationToken cancellationToken)
18+
{
19+
try
20+
{
21+
await CaptureRequest(request);
22+
}
23+
catch
24+
{
25+
// Never let logging break the request
26+
}
27+
28+
var response = await base.SendAsync(request, cancellationToken);
29+
30+
try
31+
{
32+
await CaptureResponse(response);
33+
}
34+
catch
35+
{
36+
// Never let logging break the response
37+
}
38+
39+
return response;
40+
}
41+
42+
private async Task CaptureRequest(HttpRequestMessage request)
43+
{
44+
var headers = new Dictionary<string, string>();
45+
foreach (var h in request.Headers)
46+
headers[h.Key] = string.Join(", ", h.Value);
47+
48+
string body = null;
49+
if (request.Content != null)
50+
{
51+
foreach (var h in request.Content.Headers)
52+
headers[h.Key] = string.Join(", ", h.Value);
53+
54+
await request.Content.LoadIntoBufferAsync();
55+
body = await request.Content.ReadAsStringAsync();
56+
}
57+
58+
var curl = BuildCurl(request.Method.ToString(), request.RequestUri?.ToString(), headers, body);
59+
60+
TestOutputLogger.LogHttpRequest(
61+
method: request.Method.ToString(),
62+
url: request.RequestUri?.ToString() ?? "",
63+
headers: headers,
64+
body: body ?? "",
65+
curlCommand: curl,
66+
sdkMethod: ""
67+
);
68+
}
69+
70+
private async Task CaptureResponse(HttpResponseMessage response)
71+
{
72+
var headers = new Dictionary<string, string>();
73+
foreach (var h in response.Headers)
74+
headers[h.Key] = string.Join(", ", h.Value);
75+
76+
string body = null;
77+
if (response.Content != null)
78+
{
79+
foreach (var h in response.Content.Headers)
80+
headers[h.Key] = string.Join(", ", h.Value);
81+
82+
await response.Content.LoadIntoBufferAsync();
83+
body = await response.Content.ReadAsStringAsync();
84+
}
85+
86+
TestOutputLogger.LogHttpResponse(
87+
statusCode: (int)response.StatusCode,
88+
statusText: response.ReasonPhrase ?? response.StatusCode.ToString(),
89+
headers: headers,
90+
body: body ?? ""
91+
);
92+
}
93+
94+
private static string BuildCurl(string method, string url,
95+
IDictionary<string, string> headers, string body)
96+
{
97+
var sb = new StringBuilder();
98+
sb.Append($"curl -X {method} \\\n");
99+
sb.Append($" '{url}' \\\n");
100+
foreach (var kv in headers)
101+
sb.Append($" -H '{kv.Key}: {kv.Value}' \\\n");
102+
if (!string.IsNullOrEmpty(body))
103+
{
104+
var escaped = body.Replace("'", "'\\''");
105+
sb.Append($" -d '{escaped}'");
106+
}
107+
return sb.ToString().TrimEnd('\\', '\n', ' ');
108+
}
109+
}
110+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
4+
5+
namespace Contentstack.Management.Core.Tests.Helpers
6+
{
7+
public static class TestOutputLogger
8+
{
9+
private const string START_MARKER = "###TEST_OUTPUT_START###";
10+
private const string END_MARKER = "###TEST_OUTPUT_END###";
11+
12+
public static void LogAssertion(string assertionName, object expected, object actual, bool passed)
13+
{
14+
Emit(new Dictionary<string, object>
15+
{
16+
{ "type", "ASSERTION" },
17+
{ "assertionName", assertionName },
18+
{ "expected", expected?.ToString() ?? "null" },
19+
{ "actual", actual?.ToString() ?? "null" },
20+
{ "passed", passed }
21+
});
22+
}
23+
24+
public static void LogHttpRequest(string method, string url,
25+
IDictionary<string, string> headers, string body,
26+
string curlCommand, string sdkMethod)
27+
{
28+
Emit(new Dictionary<string, object>
29+
{
30+
{ "type", "HTTP_REQUEST" },
31+
{ "method", method ?? "" },
32+
{ "url", url ?? "" },
33+
{ "headers", headers ?? new Dictionary<string, string>() },
34+
{ "body", body ?? "" },
35+
{ "curlCommand", curlCommand ?? "" },
36+
{ "sdkMethod", sdkMethod ?? "" }
37+
});
38+
}
39+
40+
public static void LogHttpResponse(int statusCode, string statusText,
41+
IDictionary<string, string> headers, string body)
42+
{
43+
Emit(new Dictionary<string, object>
44+
{
45+
{ "type", "HTTP_RESPONSE" },
46+
{ "statusCode", statusCode },
47+
{ "statusText", statusText ?? "" },
48+
{ "headers", headers ?? new Dictionary<string, string>() },
49+
{ "body", body ?? "" }
50+
});
51+
}
52+
53+
public static void LogContext(string key, string value)
54+
{
55+
Emit(new Dictionary<string, object>
56+
{
57+
{ "type", "CONTEXT" },
58+
{ "key", key ?? "" },
59+
{ "value", value ?? "" }
60+
});
61+
}
62+
63+
private static void Emit(object data)
64+
{
65+
try
66+
{
67+
var json = JsonConvert.SerializeObject(data, Formatting.None);
68+
Console.Write(START_MARKER);
69+
Console.Write(json);
70+
Console.WriteLine(END_MARKER);
71+
}
72+
catch
73+
{
74+
// Never let logging break a test
75+
}
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)