Skip to content

Commit

Permalink
[chore] Support new UPS carrier account create/update endpoint (#574)
Browse files Browse the repository at this point in the history
- Consolidate dictionary serialization process for easier re-usability
- Add UPS-specific carrier account creation parameters
- Mark legacy (dictionary) carrier account creation function as deprecated (does not support FedEx and UPS registrations)
- Introduce new error type raised if invalid function called (guard clause for legacy carrier account creation function)
- Add and update unit tests for carrier account creation + FedEx and UPS-specific tests
- Add support for UPS carrier account update deviating flow
- New parameter sets for UPS update
- Add and update unit tests for carrier account updates
- Add/re-record cassettes as needed
- Add init-examples-submodule Make step
- Update examples submodule
  • Loading branch information
nwithan8 authored Jul 15, 2024
1 parent a27ef94 commit 7a6b3b7
Show file tree
Hide file tree
Showing 43 changed files with 1,800 additions and 727 deletions.
16 changes: 14 additions & 2 deletions EasyPost.Tests/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ internal static Dictionary<string, object> BasicPickup

const string pickupDate = "2024-04-08";

fixture!.AddOrUpdate("min_datetime", pickupDate);
fixture!.AddOrUpdate("max_datetime", pickupDate);
fixture!.AddOrUpdate(pickupDate, "min_datetime");
fixture!.AddOrUpdate(pickupDate, "max_datetime");

return fixture;
}
Expand Down Expand Up @@ -258,6 +258,18 @@ internal static ParameterSets.CarrierAccount.CreateFedEx CreateFedEx(Dictionary<
ShippingAddressStreet = "345 California St",
};
}

internal static ParameterSets.CarrierAccount.CreateUps CreateUps(Dictionary<string, object>? fixture)
{
fixture ??= new Dictionary<string, object>();

return new ParameterSets.CarrierAccount.CreateUps
{
Description = fixture.GetOrNull<string>("description"),
Reference = fixture.GetOrNull<string>("reference"),
AccountNumber = "123456789",
};
}
}

internal static class CustomsInfo
Expand Down
51 changes: 40 additions & 11 deletions EasyPost.Tests/ServicesTests/CarrierAccountServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net;
using System.Threading.Tasks;
using EasyPost.Exceptions.API;
using EasyPost.Exceptions.General;
using EasyPost.Http;
using EasyPost.Models.API;
using EasyPost.Tests._Utilities;
Expand Down Expand Up @@ -56,24 +57,20 @@ public async Task TestCreateWithCustomWorkflow()
{
UseVCR("create_with_custom_workflow");

// Carriers like FedEx and UPS should hit the `/carrier_accounts/register` endpoint
// FedEx or UPS should trigger a function error since not supported by legacy parameter method
try
{
Dictionary<string, object> parameters = Fixtures.BasicCarrierAccount;
parameters["type"] = "FedexAccount";
parameters["type"] = CarrierAccountType.FedEx.Name;
parameters["registration_data"] = new Dictionary<string, object>();

CarrierAccount carrierAccount = await Client.CarrierAccount.Create(parameters);
CleanUpAfterTest(carrierAccount.Id);
}
catch (InvalidRequestError e)
catch (InvalidFunctionError e)
{
// the data we're sending is invalid, we want to check that the API error is because of malformed data and not due to the endpoint
Assert.Equal(422, e.StatusCode); // 422 is fine. We don't want a 404 not found
Assert.NotNull(e.Errors);
Assert.Contains(e.Errors, error => error is { Field: "account_number", Message: "must be present and a string" });

// Check the cassette to make sure the endpoint is correct (it should be carrier_accounts/register)
// Function should have been halted due to incompatible carrier account type
Assert.NotNull(e);
}
}

Expand Down Expand Up @@ -128,6 +125,39 @@ public async Task TestUpdate()
Assert.Equal(testDescription, carrierAccount.Description);
}

