Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ packages/
*/TestResults/*
*/app.config
*/mono**
*/appSettings.json
*/appsettings.json
*/appsettings.template.json
SDK_METHOD_COVERAGE_MAP.md
api_referece/*
.sonarqube/
*.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</ItemGroup>

<ItemGroup>
<Content Include="appSettings.json">
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
Expand Down
74 changes: 73 additions & 1 deletion Contentstack.Management.Core.Tests/Contentstack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@

namespace Contentstack.Management.Core.Tests
{
/// <summary>Holds OAuth credentials from appsettings.json Contentstack:OAuth section.</summary>
public class OAuthConfig
{
public string ClientId { get; set; }
public string AppId { get; set; }
public string RedirectUri { get; set; }
}

public class Contentstack
{
private static readonly Lazy<IConfigurationRoot>
Expand Down Expand Up @@ -43,11 +51,75 @@ private static readonly Lazy<IConfigurationRoot>
return Config.GetSection("Contentstack:MfaSecret").Value;
});

public static IConfigurationRoot Config{ get { return config.Value; } }
// ── New optional config keys ─────────────────────────────────────────

private static readonly Lazy<string> memberEmail =
new Lazy<string>(() => Config.GetSection("Contentstack:MemberEmail").Value);

private static readonly Lazy<string> tfaEmail =
new Lazy<string>(() => Config.GetSection("Contentstack:TfaEmail").Value);

private static readonly Lazy<string> tfaPassword =
new Lazy<string>(() => Config.GetSection("Contentstack:TfaPassword").Value);

private static readonly Lazy<OAuthConfig> oAuthConfig =
new Lazy<OAuthConfig>(() =>
Config.GetSection("Contentstack:OAuth").Get<OAuthConfig>() ?? new OAuthConfig());

private static readonly Lazy<string> personalizeHost =
new Lazy<string>(() =>
Config.GetSection("Contentstack:PersonalizeHost").Value
?? "personalize-api.contentstack.com");

private static readonly Lazy<bool> deleteDynamicResources =
new Lazy<bool>(() =>
!string.Equals(
Config.GetSection("Contentstack:DeleteDynamicResources").Value,
"false", StringComparison.OrdinalIgnoreCase));

private static readonly Lazy<bool> damV2Enabled =
new Lazy<bool>(() =>
string.Equals(
Config.GetSection("Contentstack:DamV2Enabled").Value,
"true", StringComparison.OrdinalIgnoreCase));

private static readonly Lazy<string> amOrgUid =
new Lazy<string>(() => Config.GetSection("Contentstack:AmOrgUid").Value);

// ── Public accessors ─────────────────────────────────────────────────

public static IConfigurationRoot Config { get { return config.Value; } }
public static NetworkCredential Credential { get { return credential.Value; } }
public static OrganizationModel Organization { get { return organization.Value; } }
public static string MfaSecret { get { return mfaSecret.Value; } }

/// <summary>Secondary user email for team / stack-sharing tests.</summary>
public static string MemberEmail => memberEmail.Value;

/// <summary>Email of a 2FA-enabled account for testing the TFA login flow.</summary>
public static string TfaEmail => tfaEmail.Value;

/// <summary>Password matching TfaEmail.</summary>
public static string TfaPassword => tfaPassword.Value;

/// <summary>OAuth app credentials (ClientId, AppId, RedirectUri).</summary>
public static OAuthConfig OAuth => oAuthConfig.Value;

/// <summary>Personalize API host; defaults to personalize-api.contentstack.com.</summary>
public static string PersonalizeHost => personalizeHost.Value;

/// <summary>
/// When true (default) the dynamically created test stack is deleted after the run.
/// Set Contentstack:DeleteDynamicResources=false in appsettings.json to preserve it.
/// </summary>
public static bool DeleteDynamicResources => deleteDynamicResources.Value;

/// <summary>Enables DAM 2.0 / asset-scan-status tests.</summary>
public static bool DamV2Enabled => damV2Enabled.Value;

/// <summary>Org UID for AM (Advanced Managed) org tests.</summary>
public static string AmOrgUid => amOrgUid.Value;

public static StackModel Stack { get; set; }

// TOTP token tracking to prevent reuse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,23 @@ public LoggingHttpHandler(HttpMessageHandler innerHandler) : base(innerHandler)
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
DateTime requestedAt = DateTime.UtcNow;

try
{
await CaptureRequest(request);
await CaptureRequest(request, requestedAt);
}
catch
{
// Never let logging break the request
}

var response = await base.SendAsync(request, cancellationToken);
DateTime respondedAt = DateTime.UtcNow;

try
{
await CaptureResponse(response);
await CaptureResponse(response, requestedAt, respondedAt);
}
catch
{
Expand All @@ -39,7 +42,7 @@ protected override async Task<HttpResponseMessage> SendAsync(
return response;
}

private async Task CaptureRequest(HttpRequestMessage request)
private async Task CaptureRequest(HttpRequestMessage request, DateTime requestedAt)
{
var headers = new Dictionary<string, string>();
foreach (var h in request.Headers)
Expand All @@ -63,11 +66,12 @@ private async Task CaptureRequest(HttpRequestMessage request)
headers: headers,
body: body ?? "",
curlCommand: curl,
sdkMethod: ""
sdkMethod: "",
timestamp: requestedAt
);
}

private async Task CaptureResponse(HttpResponseMessage response)
private async Task CaptureResponse(HttpResponseMessage response, DateTime requestedAt, DateTime respondedAt)
{
var headers = new Dictionary<string, string>();
foreach (var h in response.Headers)
Expand All @@ -87,7 +91,9 @@ private async Task CaptureResponse(HttpResponseMessage response)
statusCode: (int)response.StatusCode,
statusText: response.ReasonPhrase ?? response.StatusCode.ToString(),
headers: headers,
body: body ?? ""
body: body ?? "",
timestamp: respondedAt,
durationMs: (long)(respondedAt - requestedAt).TotalMilliseconds
);
}

Expand Down
13 changes: 9 additions & 4 deletions Contentstack.Management.Core.Tests/Helpers/TestOutputLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public static void LogAssertion(string assertionName, object expected, object ac

public static void LogHttpRequest(string method, string url,
IDictionary<string, string> headers, string body,
string curlCommand, string sdkMethod)
string curlCommand, string sdkMethod,
DateTime? timestamp = null)
{
Emit(new Dictionary<string, object>
{
Expand All @@ -33,20 +34,24 @@ public static void LogHttpRequest(string method, string url,
{ "headers", headers ?? new Dictionary<string, string>() },
{ "body", body ?? "" },
{ "curlCommand", curlCommand ?? "" },
{ "sdkMethod", sdkMethod ?? "" }
{ "sdkMethod", sdkMethod ?? "" },
{ "timestamp", (timestamp ?? DateTime.UtcNow).ToString("yyyy-MM-ddTHH:mm:ss.fffZ") }
});
}

public static void LogHttpResponse(int statusCode, string statusText,
IDictionary<string, string> headers, string body)
IDictionary<string, string> headers, string body,
DateTime? timestamp = null, long durationMs = 0)
{
Emit(new Dictionary<string, object>
{
{ "type", "HTTP_RESPONSE" },
{ "statusCode", statusCode },
{ "statusText", statusText ?? "" },
{ "headers", headers ?? new Dictionary<string, string>() },
{ "body", body ?? "" }
{ "body", body ?? "" },
{ "timestamp", (timestamp ?? DateTime.UtcNow).ToString("yyyy-MM-ddTHH:mm:ss.fffZ") },
{ "durationMs", durationMs }
});
}

Expand Down
Loading
Loading