Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Consolidate SmartRate functions, add recommended ship date function #566

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

## Next Release

- Add new `SmartRate` service under beta for interacting with the SmartRate API
- New `RecommendShipDateByDeliveryDate` function to recommend a ship date based on a delivery date for each shipment rate
- Updated `EstimateDeliveryDateByShipDate` function to get an estimated delivery date based on a ship date for each shipment rate
- Existing SmartRate-related functions and classes in `Shipment` service marked as deprecated and will be removed in a future release

## v6.4.0 (2024-05-01)

- Add missing parameters for `Order.Create` parameter set
Expand Down
7 changes: 7 additions & 0 deletions EasyPost.Integration/Basics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@
var postageLabel = new PostageLabel();
var predefinedPackage = new PredefinedPackage();
var rate = new Rate();
var rateWithEstimatedDeliveryDate = new RateWithEstimatedDeliveryDate();

Check warning on line 57 in EasyPost.Integration/Basics.cs

View workflow job for this annotation

GitHub Actions / Integration_Tests

'RateWithEstimatedDeliveryDate' is obsolete: 'This class will be removed in a future version and replace with RateWithTimeInTransitDetailsByShipDate.'
var rateWithTimeInTransitDetailsByShipDate = new RateWithTimeInTransitDetailsByShipDate();
var rateWithTimeInTransitDetailsByDeliveryDate = new RateWithTimeInTransitDetailsByDeliveryDate();
var referralCustomer = new ReferralCustomer();
var refund = new Refund();
var report = new Report();
Expand All @@ -70,6 +72,9 @@
var supportedFeature = new SupportedFeature();
var taxIdentifier = new TaxIdentifier();
var timeInTransit = new TimeInTransit();
var timeInTransitDetails = new TimeInTransitDetails();

Check warning on line 75 in EasyPost.Integration/Basics.cs

View workflow job for this annotation

GitHub Actions / Integration_Tests

'TimeInTransitDetails' is obsolete: 'This class will be removed in a future version and replace with TimeInTransitDetailsByShipDate.'
var timeInTransitDetailsByShipDate = new TimeInTransitDetailsByShipDate();
var timeInTransitDetailsByDeliveryDate = new TimeInTransitDetailsByDeliveryDate();
var tracker = new Tracker();
var trackerCollection = new TrackerCollection();
var trackingDetail = new TrackingDetail();
Expand Down Expand Up @@ -139,6 +144,8 @@
var shipmentInsureParameters = new EasyPost.Parameters.Shipment.Insure();
var shipmentRegenerateRatesParameters = new EasyPost.Parameters.Shipment.RegenerateRates();
var shipmentRetrieveEstimatedDeliveryDateParameters = new EasyPost.Parameters.Shipment.RetrieveEstimatedDeliveryDate();
var smartRateEstimateDeliveryDateByShipDateParameters = new EasyPost.Parameters.SmartRate.EstimateDeliveryDateByShipDate();
var smartRateRecommendShipDateByDeliveryDateParameters = new EasyPost.Parameters.SmartRate.RecommendShipDateByDeliveryDate();
var taxIdentifierCreateParameters = new EasyPost.Parameters.TaxIdentifier.Create();
var trackerCreateParameters = new EasyPost.Parameters.Tracker.Create();
var trackerAllParameters = new EasyPost.Parameters.Tracker.All();
Expand Down
4 changes: 3 additions & 1 deletion EasyPost.Tests/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ public static byte[] EventBody

internal static string PickupService => GetFixtureStructure().ServiceNames.Usps.PickupService;

internal static string PlannedShipDate => "2024-04-08";
internal static string PlannedShipDate => "2024-05-30";

internal static string DesiredDeliveryDate => "2024-05-30";

internal static Dictionary<string, object> ReferralCustomer => GetFixtureStructure().Users.Referral;