[Fact]
[CrudOperations.Create]
[Testing.Parameters]
public async Task TestUpdateWithCustomWorkflow()
{
UseMockClient(new List<TestUtils.MockRequest>
{
// Fake retrieving an existing UPS account
new(
new TestUtils.MockRequestMatchRules(Method.Get, @"v2\/carrier_accounts\/ca_123$"),
new TestUtils.MockRequestResponseInfo(HttpStatusCode.OK, data: new CarrierAccount
{
Id = "ca_123",
Type = CarrierAccountType.Ups.Name,
}
)
)
});

// UPS should trigger a function error since not supported by legacy parameter method
try
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
CarrierAccount carrierAccount = await Client.CarrierAccount.Update("ca_123", parameters);
Assert.Fail("Expected an exception to be thrown");
}
catch (InvalidFunctionError e)
{
// Function should have been halted due to incompatible carrier account type
Assert.NotNull(e);
}
}

[Fact]
[CrudOperations.Delete]
[Testing.Function]
Expand Down Expand Up @@ -203,8 +233,7 @@ public async Task TestCarrierFieldsJsonSerialization()
Credentials = new Dictionary<string, CarrierField>
{
{
"account_number",
new CarrierField
"account_number", new CarrierField
{
Visibility = "visible",
Label = "DHL Account Number",
Expand Down
122 changes: 109 additions & 13 deletions EasyPost.Tests/ServicesTests/WithParameters/CarrierAccountServiceTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using EasyPost.Exceptions.API;
using EasyPost.Exceptions.General;
using EasyPost.Http;
using EasyPost.Models.API;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
Expand Down Expand Up @@ -53,18 +55,18 @@ public async Task TestCreate()
[Fact]
[CrudOperations.Create]
[Testing.Parameters]
public async Task TestCreateWithCustomWorkflow()
public async Task TestCreateFedEx()
{
UseVCR("create_with_custom_workflow");
UseVCR("create_fedex");

// Carriers like FedEx and UPS should hit the `/carrier_accounts/register` endpoint
// FedEx should hit the `/carrier_accounts/register` endpoint
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

Parameters.CarrierAccount.CreateFedEx parameters = Fixtures.Parameters.CarrierAccounts.CreateFedEx(data);

try
{
// confirms we can pass in CreateFedEx and CreateUps parameters to the same Create method because they are children of the generic Create class
// confirms we can pass in CreateFedEx parameters to the same Create method because they are children of the generic Create class
CarrierAccount carrierAccount = await Client.CarrierAccount.Create(parameters);
CleanUpAfterTest(carrierAccount.Id);
}
Expand All @@ -81,24 +83,52 @@ public async Task TestCreateWithCustomWorkflow()
}
}

[Fact]
[CrudOperations.Create]
[Testing.Parameters]
public async Task TestCreateUps()
{
UseVCR("create_ups");

// UPS should hit the `/ups_oauth_registrations` endpoint
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

Parameters.CarrierAccount.CreateUps parameters = Fixtures.Parameters.CarrierAccounts.CreateUps(data);

// confirms we can pass in CreateUps parameters to the same Create method because they are children of the generic Create class
CarrierAccount carrierAccount = await Client.CarrierAccount.Create(parameters);
CleanUpAfterTest(carrierAccount.Id);

Assert.IsType<CarrierAccount>(carrierAccount);
Assert.StartsWith("ca_", carrierAccount.Id);
}

[Fact]
[CrudOperations.Create]
[Testing.Exception]
public async Task TestPreventUsersUsingGenericParameterSetWithCustomWorkflow()
public async Task TestPreventUsersUsingGenericParameterSetWithCustomCreateWorkflow()
{
UseVCR("prevent_users_using_generic_parameter_set_with_custom_workflow");
UseVCR("prevent_users_using_generic_parameter_set_with_custom_create_workflow");

// Generic Create parameter set configured for DHL
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

// Override the type to be a custom type
data["type"] = CarrierAccountType.FedEx.Name;
data["registration_data"] = new Dictionary<string, object>();
// Generic Create parameter set configured for DHL
Parameters.CarrierAccount.Create standardParameters = Fixtures.Parameters.CarrierAccounts.Create(data);

Parameters.CarrierAccount.Create parameters = Fixtures.Parameters.CarrierAccounts.Create(data);
// Override the type to be a custom type
standardParameters.Type = CarrierAccountType.FedEx.Name;

// should raise an exception because we're using a generic Create set with a custom workflow type (FedExAccount)
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Create(parameters));
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Create(standardParameters));

// Specialized CreateFedEx parameter set configured for FedEx
Parameters.CarrierAccount.CreateFedEx fedExParameters = Fixtures.Parameters.CarrierAccounts.CreateFedEx(data);

// Override the type to be a standard type
fedExParameters.Type = "DhlExpressAccount";

// should raise an exception because we're using a FedEx-specific Create set with a standard workflow type (DhlExpressAccount)
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Create(fedExParameters));
}

