Skip to content

Commit

Permalink
feat: Add single-seller feature, which is mutually exclusive with mul…
Browse files Browse the repository at this point in the history
…tiple-sellers (#640)
  • Loading branch information
lukehesluke authored Mar 11, 2024
1 parent e2f4329 commit 589099d
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 6 deletions.
1 change: 1 addition & 0 deletions config/.example.single-seller-client-credentials.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"integrationTests": {
"implementedFeatures": {
"multiple-sellers": false,
"single-seller": true,
"booking-partner-authentication": false,
"dynamic-client-registration": false,
"business-to-consumer-tax-calculation-net": false,
Expand Down
1 change: 1 addition & 0 deletions config/.example.single-seller.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"integrationTests": {
"implementedFeatures": {
"multiple-sellers": false,
"single-seller": true,
"booking-partner-authentication": false,
"dynamic-client-registration": false,
"business-to-consumer-tax-calculation-net": false,
Expand Down
1 change: 1 addition & 0 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"prepayment-unavailable": true,
"minimal-proposal": true,
"proposal-amendment": true,
"single-seller": false,
"multiple-sellers": true,
"payment-reconciliation-detail-validation": true,
"booking-window": true,
Expand Down
16 changes: 13 additions & 3 deletions packages/openactive-integration-tests/documentation/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ const INDEX_TESTS_IMPLEMENTED_JSON_FILE = path.join(FEATURES_ROOT, 'tests-implem
* }} FeatureMetadataItem
*/

const FEATURES_NOT_REQUIRED_FOR_DEFAULT_JSON = new Set([
/* Not required because the default behaviour allows for both opportunities
which can and can not be cancelled */
'customer-requested-cancellation-always-allowed',
/* change-of-logistics-notification is also temporarily disabled while the
Reference Implementation catches up to its new specification. */
'change-of-logistics-notifications',
// This is not required because it is mutually exclusive with multiple-sellers
'single-seller',
]);

const rootDirectory = path.join(__dirname, '../');

// Stub global config
Expand All @@ -80,9 +91,8 @@ const testMetadata = fg.sync(jestConfig.testMatch, { cwd: rootDirectory }).map(f
// ## Validate the test metadata
const expectedPath = `test/features/${renderFullTestPath(data)}`;
chai.expect(expectedPath, `Expected ${file} to contain metadata matching its path`).to.equal(file);
// All features in default.json should be true except for customer-requested-cancellation-always-allowed
// change-of-logistics-notification is also temporarily disabled while the Reference Implementation catches up to its new specification.
const expectedValue = data.testFeature !== 'customer-requested-cancellation-always-allowed' && data.testFeature !== 'change-of-logistics-notifications';
// All features in default.json should be true, with some exceptions
const expectedValue = !FEATURES_NOT_REQUIRED_FOR_DEFAULT_JSON.has(data.testFeature);
chai.expect(defaultConfig.integrationTests.implementedFeatures, `Expected default.json to contain feature '${data.testFeature} set to "${expectedValue}"'`).to.have.property(data.testFeature).to.equal(expectedValue);
return data;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The tests for these features cover all known edge cases, including both happy an
| cancellation | cancellationMessage for Seller Requested Cancellation ([seller-requested-cancellation-message](./cancellation/seller-requested-cancellation-message/README.md)) | Optional<br>[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#seller-requested-cancellation) | A message associated with a Cancellation triggered by the Seller through the Booking System | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x4 |
| cancellation | Seller Requested Replacement ([seller-requested-replacement](./cancellation/seller-requested-replacement/README.md)) | Optional<br>[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#cancellation-replacement-refund-calculation-and-notification) | Replacement triggered by the Seller through the Booking System | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x4 |
| core | Multiple Sellers ([multiple-sellers](./core/multiple-sellers/README.md)) | Optional<br>[View Spec](https://openactive.io/open-booking-api/EditorsDraft/#booking-pre-conditions) | The booking system is multi-tenanted and provides services to multiple sellers. | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x6 |
| core | Single Seller ([single-seller](./core/single-seller/README.md)) | Optional<br>[View Spec](https://openactive.io/open-booking-api/EditorsDraft/#booking-pre-conditions) | The booking system only supports providing services to one seller. | |
| core | Test interface ([test-interface](./core/test-interface/README.md)) | Optional<br>[View Spec](https://openactive.io/test-interface/) | Open Booking API Test Interface implementation | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x1 |
| details-capture | Additional Details capture ([additional-details-capture](./details-capture/additional-details-capture/README.md)) | Optional<br>[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#additional-details-capture) | Support for capturing additional details with required set to true | [TestOpportunityBookableAdditionalDetails](https://openactive.io/test-interface#TestOpportunityBookableAdditionalDetails) x9, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x3 |
| details-capture | Simple Book including Attendee Details capture ([attendee-details-capture](./details-capture/attendee-details-capture/README.md)) | Optional<br>[View Spec](https://www.openactive.io/open-booking-api/EditorsDraft/#attendee-details-capture) | Support for capturing attendee details | [TestOpportunityBookableAttendeeDetails](https://openactive.io/test-interface#TestOpportunityBookableAttendeeDetails) x6, [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x2 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"order-deletion": true,
"multiple-sellers": true,
"opportunity-feed": true,
"single-seller": true,
"test-interface": true
},
"access": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,20 @@ Update `default.json` within `packages/openactive-integration-tests/config/` as
| [seller-access-restricted-by-auth](./implemented/seller-access-restricted-by-auth-test.js) | Credentials for Seller (a) must not provide access to make bookings for Seller (b) | Using primary seller auth, make a call to C1, C2, and P/B for the secondary seller, expecting all calls to fail with InvalidAuthorizationDetailsError | [TestOpportunityBookable](https://openactive.io/test-interface#TestOpportunityBookable) x4 |



## 'Not Implemented' tests


Update `default.json` within `packages/openactive-integration-tests/config/` as follows to enable 'Not Implemented' testing for this feature:

```json
"implementedFeatures": {
...
"multiple-sellers": false,
...
}
```

| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
| [single-seller-implemented](./not-implemented/single-seller-implemented-test.js) | Single Seller feature must be implemented if Multiple Sellers is not implemented | Either one, and only one, of the Multiple Sellers feature and the Single Seller feature must be implemented | |
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { FeatureHelper } = require('../../../../helpers/feature-helper');

FeatureHelper.describeFeatureShouldBeImplementedIfOtherFeaturesAreNot(module, {
testCategory: 'core',
testFeature: 'multiple-sellers',
testFeatureImplemented: false,
testIdentifier: 'single-seller-implemented',
testName: 'Single Seller feature must be implemented if Multiple Sellers is not implemented',
testDescription: 'Either one, and only one, of the Multiple Sellers feature and the Single Seller feature must be implemented',
otherFeaturesWhichAreMutuallyExclusiveWithThisOne: [
'single-seller',
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[< Return to Overview](../../README.md)
# Single Seller (single-seller)

The booking system only supports providing services to one seller.


https://openactive.io/open-booking-api/EditorsDraft/#booking-pre-conditions

Coverage Status: **complete**



### Running tests for only this feature

```bash
npm start -- --runInBand test/features/core/single-seller/
```



## 'Implemented' tests

Update `default.json` within `packages/openactive-integration-tests/config/` as follows to enable 'Implemented' testing for this feature:

```json
"implementedFeatures": {
...
"single-seller": true,
...
}
```

| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
| [only-primary-seller-configured](./implemented/only-primary-seller-configured-test.js) | Only the primary seller should be configured | If the single-seller feature is implemented, multiple-sellers is not enabled, and so a secondary seller should not be configured. | |



## 'Not Implemented' tests


Update `default.json` within `packages/openactive-integration-tests/config/` as follows to enable 'Not Implemented' testing for this feature:

```json
"implementedFeatures": {
...
"single-seller": false,
...
}
```

| Identifier | Name | Description | Prerequisites per Opportunity Type |
|------------|------|-------------|---------------|
| [multiple-sellers-implemented](./not-implemented/multiple-sellers-implemented-test.js) | Multiple Sellers feature must be implemented if Single Seller is not implemented | Either one, and only one, of the Multiple Sellers feature and Single Seller feature must be implemented | |
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"category": "core",
"identifier": "single-seller",
"name": "Single Seller",
"description": "The booking system only supports providing services to one seller.",
"explainer": "",
"specificationReference": "https://openactive.io/open-booking-api/EditorsDraft/#booking-pre-conditions",
"required": false,
"coverageStatus": "complete"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { expect } = require('chai');
const { FeatureHelper } = require('../../../../helpers/feature-helper');

const { SELLER_CONFIG } = global;

FeatureHelper.describeFeature(module, {
testCategory: 'core',
testFeature: 'single-seller',
testFeatureImplemented: true,
testIdentifier: 'only-primary-seller-configured',
testName: 'Only the primary seller should be configured',
testDescription: 'If the single-seller feature is implemented, multiple-sellers is not enabled, and so a secondary seller should not be configured.',
doesNotUseOpportunitiesMode: true,
}, () => {
describe('Feature', () => {
it('should only be implemented if there is only one `primary` seller configured', () => {
expect(SELLER_CONFIG).to.have.nested.property('primary.@id');
expect(SELLER_CONFIG).to.not.have.nested.property('secondary.@id');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { FeatureHelper } = require('../../../../helpers/feature-helper');

FeatureHelper.describeFeatureShouldBeImplementedIfOtherFeaturesAreNot(module, {
testCategory: 'core',
testFeature: 'single-seller',
testFeatureImplemented: false,
testIdentifier: 'multiple-sellers-implemented',
testName: 'Multiple Sellers feature must be implemented if Single Seller is not implemented',
testDescription: 'Either one, and only one, of the Multiple Sellers feature and Single Seller feature must be implemented',
otherFeaturesWhichAreMutuallyExclusiveWithThisOne: [
'multiple-sellers',
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
}
},
"opportunity-feed": {},
"single-seller": {},
"test-interface": {
"primary": {
"TestOpportunityBookable": 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,16 @@
},
"multiple-sellers": {
"implementedTestFiles": 2,
"notImplementedTestFiles": 0
"notImplementedTestFiles": 1
},
"opportunity-feed": {
"implementedTestFiles": 0,
"notImplementedTestFiles": 0
},
"single-seller": {
"implementedTestFiles": 1,
"notImplementedTestFiles": 1
},
"test-interface": {
"implementedTestFiles": 1,
"notImplementedTestFiles": 0
Expand Down
2 changes: 1 addition & 1 deletion packages/openactive-integration-tests/test/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ declare global {
};
SELLER_CONFIG: {
primary: SellerConfig;
secondary: SellerConfig
secondary?: SellerConfig
};
AUTHENTICATION_FAILURE: boolean;
DYNAMIC_REGISTRATION_FAILURE: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,12 @@ class FeatureHelper {
* set of features are.
*
* @param {NodeModule} documentationModule
* @param {Omit<DescribeFeatureConfiguration, 'testDescription' | 'skipMultiple' | 'doesNotUseOpportunitiesMode'> & {
* @param {Omit<DescribeFeatureConfiguration, 'skipMultiple' | 'doesNotUseOpportunitiesMode'> & {
* otherFeaturesWhichImplyThisOne: string[];
* }} configuration
* - `otherFeaturesWhichImplyThisOne` is an array of feature names. If any
* of these features are implemented, then the feature in focus MUST also
* be implemented
*/
static describeFeatureShouldBeImplementedIfOtherFeaturesAre(documentationModule, configuration) {
const otherFeaturesSummary = configuration.otherFeaturesWhichImplyThisOne.map(f => `'${f}'`).join(' and ');
Expand All @@ -299,6 +302,43 @@ class FeatureHelper {
});
}

/**
* Use this for a `not-implemented` test for a feature that should be
* implemented if another given set of features are NOT. i.e. this feature
* is mutually exclusive with another set of features.
*
* @param {NodeModule} documentationModule
* @param {Omit<DescribeFeatureConfiguration, 'skipMultiple' | 'doesNotUseOpportunitiesMode'> & {
* otherFeaturesWhichAreMutuallyExclusiveWithThisOne: string[];
* }} configuration
* - `otherFeaturesWhichAreMutuallyExclusiveWithThisOne` is an array of
* feature names. If all of these features are not implemented, then
* the feature in focus MUST be implemented.
*/
static describeFeatureShouldBeImplementedIfOtherFeaturesAreNot(documentationModule, configuration) {
const otherFeaturesSummary = configuration.otherFeaturesWhichAreMutuallyExclusiveWithThisOne
.map(f => `'${f}'`)
.join(' and ');
this.describeFeature(documentationModule, {
testDescription: `This feature must be implemented if features: ${otherFeaturesSummary} are NOT implemented`,
skipMultiple: true,
doesNotUseOpportunitiesMode: true,
...configuration,
}, () => {
describe('Feature', () => {
it(`must be implemented if other features: ${otherFeaturesSummary} are NOT`, () => {
expect(IMPLEMENTED_FEATURES).to.include(
Object.fromEntries(
configuration.otherFeaturesWhichAreMutuallyExclusiveWithThisOne.map(
f => [f, true],
),
),
);
});
});
});
}

/**
* Use this for a `not-implemented` test for a feature that should be implemented if a specific flow is implemented.
*
Expand Down

0 comments on commit 589099d

Please sign in to comment.