Expand Down
136 changes: 136 additions & 0 deletions EasyPost.Tests/ServicesTests/Beta/SmartRateServiceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyPost.Exceptions.General;
using EasyPost.Models.API;
using EasyPost.Models.API.Beta;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
using EasyPost.Utilities;
using EasyPost.Utilities.Internal.Attributes;
using Xunit;

namespace EasyPost.Tests.ServicesTests.Beta
{
public class SmartRateServiceTests : UnitTest
{
public SmartRateServiceTests() : base("beta_smartrate_service")
{
}

#region Tests

#region Test CRUD Operations

[Fact]
[CrudOperations.Read]
[Testing.Function]
public async Task TestGetSmartRates()
{
UseVCR("get_smart_rates");

Shipment shipment = await Client.Shipment.Create(Fixtures.BasicShipment);

Assert.NotNull(shipment.Rates);

List<SmartRate> smartRates = await Client.Beta.SmartRate.GetSmartRates(shipment.Id);
SmartRate smartRate = smartRates.First();
// Must compare IDs because one is a Rate object and one is a SmartRate object
Assert.Equal(shipment.Rates[0].Id, smartRate.Id);
Assert.NotNull(smartRate.TimeInTransit.Percentile50);
Assert.NotNull(smartRate.TimeInTransit.Percentile75);
Assert.NotNull(smartRate.TimeInTransit.Percentile85);
Assert.NotNull(smartRate.TimeInTransit.Percentile90);
Assert.NotNull(smartRate.TimeInTransit.Percentile95);
Assert.NotNull(smartRate.TimeInTransit.Percentile97);
Assert.NotNull(smartRate.TimeInTransit.Percentile99);
}

[Fact]
[CrudOperations.Read]
[Testing.Function]
public async Task TestEstimateDeliveryDateByShipDate()
{
UseVCR("estimated_delivery_date_by_ship_date");

Shipment shipment = await Client.Shipment.Create(Fixtures.BasicShipment);

List<RateWithTimeInTransitDetailsByShipDate> ratesWithEstimatedDeliveryDates = await Client.Beta.SmartRate.EstimateDeliveryDateByShipDate(shipment.Id, Fixtures.PlannedShipDate);

foreach (var rate in ratesWithEstimatedDeliveryDates)
{
Assert.NotNull(rate.TimeInTransitDetails);
nwithan8 marked this conversation as resolved.
Show resolved Hide resolved
Assert.NotNull(rate.TimeInTransitDetails.EasyPostEstimatedDeliveryDate);
Assert.NotNull(rate.TimeInTransitDetails.TimeInTransitPercentiles);
Assert.NotNull(rate.TimeInTransitDetails.PlannedShipDate);
}
}

[Fact]
[CrudOperations.Read]
[Testing.Function]
public async Task TestRecommendedShipDateByDeliveryDate()
{
UseVCR("recommended_ship_date_by_delivery_date");

Shipment shipment = await Client.Shipment.Create(Fixtures.BasicShipment);

List<RateWithTimeInTransitDetailsByDeliveryDate> ratesWithEstimatedDeliveryDates = await Client.Beta.SmartRate.RecommendShipDateByDeliveryDate(shipment.Id, Fixtures.DesiredDeliveryDate);

foreach (var rate in ratesWithEstimatedDeliveryDates)
{
Assert.NotNull(rate.TimeInTransitDetails);
Assert.NotNull(rate.TimeInTransitDetails.EasyPostRecommendedShipDate);
Assert.NotNull(rate.TimeInTransitDetails.TimeInTransitPercentiles);
Assert.NotNull(rate.TimeInTransitDetails.DesiredDeliveryDate);
}
}

#endregion

[Fact]
[Testing.Function]
public async Task TestLowestSmartRateFiltering()
{
// Mock rates since these can change from the API and we want to test the local filtering logic, not the API call
// API call is tested in TestGetSmartRates
List<SmartRate> smartRates = new List<SmartRate>
{
new SmartRate
{
Service = "Priority",
Carrier = "USPS",
Rate = 1.00, // this rate is cheaper but doesn't meet the filters
TimeInTransit = new TimeInTransit
{
Percentile90 = 3,
},
},
new SmartRate
{
Service = "First",
Carrier = "USPS",
Rate = 6.07,
TimeInTransit = new TimeInTransit
{
Percentile90 = 2,
},
},
};

// test lowest SmartRate with valid filters
SmartRate lowestSmartRate = Utilities.Rates.GetLowestSmartRate(smartRates, 2, SmartRateAccuracy.Percentile90);
Assert.Equal("First", lowestSmartRate.Service);
Assert.Equal(6.07, lowestSmartRate.Rate);
Assert.Equal("USPS", lowestSmartRate.Carrier);

// test lowest SmartRate with invalid filters (should error due to strict delivery_days)
await Assert.ThrowsAsync<FilteringError>(() => Task.FromResult(Utilities.Rates.GetLowestSmartRate(smartRates, 0, SmartRateAccuracy.Percentile90)));

// test lowest SmartRate with invalid filters (should error due to bad delivery_accuracy)
// this test is not needed in the C# CL because it uses enums for the accuracy (can't pass in an incorrect value)
nwithan8 marked this conversation as resolved.
Show resolved Hide resolved
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyPost.Models.API;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
using EasyPost.Utilities.Internal.Attributes;
using Xunit;

namespace EasyPost.Tests.ServicesTests.WithParameters.Beta
{
public class SmartRateServiceTests : UnitTest
{
public SmartRateServiceTests() : base("beta_smartrate_service_with_parameters")
{
}

#region Tests

#region Test CRUD Operations

[Fact]
[CrudOperations.Read]
[Testing.Function]
public async Task TestEstimateDeliveryDateByShipDate()
{
UseVCR("estimated_delivery_date_by_ship_date");

Shipment shipment = await Client.Shipment.Create(Fixtures.BasicShipment);

Parameters.SmartRate.EstimateDeliveryDateByShipDate estimateDeliveryDateByShipDateParameters = new()
{
PlannedShipDate = Fixtures.PlannedShipDate,
};

List<RateWithTimeInTransitDetailsByShipDate> ratesWithEstimatedDeliveryDates = await Client.Beta.SmartRate.EstimateDeliveryDateByShipDate(shipment.Id, estimateDeliveryDateByShipDateParameters);

foreach (var rate in ratesWithEstimatedDeliveryDates)
{
Assert.NotNull(rate.TimeInTransitDetails);
Assert.NotNull(rate.TimeInTransitDetails.EasyPostEstimatedDeliveryDate);
Assert.NotNull(rate.TimeInTransitDetails.TimeInTransitPercentiles);
Assert.NotNull(rate.TimeInTransitDetails.PlannedShipDate);
}
}

[Fact]
[CrudOperations.Read]
[Testing.Function]
public async Task TestRecommendedShipDateByDeliveryDate()
{
UseVCR("recommended_ship_date_by_delivery_date");

Shipment shipment = await Client.Shipment.Create(Fixtures.BasicShipment);

Parameters.SmartRate.RecommendShipDateByDeliveryDate recommendShipDateByDeliveryDateParameters = new()
{
DesiredDeliveryDate = Fixtures.DesiredDeliveryDate,
};

List<RateWithTimeInTransitDetailsByDeliveryDate> ratesWithEstimatedDeliveryDates = await Client.Beta.SmartRate.RecommendShipDateByDeliveryDate(shipment.Id, recommendShipDateByDeliveryDateParameters);

foreach (var rate in ratesWithEstimatedDeliveryDates)
{
Assert.NotNull(rate.TimeInTransitDetails);
Assert.NotNull(rate.TimeInTransitDetails.EasyPostRecommendedShipDate);
Assert.NotNull(rate.TimeInTransitDetails.TimeInTransitPercentiles);
Assert.NotNull(rate.TimeInTransitDetails.DesiredDeliveryDate);
}
}

#endregion

#endregion
}
}