-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[chore] Add integration test for sync/async support (#500)
- Add integration test for sync/async support - Add VCR, utilities to integration test
- Loading branch information
Showing
15 changed files
with
1,370 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
using System.Web.Mvc; | ||
using EasyPost.Integration.Utilities; | ||
using EasyPost.Integration.Utilities.Attributes; | ||
using EasyPost.Models.API; | ||
using EasyPost.Parameters.Parcel; | ||
using Xunit; | ||
|
||
namespace EasyPost.Integration; | ||
|
||
public class Synchronous | ||
{ | ||
private Utils.VCR Vcr { get; } = new("synchronous", Utils.ApiKey.Test); | ||
|
||
/// <summary> | ||
/// Test that an end-user can run asynchronous code asynchronously | ||
/// </summary> | ||
[Fact, Testing.Run] | ||
public async void TestUserCanRunAsyncCodeAsynchronously() | ||
{ | ||
var client = Vcr.SetUpTest("async"); | ||
|
||
// create a parcel asynchronously | ||
var parcel = await client.Parcel.Create(new Create | ||
{ | ||
Height = 1, | ||
Length = 1, | ||
Width = 1, | ||
Weight = 1, | ||
}); | ||
Assert.NotNull(parcel); | ||
Assert.IsType<Parcel>(parcel); | ||
|
||
string parcelId = parcel.Id!; | ||
|
||
// retrieve a parcel asynchronously | ||
var retrievedParcel = await client.Parcel.Retrieve(parcelId); | ||
Assert.NotNull(retrievedParcel); | ||
Assert.IsType<Parcel>(retrievedParcel); | ||
} | ||
|
||
/// <summary> | ||
/// Test that an end-user can run asynchronous code synchronously via .Result | ||
/// </summary> | ||
[Fact, Testing.Run] | ||
public void TestUserCanRunAsyncCodeSynchronouslyViaResult() | ||
{ | ||
var client = Vcr.SetUpTest("via_result"); | ||
|
||
// create a parcel via .Result | ||
var parcel = client.Parcel.Create(new Create | ||
{ | ||
Height = 1, | ||
Length = 1, | ||
Width = 1, | ||
Weight = 1, | ||
}).Result; | ||
Assert.NotNull(parcel); | ||
Assert.IsType<Parcel>(parcel); | ||
|
||
string parcelId = parcel.Id!; | ||
|
||
// retrieve a parcel via .Result | ||
var retrievedParcel = client.Parcel.Retrieve(parcelId).Result; | ||
Assert.NotNull(retrievedParcel); | ||
Assert.IsType<Parcel>(retrievedParcel); | ||
} | ||
|
||
/// <summary> | ||
/// Test that an end-user can run asynchronous code synchronously via .GetAwaiter().GetResult() | ||
/// </summary> | ||
[Fact, Testing.Run] | ||
public void TestUserCanRunAsyncCodeSynchronouslyViaGetAwaiter() | ||
{ | ||
var client = Vcr.SetUpTest("via_get_awaiter"); | ||
|
||
// create a parcel via GetAwaiter().GetResult() | ||
var parcel = client.Parcel.Create(new Create | ||
{ | ||
Height = 1, | ||
Length = 1, | ||
Width = 1, | ||
Weight = 1, | ||
}).GetAwaiter().GetResult(); | ||
Assert.NotNull(parcel); | ||
Assert.IsType<Parcel>(parcel); | ||
|
||
string parcelId = parcel.Id!; | ||
|
||
// retrieve a parcel via GetAwaiter().GetResult() | ||
var retrievedParcel = client.Parcel.Retrieve(parcelId).GetAwaiter().GetResult(); | ||
Assert.NotNull(retrievedParcel); | ||
Assert.IsType<Parcel>(retrievedParcel); | ||
} | ||
} | ||
|
||
#pragma warning disable CA3147 // Mark Verb Handlers With Validate Antiforgery Token | ||
/// <summary> | ||
/// Test that an end-user can run asynchronous code in System.Web.Mvc.Controller | ||
/// </summary> | ||
public class SynchronousMvcController : System.Web.Mvc.Controller | ||
{ | ||
private Utils.VCR Vcr { get; } = new("synchronous_mvc_controller", Utils.ApiKey.Test); | ||
|
||
/// <summary> | ||
/// Test that an end-user can run asynchronous code asynchronously | ||
/// </summary> | ||
[Fact, Testing.Run] | ||
public async Task<ActionResult> TestUserCanRunAsyncCodeAsynchronously() | ||
{ | ||
var client = Vcr.SetUpTest("async"); | ||
|
||
// create a parcel asynchronously | ||
var parcel = await client.Parcel.Create(new Create | ||
{ | ||
Height = 1, | ||
Length = 1, | ||
Width = 1, | ||
Weight = 1, | ||
}); | ||
Assert.NotNull(parcel); | ||
Assert.IsType<Parcel>(parcel); | ||
|
||
string parcelId = parcel.Id!; | ||
|
||
// retrieve a parcel asynchronously | ||
var retrievedParcel = await client.Parcel.Retrieve(parcelId); | ||
Assert.NotNull(retrievedParcel); | ||
Assert.IsType<Parcel>(retrievedParcel); | ||
|
||
return new EmptyResult(); | ||
} | ||
|
||
/// <summary> | ||
/// Test that an end-user can run asynchronous code synchronously via TaskFactory | ||
/// Ref: https://gist.github.com/leonardochaia/98ce57bcee39c18d88682424a6ffe305 | ||
/// </summary> | ||
[Fact, Testing.Run] | ||
public ActionResult TestUserCanRunAsyncCodeSynchronouslyViaTaskFactory() | ||
{ | ||
var client = Vcr.SetUpTest("via_task_factory"); | ||
|
||
// create a parcel via TaskFactory (via AsyncHelper) | ||
var parcel = AsyncHelper.RunSync(() => client.Parcel.Create(new Create | ||
{ | ||
Height = 1, | ||
Length = 1, | ||
Width = 1, | ||
Weight = 1, | ||
})); | ||
Assert.NotNull(parcel); | ||
Assert.IsType<Parcel>(parcel); | ||
|
||
string parcelId = parcel.Id!; | ||
|
||
// retrieve a parcel via TaskFactory (via AsyncHelper) | ||
var retrievedParcel = AsyncHelper.RunSync(() => client.Parcel.Retrieve(parcelId)); | ||
Assert.NotNull(retrievedParcel); | ||
Assert.IsType<Parcel>(retrievedParcel); | ||
|
||
return new EmptyResult(); | ||
} | ||
|
||
private static class AsyncHelper | ||
{ | ||
private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); | ||
|
||
public static void RunSync(Func<Task> func) | ||
{ | ||
TaskFactory.StartNew<Task>(func).Unwrap().GetAwaiter().GetResult(); | ||
} | ||
|
||
public static TResult RunSync<TResult>(Func<Task<TResult>> func) | ||
{ | ||
return TaskFactory.StartNew<Task<TResult>>(func).Unwrap<TResult>().GetAwaiter().GetResult(); | ||
} | ||
} | ||
} | ||
#pragma warning restore CA3147 // Mark Verb Handlers With Validate Antiforgery Token |
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,174 @@ | ||
using System.Runtime.CompilerServices; | ||
using EasyVCR; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace EasyPost.Integration.Utilities | ||
{ | ||
public class Utils | ||
{ | ||
internal const string ApiKeyFailedToPull = "couldnotpullapikey"; | ||
|
||
private static readonly List<string> BodyCensors = new() | ||
{ | ||
"api_keys", | ||
"children", | ||
"client_ip", | ||
"credentials", | ||
"email", | ||
"key", | ||
"keys", | ||
"phone_number", | ||
"phone", | ||
"test_credentials" | ||
}; | ||
|
||
private static readonly List<string> HeaderCensors = new() | ||
{ | ||
"Authorization", | ||
"User-Agent" | ||
}; | ||
|
||
private static readonly List<string> QueryCensors = new() | ||
{ | ||
"card[number]", | ||
"card[cvc]" | ||
}; | ||
|
||
public enum ApiKey | ||
{ | ||
Test, | ||
Production, | ||
Partner, | ||
Referral, | ||
Mock, | ||
} | ||
|
||
public static string GetSourceFileDirectory([CallerFilePath] string sourceFilePath = "") => Path.GetDirectoryName(sourceFilePath)!; | ||
|
||
internal static string GetApiKey(ApiKey apiKey) | ||
{ | ||
string keyName = apiKey switch | ||
{ | ||
ApiKey.Test => "EASYPOST_TEST_API_KEY", | ||
ApiKey.Production => "EASYPOST_PROD_API_KEY", | ||
ApiKey.Partner => "PARTNER_USER_PROD_API_KEY", | ||
ApiKey.Referral => "REFERRAL_CUSTOMER_PROD_API_KEY", | ||
ApiKey.Mock => "EASYPOST_MOCK_API_KEY", // does not exist, will trigger to use ApiKeyFailedToPull | ||
#pragma warning disable CA2201 | ||
var _ => throw new Exception(Constants.ErrorMessages.InvalidApiKeyType) | ||
#pragma warning restore CA2201 | ||
}; | ||
|
||
return Environment.GetEnvironmentVariable(keyName) ?? ApiKeyFailedToPull; // if can't pull from environment, will use a fake key. Won't matter on replay. | ||
} | ||
|
||
// ReSharper disable once InconsistentNaming | ||
internal static Client GetBasicVCRClient(string apiKey, HttpClient? vcrClient = null) => new(new ClientConfiguration(apiKey) | ||
{ | ||
CustomHttpClient = vcrClient, | ||
}); | ||
|
||
internal static string ReadFile(string path) | ||
{ | ||
string filePath = Path.Combine(GetSourceFileDirectory(), path); | ||
return File.ReadAllText(filePath); | ||
} | ||
|
||
internal static string NetVersion | ||
{ | ||
get | ||
{ | ||
string netVersion = "net"; | ||
#if NET462 | ||
netVersion = "netstandard"; | ||
#endif | ||
|
||
return netVersion; | ||
} | ||
} | ||
|
||
public class VCR | ||
{ | ||
// Cassettes folder will always been in the same directory as this TestUtils.cs file | ||
private const string CassettesFolder = "cassettes"; | ||
|
||
private readonly string _apiKey; | ||
|
||
private readonly string _testCassettesFolder; | ||
|
||
private readonly EasyVCR.VCR _vcr; | ||
|
||
public VCR(string? testCassettesFolder = null, ApiKey apiKey = ApiKey.Test) | ||
{ | ||
Censors censors = new("<REDACTED>"); | ||
censors.CensorHeadersByKeys(HeaderCensors); | ||
censors.CensorQueryParametersByKeys(QueryCensors); | ||
censors.CensorBodyElementsByKeys(BodyCensors); | ||
|
||
AdvancedSettings advancedSettings = new() | ||
{ | ||
MatchRules = MatchRules.DefaultStrict, | ||
Censors = censors, | ||
SimulateDelay = false, | ||
ManualDelay = 0, | ||
ValidTimeFrame = TimeFrame.Months6, | ||
WhenExpired = ExpirationActions.Warn | ||
}; | ||
_vcr = new EasyVCR.VCR(advancedSettings); | ||
|
||
_apiKey = GetApiKey(apiKey); | ||
|
||
_testCassettesFolder = Path.Combine(GetSourceFileDirectory(), CassettesFolder); // create "cassettes" folder in same directory as test files | ||
|
||
string netVersionFolder = NetVersion; | ||
|
||
_testCassettesFolder = Path.Combine(_testCassettesFolder, netVersionFolder); // create .NET version-specific folder in "cassettes" folder | ||
|
||
if (testCassettesFolder != null) | ||
{ | ||
_testCassettesFolder = Path.Combine(_testCassettesFolder, testCassettesFolder); // create test group folder in .NET version-specific folder | ||
} | ||
|
||
// if folder doesn't exist, create it | ||
if (!Directory.Exists(_testCassettesFolder)) | ||
{ | ||
Directory.CreateDirectory(_testCassettesFolder); | ||
} | ||
} | ||
|
||
internal bool IsRecording() => _vcr.Mode == Mode.Record; | ||
|
||
internal Client SetUpTest(string cassetteName, Func<string, HttpClient, Client> getClientFunc, string? overrideApiKey = null) | ||
{ | ||
// override api key if needed | ||
string apiKey = overrideApiKey ?? _apiKey; | ||
|
||
// set up cassette | ||
Cassette cassette = new(_testCassettesFolder, cassetteName, new CassetteOrder.Alphabetical()); | ||
|
||
// add cassette to vcr | ||
_vcr.Insert(cassette); | ||
|
||
string filePath = Path.Combine(_testCassettesFolder, cassetteName + ".json"); | ||
if (!File.Exists(filePath)) | ||
{ | ||
// if cassette doesn't exist, switch to record mode | ||
_vcr.Record(); | ||
} | ||
else | ||
{ | ||
// if cassette exists, switch to replay mode | ||
_vcr.Replay(); | ||
} | ||
|
||
// get EasyPost client | ||
return getClientFunc(apiKey, _vcr.Client); | ||
} | ||
|
||
internal Client SetUpTest(string cassetteName, string? overrideApiKey = null) | ||
{ | ||
return SetUpTest(cassetteName, GetBasicVCRClient, overrideApiKey); | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.