[Fact]
Expand All @@ -112,7 +142,7 @@ public async Task TestUpdate()

Parameters.CarrierAccount.Create createParameters = Fixtures.Parameters.CarrierAccounts.Create(data);

CarrierAccount carrierAccount = await Client.CarrierAccount.Create(createParameters);
CarrierAccount carrierAccount = await Client.CarrierAccount.Create(createParameters); // DHL Express
CleanUpAfterTest(carrierAccount.Id);

const string testDescription = "my custom description";
Expand All @@ -129,6 +159,72 @@ public async Task TestUpdate()
Assert.Equal(testDescription, carrierAccount.Description);
}

[Fact]
[CrudOperations.Update]
[Testing.Parameters]
public async Task TestUpdateUps()
{
UseVCR("update_ups");

Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

Parameters.CarrierAccount.CreateUps createParameters = Fixtures.Parameters.CarrierAccounts.CreateUps(data);

CarrierAccount carrierAccount = await Client.CarrierAccount.Create(createParameters);
CleanUpAfterTest(carrierAccount.Id);

const string testDescription = "my custom description";

Parameters.CarrierAccount.UpdateUps updateParameters = new()
{
Description = testDescription,
};

carrierAccount = await Client.CarrierAccount.Update(carrierAccount.Id, updateParameters);

Assert.IsType<CarrierAccount>(carrierAccount);
Assert.StartsWith("ca_", carrierAccount.Id);
// Assert.Equal(testDescription, carrierAccount.Description); // TODO: Uncomment when the UPS update endpoint is fixed
}

[Fact]
[CrudOperations.Create]
[Testing.Exception]
public async Task TestPreventUsersUsingGenericParameterSetWithCustomUpdateWorkflow()
{
UseMockClient(new List<TestUtils.MockRequest>
{
// Fake retrieving an existing UPS account
new(
new TestUtils.MockRequestMatchRules(Method.Get, @"v2\/carrier_accounts\/ca_123$"),
new TestUtils.MockRequestResponseInfo(HttpStatusCode.OK, data: new CarrierAccount
{
Id = "ca_123",
Type = CarrierAccountType.Ups.Name,
}
)
),
new(
new TestUtils.MockRequestMatchRules(Method.Get, @"v2\/carrier_accounts\/ca_456$"),
new TestUtils.MockRequestResponseInfo(HttpStatusCode.OK, data: new CarrierAccount
{
Id = "ca_456",
Type = CarrierAccountType.FedEx.Name,
}
)
),
});

Parameters.CarrierAccount.Update genericParameters = new();
Parameters.CarrierAccount.UpdateUps upsParameters = new();

// should raise an exception because we're using a generic Create set with a custom workflow type (UpsAccount)
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Update("ca_123", genericParameters));

// should raise an exception because we're using a UPS-specific Create set with a standard workflow type (FedExAccount)
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Update("ca_456", upsParameters));
}

#endregion

#endregion
Expand Down
Loading

0 comments on commit 7a6b3b7

Please sign in to comment.