From ad0c68ca5d3c5c3696547b0873181f9ac8d11aeb Mon Sep 17 00:00:00 2001
From: Nick Evans <2616208+nickevansuk@users.noreply.github.com>
Date: Thu, 17 Aug 2023 16:20:19 +0100
Subject: [PATCH] feat: Idempotency support (#552)
---
.../test/features/README.md | 4 +-
.../implemented/accept-proposal-book-test.js | 1 +
.../customer-reject-proposal-test.js | 3 ++
.../not-accept-proposal-book-test.js | 3 ++
.../seller-reject-proposal-test.js | 3 ++
.../proposal-amendment-book-test.js | 4 ++
.../implemented/amend-c1-and-c2-test.js | 1 +
.../implemented/amend-c1-test.js | 1 +
.../implemented/amend-c2-test.js | 1 +
.../c2-with-different-details-test.js | 1 +
.../test/features/criteria-requirements.json | 6 +--
.../payment/free-opportunities/README.md | 3 +-
.../opportunity-free-idempotency-test.js | 33 +++++++++++++++
...portunity-free-without-checkpoints-test.js | 29 +++++++------
.../payment/non-free-opportunities/README.md | 3 +-
.../opportunity-paid-idempotency-test.js | 36 ++++++++++++++++
...portunity-paid-without-checkpoints-test.js | 29 +++++++------
...culation-gross-without-checkpoints-test.js | 29 +++++++------
.../test/features/tests-implemented.json | 4 +-
.../test/helpers/flow-stages/b.js | 36 +++++++++++++++-
.../helpers/flow-stages/flow-stage-recipes.js | 41 +++++++++++++++----
.../test/helpers/flow-stages/p.js | 23 ++++++++++-
.../test/templates/b-req.js | 7 ++--
.../test/templates/c1-req.js | 4 +-
.../test/templates/c2-req.js | 4 +-
.../test/templates/common.js | 12 +++---
26 files changed, 251 insertions(+), 70 deletions(-)
create mode 100644 packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-idempotency-test.js
create mode 100644 packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-idempotency-test.js
diff --git a/packages/openactive-integration-tests/test/features/README.md b/packages/openactive-integration-tests/test/features/README.md
index f70c85f0ec..0f17792a14 100644
--- a/packages/openactive-integration-tests/test/features/README.md
+++ b/packages/openactive-integration-tests/test/features/README.md
@@ -47,8 +47,8 @@ The tests for these features cover all known edge cases, including both happy an
| notifications | Change of logistics notifications ([change-of-logistics-notifications](./notifications/change-of-logistics-notifications/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#change-of-logistics-notifications) | Notifications for when an opportunity's name, location, or start/end date/time are updated | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x12 |
| notifications | Customer notice notifications ([customer-notice-notifications](./notifications/customer-notice-notifications/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#customer-notice-notifications) | Text notifications broadcast to all registered attendees of an opportunity | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x4 |
| notifications | Opportunity attendance updates ([opportunity-attendance-updates](./notifications/opportunity-attendance-updates/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#opportunity-attendance-updates) | Allowing the broker to recieve updates for when an attendee attends an event | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x8 |
-| payment | Free opportunities ([free-opportunities](./payment/free-opportunities/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#free-opportunities) | The most simple form of booking, for free opportunities. Does not check for leases. | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x10 |
-| payment | Opportunities with a non-zero price ([non-free-opportunities](./payment/non-free-opportunities/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#step-by-step-process-description) | The most simple form of booking with payment. Does not check for leases. | [TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x6, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x2 |
+| payment | Free opportunities ([free-opportunities](./payment/free-opportunities/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#free-opportunities) | The most simple form of booking, for free opportunities. Does not check for leases. | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x14 |
+| payment | Opportunities with a non-zero price ([non-free-opportunities](./payment/non-free-opportunities/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#step-by-step-process-description) | The most simple form of booking with payment. Does not check for leases. | [TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x9, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x3 |
| payment | Payment reconciliation detail validation ([payment-reconciliation-detail-validation](./payment/payment-reconciliation-detail-validation/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#payment-reconciliation-detail-validation) | Booking with valid, invalid, and missing Payment details | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x12, [TestOpportunityBookableUsingPayment](https://openactive.io/test-interface#TestOpportunityBookableUsingPayment) x15, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x5 |
| payment | prepayment optional ([prepayment-optional](./payment/prepayment-optional/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#booking-without-payment) | Support for booking with optional payment | [TestOpportunityBookableNonFreePrepaymentOptional](https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentOptional) x8 |
| payment | prepayment required ([prepayment-required](./payment/prepayment-required/README.md)) | Optional
[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#booking-without-payment) | Support for booking with required payment | [TestOpportunityBookableNonFreePrepaymentRequired](https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentRequired) x8 |
diff --git a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/accept-proposal-book-test.js b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/accept-proposal-book-test.js
index 87466f80a5..297ee52186 100644
--- a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/accept-proposal-book-test.js
+++ b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/accept-proposal-book-test.js
@@ -56,6 +56,7 @@ FeatureHelper.describeFeature(module, {
opportunityFeedExtractResponses: c2.getStage('assertOpportunityCapacityAfterC2').getOutput().opportunityFeedExtractResponses,
orderItems: fetchOpportunities.getOutput().orderItems,
}),
+ paymentIdentifierIfPaid: FlowStageRecipes.createRandomPaymentIdentifierIfPaid(),
});
// ## Set up tests
diff --git a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/customer-reject-proposal-test.js b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/customer-reject-proposal-test.js
index 4f67f6508d..302ad6fb62 100644
--- a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/customer-reject-proposal-test.js
+++ b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/customer-reject-proposal-test.js
@@ -40,6 +40,7 @@ FeatureHelper.describeFeature(module, {
},
(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
const { fetchOpportunities, c1, c2, defaultFlowStageParams } = FlowStageRecipes.initialiseSimpleC1C2Flow(orderItemCriteriaList, logger);
+ const paymentIdentifierIfPaid = FlowStageRecipes.createRandomPaymentIdentifierIfPaid();
const p = new PFlowStage({
...defaultFlowStageParams,
prerequisite: c2.getLastStage(),
@@ -48,6 +49,7 @@ FeatureHelper.describeFeature(module, {
totalPaymentDue: c2.getStage('c2').getOutput().totalPaymentDue,
prepayment: c2.getStage('c2').getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
});
const [customerRejection, orderFeedUpdate] = OrderFeedUpdateFlowStageUtils.wrap({
wrappedStageFn: prerequisite => (new CustomerRejectOrderProposalFlowStage({
@@ -73,6 +75,7 @@ FeatureHelper.describeFeature(module, {
orderProposalVersion: orderFeedUpdate.getOutput().orderProposalVersion,
positionOrderIntakeFormMap: c1.getStage('c1').getOutput().positionOrderIntakeFormMap,
}),
+ paymentIdentifierIfPaid,
},
});
diff --git a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/not-accept-proposal-book-test.js b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/not-accept-proposal-book-test.js
index 283c13333a..3de1205417 100644
--- a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/not-accept-proposal-book-test.js
+++ b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/not-accept-proposal-book-test.js
@@ -34,6 +34,7 @@ FeatureHelper.describeFeature(module, {
},
(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
const { fetchOpportunities, c1, c2, defaultFlowStageParams } = FlowStageRecipes.initialiseSimpleC1C2Flow(orderItemCriteriaList, logger);
+ const paymentIdentifierIfPaid = FlowStageRecipes.createRandomPaymentIdentifierIfPaid();
const p = new PFlowStage({
...defaultFlowStageParams,
prerequisite: c2.getLastStage(),
@@ -42,6 +43,7 @@ FeatureHelper.describeFeature(module, {
totalPaymentDue: c2.getStage('c2').getOutput().totalPaymentDue,
prepayment: c2.getStage('c2').getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
});
const b = FlowStageRecipes.runs.book.simpleBAssertCapacity(p, defaultFlowStageParams, {
isExpectedToSucceed: false,
@@ -55,6 +57,7 @@ FeatureHelper.describeFeature(module, {
orderProposalVersion: p.getOutput().orderProposalVersion,
positionOrderIntakeFormMap: c1.getStage('c1').getOutput().positionOrderIntakeFormMap,
}),
+ paymentIdentifierIfPaid,
},
});
diff --git a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/seller-reject-proposal-test.js b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/seller-reject-proposal-test.js
index cef0033ea4..d4537692c2 100644
--- a/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/seller-reject-proposal-test.js
+++ b/packages/openactive-integration-tests/test/features/approval/minimal-proposal/implemented/seller-reject-proposal-test.js
@@ -40,6 +40,7 @@ FeatureHelper.describeFeature(module, {
},
(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
const { fetchOpportunities, c1, c2, defaultFlowStageParams } = FlowStageRecipes.initialiseSimpleC1C2Flow(orderItemCriteriaList, logger);
+ const paymentIdentifierIfPaid = FlowStageRecipes.createRandomPaymentIdentifierIfPaid();
const p = new PFlowStage({
...defaultFlowStageParams,
prerequisite: c2.getLastStage(),
@@ -48,6 +49,7 @@ FeatureHelper.describeFeature(module, {
totalPaymentDue: c2.getStage('c2').getOutput().totalPaymentDue,
prepayment: c2.getStage('c2').getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
});
const [simulateSellerRejection, orderFeedUpdate] = OrderFeedUpdateFlowStageUtils.wrap({
wrappedStageFn: prerequisite => (new TestInterfaceActionFlowStage({
@@ -79,6 +81,7 @@ FeatureHelper.describeFeature(module, {
orderProposalVersion: orderFeedUpdate.getOutput().orderProposalVersion,
positionOrderIntakeFormMap: c1.getStage('c1').getOutput().positionOrderIntakeFormMap,
}),
+ paymentIdentifierIfPaid,
},
});
diff --git a/packages/openactive-integration-tests/test/features/approval/proposal-amendment/implemented/proposal-amendment-book-test.js b/packages/openactive-integration-tests/test/features/approval/proposal-amendment/implemented/proposal-amendment-book-test.js
index ce7e688c49..1455ef1d4c 100644
--- a/packages/openactive-integration-tests/test/features/approval/proposal-amendment/implemented/proposal-amendment-book-test.js
+++ b/packages/openactive-integration-tests/test/features/approval/proposal-amendment/implemented/proposal-amendment-book-test.js
@@ -41,6 +41,7 @@ FeatureHelper.describeFeature(module, {
(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
// ## Initiate Flow Stages
const { fetchOpportunities, c1, c2, defaultFlowStageParams } = FlowStageRecipes.initialiseSimpleC1C2Flow(orderItemCriteriaList, logger);
+ const paymentIdentifierIfPaid = FlowStageRecipes.createRandomPaymentIdentifierIfPaid();
const p = new PFlowStage({
...defaultFlowStageParams,
prerequisite: c2.getLastStage(),
@@ -49,6 +50,7 @@ FeatureHelper.describeFeature(module, {
totalPaymentDue: c2.getStage('c2').getOutput().totalPaymentDue,
prepayment: c2.getStage('c2').getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
});
const [simulateSellerAmendment, sellerAmendmentOrderFeedUpdate] = OrderFeedUpdateFlowStageUtils.wrap({
// FlowStage that is getting wrapped
@@ -102,6 +104,7 @@ FeatureHelper.describeFeature(module, {
orderProposalVersion: p.getOutput().orderProposalVersion,
prepayment: p.getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
},
});
// Using the new proposal version should fail
@@ -116,6 +119,7 @@ FeatureHelper.describeFeature(module, {
orderProposalVersion: sellerAmendmentOrderFeedUpdate.getOutput().orderProposalVersion,
prepayment: sellerAmendmentOrderFeedUpdate.getOutput().prepayment,
}),
+ paymentIdentifierIfPaid,
},
});
diff --git a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-and-c2-test.js b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-and-c2-test.js
index 5b0d082d82..4a73d17e29 100644
--- a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-and-c2-test.js
+++ b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-and-c2-test.js
@@ -77,6 +77,7 @@ FeatureHelper.describeFeature(module, {
orderItems: secondAttemptFetchOpportunities.getOutput().orderItems,
opportunityFeedExtractResponses: secondAttemptC2.getStage('assertOpportunityCapacityAfterC2').getOutput().opportunityFeedExtractResponses,
}),
+ paymentIdentifierIfPaid: FlowStageRecipes.createRandomPaymentIdentifierIfPaid(),
});
// # Set up Tests
diff --git a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-test.js b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-test.js
index d4614c3fec..ef77bc20cc 100644
--- a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-test.js
+++ b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c1-test.js
@@ -74,6 +74,7 @@ FeatureHelper.describeFeature(module, {
orderItems: secondAttemptFetchOpportunities.getOutput().orderItems,
opportunityFeedExtractResponses: secondAttemptC2.getStage('assertOpportunityCapacityAfterC2').getOutput().opportunityFeedExtractResponses,
}),
+ paymentIdentifierIfPaid: FlowStageRecipes.createRandomPaymentIdentifierIfPaid(),
});
// # Set up Tests
diff --git a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c2-test.js b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c2-test.js
index 084754f002..87b88b6ccd 100644
--- a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c2-test.js
+++ b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/amend-c2-test.js
@@ -69,6 +69,7 @@ FeatureHelper.describeFeature(module, {
orderItems: secondAttemptFetchOpportunities.getOutput().orderItems,
opportunityFeedExtractResponses: secondAttemptC2.getStage('assertOpportunityCapacityAfterC2').getOutput().opportunityFeedExtractResponses,
}),
+ paymentIdentifierIfPaid: FlowStageRecipes.createRandomPaymentIdentifierIfPaid(),
});
// # Set up Tests
diff --git a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/c2-with-different-details-test.js b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/c2-with-different-details-test.js
index 1923d3a73e..b9387e2829 100644
--- a/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/c2-with-different-details-test.js
+++ b/packages/openactive-integration-tests/test/features/core/amending-order-quote/implemented/c2-with-different-details-test.js
@@ -60,6 +60,7 @@ FeatureHelper.describeFeature(module, {
orderItems: secondAttemptFetchOpportunities.getOutput().orderItems,
opportunityFeedExtractResponses: secondAttemptC2.getStage('assertOpportunityCapacityAfterC2').getOutput().opportunityFeedExtractResponses,
}),
+ paymentIdentifierIfPaid: FlowStageRecipes.createRandomPaymentIdentifierIfPaid(),
});
// # Set up Tests
diff --git a/packages/openactive-integration-tests/test/features/criteria-requirements.json b/packages/openactive-integration-tests/test/features/criteria-requirements.json
index db704d5d7b..dbd7fb0534 100644
--- a/packages/openactive-integration-tests/test/features/criteria-requirements.json
+++ b/packages/openactive-integration-tests/test/features/criteria-requirements.json
@@ -194,13 +194,13 @@
},
"free-opportunities": {
"primary": {
- "TestOpportunityBookableFree": 10
+ "TestOpportunityBookableFree": 14
}
},
"non-free-opportunities": {
"primary": {
- "TestOpportunityBookableNonFree": 6,
- "TestOpportunityBookable": 2
+ "TestOpportunityBookableNonFree": 9,
+ "TestOpportunityBookable": 3
}
},
"payment-reconciliation-detail-validation": {
diff --git a/packages/openactive-integration-tests/test/features/payment/free-opportunities/README.md b/packages/openactive-integration-tests/test/features/payment/free-opportunities/README.md
index 8ba9ed0c87..cd263923cd 100644
--- a/packages/openactive-integration-tests/test/features/payment/free-opportunities/README.md
+++ b/packages/openactive-integration-tests/test/features/payment/free-opportunities/README.md
@@ -12,7 +12,7 @@ See also: [.NET Tutorial](https://tutorials.openactive.io/open-booking-sdk/quick
### Test prerequisites
Opportunities that match the following criteria must exist in the booking system (for each configured `bookableOpportunityTypesInScope`) for the configured primary Seller in order to use `useRandomOpportunities: true`. Alternatively the following `testOpportunityCriteria` values must be supported by the [test interface](https://openactive.io/test-interface/) of the booking system for `useRandomOpportunities: false`.
-[TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x10
+[TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x14
### Running tests for only this feature
@@ -37,6 +37,7 @@ Update `default.json` within `packages/openactive-integration-tests/config/` as
| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
+| [opportunity-free-idempotency](./implemented/opportunity-free-idempotency-test.js) | Successful booking of free opportunity with idempotency | Testing idempotency of the B call for free opportunities | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x4 |
| [opportunity-free-must-not-include-prepayment](./implemented/opportunity-free-must-not-include-prepayment-test.js) | Free opportunities must have either a `openBookingPrepayment` value of Unspecified, or have no `openBookingPrepayment` specified | Assert that no opportunities that match criteria 'TestOpportunityBookableFreePrepaymentOptional' or 'TestOpportunityBookableFreePrepaymentRequired' are available in the opportunity feeds. | |
| [opportunity-free](./implemented/opportunity-free-test.js) | Successful booking without payment property | A successful end to end booking without the `payment` property included. | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x2 |
| [opportunity-free-unnecessary-payment-error](./implemented/opportunity-free-unnecessary-payment-error-test.js) | Fail free bookings which include erroneous payment property | C1, C2 and B with payment property: payment property is provided but not expected in the request, so an UnnecessaryPaymentDetailsError must be returned. | [TestOpportunityBookableFree](https://openactive.io/test-interface#TestOpportunityBookableFree) x2 |
diff --git a/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-idempotency-test.js b/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-idempotency-test.js
new file mode 100644
index 0000000000..af2c9f1f7b
--- /dev/null
+++ b/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-idempotency-test.js
@@ -0,0 +1,33 @@
+const { omit } = require('lodash');
+const { FeatureHelper } = require('../../../../helpers/feature-helper');
+const { FlowStageRecipes, FlowStageUtils } = require('../../../../helpers/flow-stages');
+
+FeatureHelper.describeFeature(module, {
+ testCategory: 'payment',
+ testFeature: 'free-opportunities',
+ testFeatureImplemented: true,
+ testIdentifier: 'opportunity-free-idempotency',
+ testName: 'Successful booking of free opportunity with idempotency',
+ testDescription: 'Testing idempotency of the B call for free opportunities',
+ testOpportunityCriteria: 'TestOpportunityBookableFree',
+ // This must also be TestOpportunityBookableFree as the entire Order must be free.
+ controlOpportunityCriteria: 'TestOpportunityBookableFree',
+}, (configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
+ const {
+ fetchOpportunities,
+ bookRecipe,
+ defaultFlowStageParams,
+ bookRecipeArgs,
+ } = FlowStageRecipes.initialiseSimpleC1C2BookFlow(orderItemCriteriaList, logger);
+ const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(
+ orderItemCriteriaList,
+ bookRecipe,
+ defaultFlowStageParams,
+ omit(bookRecipeArgs, ['prerequisite']),
+ );
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(fetchOpportunities);
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(bookRecipe);
+ describe('idempotent repeat B', () => {
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
+ });
+});
diff --git a/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-without-checkpoints-test.js b/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-without-checkpoints-test.js
index 0128e79922..85607dd2a3 100644
--- a/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-without-checkpoints-test.js
+++ b/packages/openactive-integration-tests/test/features/payment/free-opportunities/implemented/opportunity-free-without-checkpoints-test.js
@@ -1,3 +1,4 @@
+const { omit } = require('lodash');
const { FeatureHelper } = require('../../../../helpers/feature-helper');
const { FlowStageRecipes, FlowStageUtils } = require('../../../../helpers/flow-stages');
@@ -12,18 +13,22 @@ FeatureHelper.describeFeature(module, {
testOpportunityCriteria: 'TestOpportunityBookableFree',
// This must also be TestOpportunityBookableFree as the entire Order must be free.
controlOpportunityCriteria: 'TestOpportunityBookableFree',
-}, (configuration, orderItemCriteriaList, featureIsImplemented, logger, opportunityType, bookingFlow) => {
- const { fetchOpportunities, bookRecipe, defaultFlowStageParams, bookRecipeGetFirstStageInput, bookRecipeGetAssertOpportunityCapacityInput } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
- const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(orderItemCriteriaList, bookRecipe, defaultFlowStageParams, {
- getFirstStageInput: bookRecipeGetFirstStageInput,
- getAssertOpportunityCapacityInput: bookRecipeGetAssertOpportunityCapacityInput,
- });
+}, (configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
+ const {
+ fetchOpportunities,
+ bookRecipe,
+ defaultFlowStageParams,
+ bookRecipeArgs,
+ } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
+ const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(
+ orderItemCriteriaList,
+ bookRecipe,
+ defaultFlowStageParams,
+ omit(bookRecipeArgs, ['prerequisite']),
+ );
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(fetchOpportunities);
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(bookRecipe);
- // Remove this condition once https://github.com/openactive/OpenActive.Server.NET/issues/100 is fixed.
- if (bookingFlow === 'OpenBookingApprovalFlow') {
- describe('idempotent repeat B', () => {
- FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
- });
- }
+ describe('idempotent repeat B', () => {
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
+ });
});
diff --git a/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/README.md b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/README.md
index d8333661d5..82623512c3 100644
--- a/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/README.md
+++ b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/README.md
@@ -12,7 +12,7 @@ See also: [.NET Tutorial](https://tutorials.openactive.io/open-booking-sdk/quick
### Test prerequisites
Opportunities that match the following criteria must exist in the booking system (for each configured `bookableOpportunityTypesInScope`) for the configured primary Seller in order to use `useRandomOpportunities: true`. Alternatively the following `testOpportunityCriteria` values must be supported by the [test interface](https://openactive.io/test-interface/) of the booking system for `useRandomOpportunities: false`.
-[TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x6, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x2
+[TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x9, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x3
### Running tests for only this feature
@@ -37,6 +37,7 @@ Update `default.json` within `packages/openactive-integration-tests/config/` as
| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
+| [opportunity-paid-idempotency](./implemented/opportunity-paid-idempotency-test.js) | Successful booking of paid opportunity with idempotency | Testing idempotency of the B call for paid opportunities | [TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x3, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x1 |
| [opportunity-paid](./implemented/opportunity-paid-test.js) | Successful booking with payment property | A successful end to end booking of a non-free opportunity with the `payment` property included if required. | [TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x3, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x1 |
| [opportunity-paid-without-checkpoints](./implemented/opportunity-paid-without-checkpoints-test.js) | Successful booking without Checkpoints | Paid Opportunities should be bookable without using Checkpoints C1 & C2 if 1). tax calculations are not performed by the Booking System and 2). they do not require additional details | [TestOpportunityBookableNonFree](https://openactive.io/test-interface#TestOpportunityBookableNonFree) x3, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x1 |
diff --git a/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-idempotency-test.js b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-idempotency-test.js
new file mode 100644
index 0000000000..b764589220
--- /dev/null
+++ b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-idempotency-test.js
@@ -0,0 +1,36 @@
+const { omit } = require('lodash');
+const { FeatureHelper } = require('../../../../helpers/feature-helper');
+const { FlowStageRecipes, FlowStageUtils } = require('../../../../helpers/flow-stages');
+
+FeatureHelper.describeFeature(module, {
+ testCategory: 'payment',
+ testFeature: 'non-free-opportunities',
+ testFeatureImplemented: true,
+ testIdentifier: 'opportunity-paid-idempotency',
+ testName: 'Successful booking of paid opportunity with idempotency',
+ testDescription: 'Testing idempotency of the B call for paid opportunities',
+ testOpportunityCriteria: 'TestOpportunityBookableNonFree',
+ controlOpportunityCriteria: 'TestOpportunityBookable',
+},
+(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
+ // Initiate Flow Stages
+ const {
+ fetchOpportunities,
+ bookRecipe,
+ defaultFlowStageParams,
+ bookRecipeArgs,
+ } = FlowStageRecipes.initialiseSimpleC1C2BookFlow(orderItemCriteriaList, logger);
+ const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(
+ orderItemCriteriaList,
+ bookRecipe,
+ defaultFlowStageParams,
+ omit(bookRecipeArgs, ['prerequisite']),
+ );
+
+ // Set up tests
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(fetchOpportunities);
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(bookRecipe);
+ describe('idempotent repeat B', () => {
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
+ });
+});
diff --git a/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-without-checkpoints-test.js b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-without-checkpoints-test.js
index 98637eaf35..cc7f063461 100644
--- a/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-without-checkpoints-test.js
+++ b/packages/openactive-integration-tests/test/features/payment/non-free-opportunities/implemented/opportunity-paid-without-checkpoints-test.js
@@ -1,3 +1,4 @@
+const { omit } = require('lodash');
const { FeatureHelper } = require('../../../../helpers/feature-helper');
const { FlowStageRecipes, FlowStageUtils } = require('../../../../helpers/flow-stages');
@@ -19,21 +20,25 @@ FeatureHelper.describeFeature(module, {
runOnlyIf: !IMPLEMENTED_FEATURES['business-to-consumer-tax-calculation-gross']
&& !IMPLEMENTED_FEATURES['business-to-consumer-tax-calculation-net'],
},
-(configuration, orderItemCriteriaList, featureIsImplemented, logger, opportunityType, bookingFlow) => {
+(configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
// Initiate Flow Stages
- const { fetchOpportunities, bookRecipe, defaultFlowStageParams, bookRecipeGetFirstStageInput, bookRecipeGetAssertOpportunityCapacityInput } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
- const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(orderItemCriteriaList, bookRecipe, defaultFlowStageParams, {
- getFirstStageInput: bookRecipeGetFirstStageInput,
- getAssertOpportunityCapacityInput: bookRecipeGetAssertOpportunityCapacityInput,
- });
+ const {
+ fetchOpportunities,
+ bookRecipe,
+ defaultFlowStageParams,
+ bookRecipeArgs,
+ } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
+ const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(
+ orderItemCriteriaList,
+ bookRecipe,
+ defaultFlowStageParams,
+ omit(bookRecipeArgs, ['prerequisite']),
+ );
// Set up tests
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(fetchOpportunities);
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(bookRecipe);
- // Remove this condition once https://github.com/openactive/OpenActive.Server.NET/issues/100 is fixed.
- if (bookingFlow === 'OpenBookingApprovalFlow') {
- describe('idempotent repeat B', () => {
- FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
- });
- }
+ describe('idempotent repeat B', () => {
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
+ });
});
diff --git a/packages/openactive-integration-tests/test/features/tax/business-to-consumer-tax-calculation-gross/implemented/business-to-consumer-tax-calculation-gross-without-checkpoints-test.js b/packages/openactive-integration-tests/test/features/tax/business-to-consumer-tax-calculation-gross/implemented/business-to-consumer-tax-calculation-gross-without-checkpoints-test.js
index 1b17f439de..a88ba4b7b2 100644
--- a/packages/openactive-integration-tests/test/features/tax/business-to-consumer-tax-calculation-gross/implemented/business-to-consumer-tax-calculation-gross-without-checkpoints-test.js
+++ b/packages/openactive-integration-tests/test/features/tax/business-to-consumer-tax-calculation-gross/implemented/business-to-consumer-tax-calculation-gross-without-checkpoints-test.js
@@ -1,3 +1,4 @@
+const { omit } = require('lodash');
const { FeatureHelper } = require('../../../../helpers/feature-helper');
const { FlowStageRecipes, FlowStageUtils } = require('../../../../helpers/flow-stages');
@@ -13,18 +14,22 @@ FeatureHelper.describeFeature(module, {
testOpportunityCriteria: 'TestOpportunityBookableNonFreeTaxGross',
// the simple tests can only work if all OrderItems have the same tax mode
controlOpportunityCriteria: 'TestOpportunityBookableNonFreeTaxGross',
-}, (configuration, orderItemCriteriaList, featureIsImplemented, logger, opportunityType, bookingFlow) => {
- const { fetchOpportunities, bookRecipe, defaultFlowStageParams, bookRecipeGetFirstStageInput, bookRecipeGetAssertOpportunityCapacityInput } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
- const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(orderItemCriteriaList, bookRecipe, defaultFlowStageParams, {
- getFirstStageInput: bookRecipeGetFirstStageInput,
- getAssertOpportunityCapacityInput: bookRecipeGetAssertOpportunityCapacityInput,
- });
+}, (configuration, orderItemCriteriaList, featureIsImplemented, logger) => {
+ const {
+ fetchOpportunities,
+ bookRecipe,
+ defaultFlowStageParams,
+ bookRecipeArgs,
+ } = FlowStageRecipes.initialiseSimpleBookOnlyFlow(orderItemCriteriaList, logger);
+ const idempotentRepeatB = FlowStageRecipes.idempotentRepeatBAfterBook(
+ orderItemCriteriaList,
+ bookRecipe,
+ defaultFlowStageParams,
+ omit(bookRecipeArgs, ['prerequisite']),
+ );
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(fetchOpportunities);
FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(bookRecipe);
- // Remove this condition once https://github.com/openactive/OpenActive.Server.NET/issues/100 is fixed.
- if (bookingFlow === 'OpenBookingApprovalFlow') {
- describe('idempotent repeat B', () => {
- FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
- });
- }
+ describe('idempotent repeat B', () => {
+ FlowStageUtils.describeRunAndCheckIsSuccessfulAndValid(idempotentRepeatB);
+ });
});
diff --git a/packages/openactive-integration-tests/test/features/tests-implemented.json b/packages/openactive-integration-tests/test/features/tests-implemented.json
index 30d40853d8..fb06a6be46 100644
--- a/packages/openactive-integration-tests/test/features/tests-implemented.json
+++ b/packages/openactive-integration-tests/test/features/tests-implemented.json
@@ -162,11 +162,11 @@
"notImplementedTestFiles": 0
},
"free-opportunities": {
- "implementedTestFiles": 5,
+ "implementedTestFiles": 6,
"notImplementedTestFiles": 1
},
"non-free-opportunities": {
- "implementedTestFiles": 2,
+ "implementedTestFiles": 3,
"notImplementedTestFiles": 1
},
"payment-reconciliation-detail-validation": {
diff --git a/packages/openactive-integration-tests/test/helpers/flow-stages/b.js b/packages/openactive-integration-tests/test/helpers/flow-stages/b.js
index 5242867723..d7c8ba2c20 100644
--- a/packages/openactive-integration-tests/test/helpers/flow-stages/b.js
+++ b/packages/openactive-integration-tests/test/helpers/flow-stages/b.js
@@ -37,9 +37,24 @@ const { FlowStageUtils } = require('./flow-stage-utils');
* @param {RequestHelperType} args.requestHelper
* @param {string | null} args.brokerRole
* @param {PositionOrderIntakeFormMap} args.positionOrderIntakeFormMap
+ * @param {string} args.paymentIdentifierIfPaid
* @returns {Promise