diff --git a/package.json b/package.json index b0d905f2b0..9ff6da0be9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "debug-tests": "cd packages/openactive-integration-tests && npm run debug", "certificate-validator": "cd packages/openactive-integration-tests && npm run certificate-validator", "doc-gen": "cd packages/openactive-integration-tests && npm run doc-gen", - "git-add-newly-generated-integration-test-docs": "git add \"packages/openactive-integration-tests/test/**/README.md\" packages/openactive-integration-tests/test/features/criteria-requirements.json", + "git-add-newly-generated-integration-test-docs": "git add \"packages/openactive-integration-tests/test/**/README.md\" packages/openactive-integration-tests/test/features/feature-requirements.json", "gen-interface-criteria-types": "cd packages/test-interface-criteria && npm run gen-types", "git-add-newly-generated-interface-criteria-types": "git add packages/test-interface-criteria/built-types", "test-data-generator": "cd packages/openactive-integration-tests && npm run test-data-generator", diff --git a/packages/openactive-integration-tests/documentation/generator.js b/packages/openactive-integration-tests/documentation/generator.js index 4c5bbc157d..63825f3775 100644 --- a/packages/openactive-integration-tests/documentation/generator.js +++ b/packages/openactive-integration-tests/documentation/generator.js @@ -15,7 +15,7 @@ const { FeatureJsonSchema } = require('./featureJson.js'); const FEATURES_ROOT = path.join(__dirname, '..', 'test', 'features'); const INDEX_README_FILE = path.join(FEATURES_ROOT, 'README.md'); -const INDEX_CRITERIA_REQUIREMENTS_JSON_FILE = path.join(FEATURES_ROOT, 'criteria-requirements.json'); +const INDEX_FEATURE_REQUIREMENTS_JSON_FILE = path.join(FEATURES_ROOT, 'feature-requirements.json'); const INDEX_CATEGORIES_JSON_FILE = path.join(FEATURES_ROOT, 'categories.json'); const INDEX_TESTS_IMPLEMENTED_JSON_FILE = path.join(FEATURES_ROOT, 'tests-implemented.json'); @@ -32,12 +32,15 @@ const INDEX_TESTS_IMPLEMENTED_JSON_FILE = path.join(FEATURES_ROOT, 'tests-implem * * @typedef {{ * _createdByDocumentationGeneratorScript: true, - * criteriaRequirements: { + * features: { * [featureIdentifier: string]: { - * [sellerCriteria in SellerCriteria]?: OpportunityCriteriaRequirementsObj; - * }, - * }, - * }} CriteriaRequirementsJson + * criteriaRequirements: { + * [sellerCriteria in SellerCriteria]?: OpportunityCriteriaRequirementsObj; + * }; + * testInterfaceActionImplementationRequirements: string[]; + * }; + * }; + * }} FeatureRequirementsJson * * @typedef {{ * _createdByDocumentationGeneratorScript: true, @@ -145,8 +148,8 @@ writeFileSetErrorExitCodeButDontThrowIfFails( // This file will be used by the test-data-generator script to help seed random // mode tests. writeFileSetErrorExitCodeButDontThrowIfFails( - INDEX_CRITERIA_REQUIREMENTS_JSON_FILE, - renderCriteraRequirementsJson(featureMetadata), + INDEX_FEATURE_REQUIREMENTS_JSON_FILE, + renderFeatureRequirementsJson(featureMetadata), ); // Save categories information to a machine-readable (JSON) file. @@ -364,21 +367,34 @@ function renderTestInterfaceActionImplementationRequirements(testInterfaceAction /** * @param {FeatureMetadataItem[]} features */ -function renderCriteraRequirementsJson(features) { - /** @type {CriteriaRequirementsJson} */ +function renderFeatureRequirementsJson(features) { + /** @type {FeatureRequirementsJson} */ const obj = { _createdByDocumentationGeneratorScript: true, - criteriaRequirements: Object.fromEntries(features.map(feature => ([ + features: Object.fromEntries(features.map(feature => ([ feature.identifier, - Object.fromEntries(Array.from(feature.sellerCriteriaRequirements).map(([sellerCriteria, tallyByCriteria]) => ([ - sellerCriteria, - Object.fromEntries(tallyByCriteria), - ]))), + { + criteriaRequirements: renderFeatureCriteriaRequirements(feature), + testInterfaceActionImplementationRequirements: [ + ...feature.testInterfaceActionImplementationRequirements, + ], + }, ]))), }; return JSON.stringify(obj, null, 2); } +/** + * @param {FeatureMetadataItem} feature + * @returns {FeatureRequirementsJson['features'][string]['criteriaRequirements']} + */ +function renderFeatureCriteriaRequirements(feature) { + return Object.fromEntries(Array.from(feature.sellerCriteriaRequirements).map(([sellerCriteria, tallyByCriteria]) => ([ + sellerCriteria, + Object.fromEntries(tallyByCriteria), + ]))); +} + /** * @param {FeatureMetadataItem[]} features */ diff --git a/packages/openactive-integration-tests/test-data-generator/.gitignore b/packages/openactive-integration-tests/test-data-generator/.gitignore index 6f6a87c96b..ea1472ec1f 100644 --- a/packages/openactive-integration-tests/test-data-generator/.gitignore +++ b/packages/openactive-integration-tests/test-data-generator/.gitignore @@ -1 +1 @@ -test-data/test-data.json +output/ diff --git a/packages/openactive-integration-tests/test-data-generator/README.md b/packages/openactive-integration-tests/test-data-generator/README.md index 62bbe8c9f9..b0a0b6afef 100644 --- a/packages/openactive-integration-tests/test-data-generator/README.md +++ b/packages/openactive-integration-tests/test-data-generator/README.md @@ -2,9 +2,15 @@ Command line tool to generate a distribution definition of test data based on the configured features. -Test data is outputted to `./test-data/test-data.json` by default. +The data outputted by this tool can be used for the following purposes: -The data outputted by this tool can be used by a script to add sufficient test data into your [Open Booking API](https://openactive.io/open-booking-api/EditorsDraft/) implementation's database for testing when using [random mode](../README.md#userandomopportunities). +- Used by a script to add sufficient test data into your [Open Booking API](https://openactive.io/open-booking-api/EditorsDraft/) implementation's database for testing when using [random mode](../README.md#userandomopportunities). +- Used to determine which [Test Interface Actions](https://openactive.io/test-interface/#actions-endpoint) (e.g. `test:AttendeeAbsentSimulateAction`) are required to be supported by your [Open Booking API](https://openactive.io/open-booking-api/EditorsDraft/) implementation. + +Test data is outputted to the following files by default: + +- `./output/opportunity-test-data.json` ([more info](#opportunity-test-datajson-file-format)) +- `./output/test-interface-actions.json` ([more info](#test-interface-actionsjson-file-format)) ## Usage @@ -13,10 +19,12 @@ npm run test-data-generator # all active features specified in the current confi npm run test-data-generator -- common-error-conditions # only enough data for the common-error-conditions feature -npm run test-data-generator -- --output ./test-data.json # output to ./test-data.json +npm run test-data-generator -- --output-dir ../../../test-data-generator-output # output to ../../../test-data-generator-output ``` -## Test data file format +## `opportunity-test-data.json` file format + +This file can be used by a script to add sufficient test data into your [Open Booking API](https://openactive.io/open-booking-api/EditorsDraft/) implementation's database for testing when using [random mode](../README.md#userandomopportunities). The `item` within the file format is identical to the Controlled Opportunity Creation endpoint defined within the [OpenActive Test Interface](https://openactive.io/test-interface/). This allows an implementation to use the same logic to generate data from the test data file and from the test interface. @@ -71,6 +79,37 @@ The `item` within the file format is identical to the Controlled Opportunity Cre } ``` +## `test-interface-actions.json` file format + +This file can be used to determine which [Test Interface Actions](https://openactive.io/test-interface/#actions-endpoint) (e.g. `test:AttendeeAbsentSimulateAction`) are required to be supported by your [Open Booking API](https://openactive.io/open-booking-api/EditorsDraft/) implementation. + +This example shows that the `test:AccessChannelUpdateSimulateAction` and `test:AccessCodeUpdateSimulateAction` [Test Interface Actions](https://openactive.io/test-interface/#actions-endpoint) need to be implemented. + +```json +{ + "@context": [ + "https://openactive.io/", + "https://openactive.io/test-interface" + ], + "@type": "ItemList", + "numberOfItems": 2, + "itemListElement": [ + { + "@type": "ListItem", + "item": { + "@type": "test:AccessChannelUpdateSimulateAction" + } + }, + { + "@type": "ListItem", + "item": { + "@type": "test:AccessCodeUpdateSimulateAction" + } + } + ] +} +``` + ## Use with reference implementation in development Run the generator: @@ -78,12 +117,12 @@ Run the generator: $ npm run test-data-generator Test data file created containing a distribution with 482 items: -/example/path/test-data/test-data.json +/example/path/output/opportunity-test-data.json ``` Take the output path, and set the environment variable `OpenActiveTestDataFile` in `AppSettings.Development.json`: ```json -"OpenActiveTestDataFile": "/example/path/test-data/test-data.json" +"OpenActiveTestDataFile": "/example/path/output/opportunity-test-data.json" ``` ``` @@ -94,6 +133,6 @@ dotnet run ```bash npm run test-data-generator -export OpenActiveTestDataFile=/example/path/test-data/test-data.json +export OpenActiveTestDataFile=/example/path/output/opportunity-test-data.json dotnet run ``` diff --git a/packages/openactive-integration-tests/test-data-generator/test-data-generator.js b/packages/openactive-integration-tests/test-data-generator/test-data-generator.js index f27d2b6b4a..830c0b485b 100644 --- a/packages/openactive-integration-tests/test-data-generator/test-data-generator.js +++ b/packages/openactive-integration-tests/test-data-generator/test-data-generator.js @@ -14,33 +14,44 @@ const { createTestInterfaceOpportunity } = require('../test/helpers/test-interfa /** * @typedef {import('../documentation/generator').CategoriesJson} CategoriesJson - * @typedef {import('../documentation/generator').CriteriaRequirementsJson} CriteriaRequirementsJson + * @typedef {import('../documentation/generator').FeatureRequirementsJson} FeatureRequirementsJson * @typedef {import('../test/types/TestInterfaceOpportunity').TestInterfaceOpportunity} TestInterfaceOpportunity * @typedef {import('../test/types/OpportunityCriteria').SellerCriteria} SellerCriteria */ /** + * @template TListItem * @typedef {{ * '@context': [ * 'https://openactive.io/', * 'https://openactive.io/test-interface', - * ], - * '@type': 'ItemList', - * numberOfItems: number, - * itemListElement: TestDataListItem[], - * }} TestData + * ]; + * '@type': 'ItemList'; + * numberOfItems: number; + * itemListElement: TListItem[]; + * }} ItemList + */ +/** + * @typedef {ItemList} OutputOpportunityTestData * * @typedef {{ * '@type': 'ListItem', * 'test:numberOfInstancesInDistribution': number, * item: Omit, - * }} TestDataListItem + * }} OutputOpportunityTestDataListItem + * + * @typedef {ItemList<{ + * '@type': 'ListItem', + * item: { + * '@type': string; + * }; + * }>} OutputTestInterfaceActions */ // # Constants - File Paths -const CRITERIA_REQUIREMENTS_JSON_FILE_PATH = path.join(__dirname, '..', 'test', 'features', 'criteria-requirements.json'); +const FEATURE_REQUIREMENTS_JSON_FILE_PATH = path.join(__dirname, '..', 'test', 'features', 'feature-requirements.json'); const CATEGORIES_JSON_FILE_PATH = path.join(__dirname, '..', 'test', 'features', 'categories.json'); -const DEFAULT_OUTPUT_FILE_PATH = path.join(__dirname, 'test-data', 'test-data.json'); +const DEFAULT_OUTPUT_DIR = path.join(__dirname, 'output'); // # Constants - Config const IMPLEMENTED_FEATURES = getConfigVarOrThrow('integrationTests', 'implementedFeatures'); @@ -64,38 +75,59 @@ const argv = yargs(process.argv.slice(2)) // eslint-disable-line prefer-destruct }); }) .options({ - output: { + 'output-dir': { type: 'string', alias: 'o', description: 'Output Directory', - default: DEFAULT_OUTPUT_FILE_PATH, + default: DEFAULT_OUTPUT_DIR, }, }) .argv; -const { output: outputFilePath, 'category-or-feature': categoryOrFeatureUntyped } = argv; +const { 'output-dir': outputDirectory, 'category-or-feature': categoryOrFeatureUntyped } = argv; +const outputOpportunityTestDataFilePath = path.join(outputDirectory, 'opportunity-test-data.json'); +const outputTestInterfaceActionsFilePath = path.join(outputDirectory, 'test-interface-actions.json'); const categoryOrFeature = /** @type {string} */(categoryOrFeatureUntyped); // yargs can only properly type the option args - not the positional ones. Hence the TS coercion here // # Main (async () => { // ## Load Requirements - console.info(`Reading: ${CRITERIA_REQUIREMENTS_JSON_FILE_PATH}`); - const criteriaRequirementsJsonRaw = await fs.readFile(CRITERIA_REQUIREMENTS_JSON_FILE_PATH); - /** @type {CriteriaRequirementsJson} */ - const criteriaRequirementsJson = JSON.parse(criteriaRequirementsJsonRaw.toString()); + console.info(`Reading: ${FEATURE_REQUIREMENTS_JSON_FILE_PATH}`); + const featureRequirementsJsonRaw = await fs.readFile(FEATURE_REQUIREMENTS_JSON_FILE_PATH); + /** @type {FeatureRequirementsJson} */ + const featureRequirementsJson = JSON.parse(featureRequirementsJsonRaw.toString()); + + const featureIdentifiers = await getSelectedFeatureIdentifiers(categoryOrFeature); // ## Tally the requirements from each implemented feature const sellerRequirements = tallySellerCriteriaRequirements( - criteriaRequirementsJson, - (await getSelectedFeatureIdentifiers(categoryOrFeature)), + featureRequirementsJson, + featureIdentifiers, ); // ## Create Test Data + const outputOpportunityTestData = createOutputOpportunityTestData(sellerRequirements); + const outputTestInterfaceActions = createOutputTestInterfaceActions(featureRequirementsJson, featureIdentifiers); + // ## Write Test Data // + // Create the directory if it doesn't exist + await fs.mkdir(outputDirectory, { recursive: true }); + await fs.writeFile(outputOpportunityTestDataFilePath, JSON.stringify(outputOpportunityTestData, null, 2)); + console.log(`FILE SAVED: ${outputOpportunityTestDataFilePath}`); + await fs.writeFile(outputTestInterfaceActionsFilePath, JSON.stringify(outputTestInterfaceActions, null, 2)); + console.log(`FILE SAVED: ${outputTestInterfaceActionsFilePath}`); +})(); + +// # Utils + +/** + * @param {SellerCriteriaRequirements} sellerRequirements + */ +function createOutputOpportunityTestData(sellerRequirements) { // One for each seller x opportunity criteria const harvestStartTimeOverride = DateTime.now().toISO(); - /** @type {TestDataListItem[]} */ + /** @type {OutputOpportunityTestDataListItem[]} */ const itemListElement = []; let numberOfItems = 0; for (const [sellerCriteria, opportunityCriteriaRequirements] of sellerRequirements) { @@ -126,38 +158,76 @@ const categoryOrFeature = /** @type {string} */(categoryOrFeatureUntyped); // ya } } } - /** @type {TestData} */ - const testData = { + /** @type {OutputOpportunityTestData} */ + const outputOpportunityTestData = { '@context': ['https://openactive.io/', 'https://openactive.io/test-interface'], '@type': 'ItemList', numberOfItems, itemListElement, }; - // ## Write Test Data - // - // Create the directory if it doesn't exist - await fs.mkdir(path.dirname(outputFilePath), { recursive: true }); - await fs.writeFile(outputFilePath, JSON.stringify(testData, null, 2)); - console.log(`FILE SAVED: ${outputFilePath}`); -})(); + return outputOpportunityTestData; +} -// # Utils +/** + * @param {FeatureRequirementsJson} featureRequirementsJson + * @param {string[]} featureIdentifiers + * @returns {OutputTestInterfaceActions} + */ +function createOutputTestInterfaceActions(featureRequirementsJson, featureIdentifiers) { + const allActionsSet = Object.entries(featureRequirementsJson.features) + .reduce((acc, [feature, { testInterfaceActionImplementationRequirements }]) => { + // Ignore any features which weren't specified + if (!featureIdentifiers.includes(feature)) { return acc; } + for (const action of testInterfaceActionImplementationRequirements) { + acc.add(action); + } + return acc; + }, /** @type {Set} */(new Set())); + /* If the Booking System supports approval flow, then every test which creates + a successful booking will need to use the + `SellerAcceptOrderProposalSimulateAction` (which is not explicitly annotated + for each test as it depends on the implemented booking flows). + + ! There is a sort-of bug here, which is that + test:SellerAcceptOrderProposalSimulateAction will be outputted even if Test + Data Generator has been called on a category or feature that doesn't include + successful bookings. This is technically incorrect. In the opinion of this + author, it's fine because every Booking System should be able to support + successful bookings (!) */ + if (IMPLEMENTED_BOOKING_FLOWS.includes('OpenBookingApprovalFlow')) { + allActionsSet.add('test:SellerAcceptOrderProposalSimulateAction'); + } + const sortedActions = [...allActionsSet].sort(); + /** @type {OutputTestInterfaceActions['itemListElement']} */ + const listItems = sortedActions.map(action => ({ + '@type': 'ListItem', + item: { + '@type': action, + }, + })); + return { + '@context': ['https://openactive.io/', 'https://openactive.io/test-interface'], + '@type': 'ItemList', + numberOfItems: listItems.length, + itemListElement: listItems, + }; +} /** * Count opportunity requirements for each seller, given the features which are selected and implemented * - * @param {CriteriaRequirementsJson} criteriaRequirementsJson + * @param {FeatureRequirementsJson} featureRequirementsJson * @param {string[]} featureIdentifiers Only generates test data for these feature identifiers. * @returns {SellerCriteriaRequirements} */ -function tallySellerCriteriaRequirements(criteriaRequirementsJson, featureIdentifiers) { +function tallySellerCriteriaRequirements(featureRequirementsJson, featureIdentifiers) { /** * The SellerCriteriaRequirements for every implemented features * @type {SellerCriteriaRequirements[]} */ const sellerCriteriaRequirementMaps = []; for (const featureIdentifier of featureIdentifiers) { - const sellerCriteriaRequirementsObj = criteriaRequirementsJson.criteriaRequirements[featureIdentifier]; + const sellerCriteriaRequirementsObj = featureRequirementsJson.features[featureIdentifier].criteriaRequirements; if (!sellerCriteriaRequirementsObj) { console.log(`WARNING: Missing Criteria Requirements for '${featureIdentifier}'. Try running \`npm run doc-gen\`.`); } @@ -272,12 +342,12 @@ function onlyIncludeImplementedFeatures(featureIdentifiers) { // } /** - * @param {CriteriaRequirementsJson['criteriaRequirements'][string]} sellerCriteriaRequirementsObj + * @param {FeatureRequirementsJson['features'][string]['criteriaRequirements']} sellerCriteriaRequirementsObj */ function getSellerCriteriaRequirementsFromJsonObj(sellerCriteriaRequirementsObj) { // Type casting is necessary here as Object.entries, in TypeScript, does not endow keys with the more // specific key type (https://github.com/microsoft/TypeScript/issues/20322) - /** @type {[SellerCriteria, CriteriaRequirementsJson['criteriaRequirements'][string][SellerCriteria]][]} */ + /** @type {[SellerCriteria, FeatureRequirementsJson['features'][string]['criteriaRequirements'][SellerCriteria]][]} */ const sellerCriteriaRequirementsObjEntries = /** @type {any} */(Object.entries(sellerCriteriaRequirementsObj)); // This is a bit baroque. It's converting the JSON object into Maps (specifically, // our SellerCriteriaRequirements maps). diff --git a/packages/openactive-integration-tests/test-data-generator/test-data/test-data.txt b/packages/openactive-integration-tests/test-data-generator/test-data/test-data.txt deleted file mode 100644 index 130bf419a5..0000000000 --- a/packages/openactive-integration-tests/test-data-generator/test-data/test-data.txt +++ /dev/null @@ -1,1206 +0,0 @@ -{ - "@context": [ - "https://openactive.io/", - "https://openactive.io/test-interface" - ], - "@type": "ItemList", - "numberOfItems": 432, - "itemListElement": [ - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 125, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 125, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNoSpaces", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "maxinclusive": 0 - } - } - ], - "test:testOfferDataShapeExpression": [] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNoSpaces", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "maxinclusive": 0 - } - } - ], - "test:testOfferDataShapeExpression": [] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFree", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "maxinclusive": 0 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Unavailable" - ], - "allowNull": true - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFree", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "maxinclusive": 0 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Unavailable" - ], - "allowNull": true - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentRequired", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Required" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentRequired", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Required" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentOptional", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Optional" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentOptional", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Optional" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentUnavailable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Unavailable" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreePrepaymentUnavailable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "allowlist": [ - "https://openactive.io/Unavailable" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 15, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableUsingPayment", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "blocklist": [ - "https://openactive.io/Unavailable" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 15, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableUsingPayment", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - }, - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingPrepayment", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:RequiredStatusType", - "blocklist": [ - "https://openactive.io/Unavailable" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableWithinValidFromBeforeStartDate", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/validFromBeforeStartDate", - "valueExpr": { - "@type": "test:DateRangeNodeConstraint", - "allowNull": true, - "minDate": "2021-01-08T12:47:43.499Z", - "maxDate": "2021-01-08T12:47:43.500Z" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableWithinValidFromBeforeStartDate", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/validFromBeforeStartDate", - "valueExpr": { - "@type": "test:DateRangeNodeConstraint", - "allowNull": true, - "minDate": "2021-01-08T12:47:43.500Z", - "maxDate": "2021-01-08T12:47:43.500Z" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableOutsideValidFromBeforeStartDate", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/validFromBeforeStartDate", - "valueExpr": { - "@type": "test:DateRangeNodeConstraint", - "minDate": "2021-01-08T12:47:43.500Z" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableOutsideValidFromBeforeStartDate", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/validFromBeforeStartDate", - "valueExpr": { - "@type": "test:DateRangeNodeConstraint", - "minDate": "2021-01-08T12:47:43.500Z" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableCancellable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/latestCancellationBeforeStartDate", - "valueExpr": { - "@type": "test:NullNodeConstraint" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 3, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableCancellable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/latestCancellationBeforeStartDate", - "valueExpr": { - "@type": "test:NullNodeConstraint" - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 4, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFiveSpaces", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2, - "maxinclusive": 5 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 4, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFiveSpaces", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2, - "maxinclusive": 5 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreeTaxNet", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/taxMode", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:TaxMode", - "allowlist": [ - "https://openactive.io/TaxNet" - ] - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreeTaxNet", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/taxMode", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:TaxMode", - "allowlist": [ - "https://openactive.io/TaxNet" - ] - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreeTaxGross", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/taxMode", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:TaxMode", - "allowlist": [ - "https://openactive.io/TaxGross" - ] - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 8, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableNonFreeTaxGross", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/taxMode", - "valueExpr": { - "@type": "test:OptionNodeConstraint", - "datatype": "oa:TaxMode", - "allowlist": [ - "https://openactive.io/TaxGross" - ] - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/price", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 0.01 - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFlowRequirementOnlyApproval", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "includesAll": [ - "https://openactive.io/OpenBookingApproval" - ], - "excludesAll": [ - "https://openactive.io/OpenBookingAttendeeDetails", - "https://openactive.io/OpenBookingIntakeForm", - "https://openactive.io/OpenBookingMessageExchange", - "https://openactive.io/OpenBookingNegotiation" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 9, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/1" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookableFlowRequirementOnlyApproval", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "includesAll": [ - "https://openactive.io/OpenBookingApproval" - ], - "excludesAll": [ - "https://openactive.io/OpenBookingAttendeeDetails", - "https://openactive.io/OpenBookingIntakeForm", - "https://openactive.io/OpenBookingMessageExchange", - "https://openactive.io/OpenBookingNegotiation" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 1, - "item": { - "@type": "ScheduledSession", - "superEvent": { - "@type": "SessionSeries", - "organizer": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/2" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://schema.org/remainingAttendeeCapacity", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - }, - { - "@type": "ListItem", - "test:numberOfInstancesInDistribution": 1, - "item": { - "@type": "Slot", - "facilityUse": { - "@type": "FacilityUse", - "provider": { - "@type": "Organization", - "@id": "https://localhost:5001/api/identifiers/sellers/2" - } - }, - "test:testOpportunityCriteria": "https://openactive.io/test-interface#TestOpportunityBookable", - "test:testOpportunityDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/remainingUses", - "valueExpr": { - "@type": "NumericNodeConstraint", - "mininclusive": 2 - } - } - ], - "test:testOfferDataShapeExpression": [ - { - "@type": "test:TripleConstraint", - "predicate": "https://openactive.io/openBookingFlowRequirement", - "valueExpr": { - "@type": "test:ArrayConstraint", - "datatype": "oa:OpenBookingFlowRequirement", - "excludesAll": [ - "https://openactive.io/OpenBookingApproval" - ] - } - } - ] - } - } - ] -} \ No newline at end of file diff --git a/packages/openactive-integration-tests/test/features/criteria-requirements.json b/packages/openactive-integration-tests/test/features/criteria-requirements.json deleted file mode 100644 index 694baf7883..0000000000 --- a/packages/openactive-integration-tests/test/features/criteria-requirements.json +++ /dev/null @@ -1,277 +0,0 @@ -{ - "_createdByDocumentationGeneratorScript": true, - "criteriaRequirements": { - "agent-broker": { - "primary": { - "TestOpportunityBookable": 8 - } - }, - "amending-order-quote": { - "primary": { - "TestOpportunityBookable": 32 - } - }, - "availability-check": { - "primary": { - "TestOpportunityBookable": 5, - "TestOpportunityBookableNoSpaces": 3 - } - }, - "common-error-conditions": { - "primary": { - "TestOpportunityBookable": 29, - "TestOpportunityBookableInPast": 3 - } - }, - "dataset-site": {}, - "order-deletion": { - "primary": { - "TestOpportunityBookable": 12 - } - }, - "access-channel": { - "primary": { - "TestOpportunityOnlineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-channel-update-notifications": { - "primary": { - "TestOpportunityOnlineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-code": { - "primary": { - "TestOpportunityOfflineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-code-update-notifications": { - "primary": { - "TestOpportunityOfflineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-pass-barcode-seller-provided": { - "primary": { - "TestOpportunityOfflineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-pass-image": { - "primary": { - "TestOpportunityOfflineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "access-pass-update-notifications": { - "primary": { - "TestOpportunityOfflineBookable": 3, - "TestOpportunityBookable": 1 - } - }, - "dynamic-payment": {}, - "offer-overrides": {}, - "minimal-proposal": { - "primary": { - "TestOpportunityBookable": 16 - } - }, - "proposal-amendment": { - "primary": { - "TestOpportunityBookableWithNegotiation": 3, - "TestOpportunityBookable": 1 - } - }, - "booking-partner-authentication": {}, - "dynamic-client-registration": {}, - "no-broker": { - "primary": { - "TestOpportunityBookable": 12 - } - }, - "reseller-broker": { - "primary": { - "TestOpportunityBookable": 12 - } - }, - "reseller-broker-tax-calculation": {}, - "cancellation-window": { - "primary": { - "TestOpportunityBookableCancellableOutsideWindow": 3, - "TestOpportunityBookable": 2, - "TestOpportunityBookableCancellableWithinWindow": 3 - } - }, - "customer-requested-cancellation": { - "primary": { - "TestOpportunityBookableCancellable": 18, - "TestOpportunityBookableNotCancellable": 1, - "TestOpportunityBookable": 4 - } - }, - "customer-requested-cancellation-always-allowed": {}, - "seller-requested-cancellation": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "seller-requested-cancellation-message": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "seller-requested-replacement": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "multiple-sellers": { - "primary": { - "TestOpportunityBookable": 1 - }, - "secondary": { - "TestOpportunityBookable": 5 - } - }, - "opportunity-feed": {}, - "single-seller": {}, - "test-interface": { - "primary": { - "TestOpportunityBookable": 1 - } - }, - "additional-details-capture": { - "primary": { - "TestOpportunityBookableAdditionalDetails": 9, - "TestOpportunityBookable": 3 - } - }, - "attendee-details-capture": { - "primary": { - "TestOpportunityBookableAttendeeDetails": 6, - "TestOpportunityBookable": 2 - } - }, - "customer-details-capture-identifier": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "customer-details-capture-non-essential": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "anonymous-leasing": { - "primary": { - "TestOpportunityBookableFiveSpaces": 2, - "TestOpportunityBookableOneSpace": 2, - "TestOpportunityBookable": 4 - } - }, - "named-leasing": { - "primary": { - "TestOpportunityBookableFiveSpaces": 2, - "TestOpportunityBookableOneSpace": 2, - "TestOpportunityBookable": 4 - } - }, - "change-of-logistics-notifications": { - "primary": { - "TestOpportunityBookable": 12 - } - }, - "customer-notice-notifications": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "opportunity-attendance-updates": { - "primary": { - "TestOpportunityBookable": 8 - } - }, - "free-opportunities": { - "primary": { - "TestOpportunityBookableFree": 14 - } - }, - "non-free-opportunities": { - "primary": { - "TestOpportunityBookableNonFree": 9, - "TestOpportunityBookable": 3 - } - }, - "payment-reconciliation-detail-validation": { - "primary": { - "TestOpportunityBookableFree": 12, - "TestOpportunityBookableUsingPayment": 15, - "TestOpportunityBookable": 5 - } - }, - "prepayment-optional": { - "primary": { - "TestOpportunityBookableNonFreePrepaymentOptional": 8 - } - }, - "prepayment-required": { - "primary": { - "TestOpportunityBookableNonFreePrepaymentRequired": 8 - } - }, - "prepayment-required-unavailable": { - "primary": { - "TestOpportunityBookableNonFreePrepaymentRequired": 1, - "TestOpportunityBookableNonFreePrepaymentUnavailable": 1 - } - }, - "prepayment-unavailable": { - "primary": { - "TestOpportunityBookableNonFreePrepaymentUnavailable": 9, - "TestOpportunityBookableFree": 3 - } - }, - "booking-restrictions": {}, - "booking-window": { - "primary": { - "TestOpportunityBookableWithinValidFromBeforeStartDate": 3, - "TestOpportunityBookable": 2, - "TestOpportunityBookableOutsideValidFromBeforeStartDate": 3 - } - }, - "business-to-business-tax-calculation-gross": { - "primary": { - "TestOpportunityBookableNonFreeTaxGross": 4 - } - }, - "business-to-business-tax-calculation-net": { - "primary": { - "TestOpportunityBookableNonFreeTaxNet": 4 - } - }, - "business-to-consumer-tax-calculation-gross": { - "primary": { - "TestOpportunityBookableNonFreeTaxGross": 8 - } - }, - "business-to-consumer-tax-calculation-net": { - "primary": { - "TestOpportunityBookableNonFreeTaxNet": 4 - } - }, - "terms-of-service-for-booking-system": { - "primary": { - "TestOpportunityBookable": 4 - } - }, - "terms-of-service-for-seller": { - "primary": { - "TestOpportunityBookableSellerTermsOfService": 4 - } - }, - "terms-of-service-with-consent": {}, - "terms-of-service-with-consent-with-date-modified": {} - } -} \ No newline at end of file diff --git a/packages/openactive-integration-tests/test/features/feature-requirements.json b/packages/openactive-integration-tests/test/features/feature-requirements.json new file mode 100644 index 0000000000..d5d53eaa83 --- /dev/null +++ b/packages/openactive-integration-tests/test/features/feature-requirements.json @@ -0,0 +1,479 @@ +{ + "_createdByDocumentationGeneratorScript": true, + "features": { + "agent-broker": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 8 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "amending-order-quote": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 32 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "availability-check": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 5, + "TestOpportunityBookableNoSpaces": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "common-error-conditions": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 29, + "TestOpportunityBookableInPast": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "dataset-site": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "order-deletion": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 12 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:SellerRequestedCancellationSimulateAction" + ] + }, + "access-channel": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOnlineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "access-channel-update-notifications": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOnlineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:AccessChannelUpdateSimulateAction" + ] + }, + "access-code": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOfflineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "access-code-update-notifications": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOfflineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:AccessCodeUpdateSimulateAction" + ] + }, + "access-pass-barcode-seller-provided": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOfflineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "access-pass-image": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOfflineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "access-pass-update-notifications": { + "criteriaRequirements": { + "primary": { + "TestOpportunityOfflineBookable": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:AccessPassUpdateSimulateAction" + ] + }, + "dynamic-payment": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "offer-overrides": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "minimal-proposal": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 16 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:SellerRejectOrderProposalSimulateAction" + ] + }, + "proposal-amendment": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableWithNegotiation": 3, + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:SellerAmendOrderProposalSimulateAction", + "test:SellerAcceptOrderProposalSimulateAction" + ] + }, + "booking-partner-authentication": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "dynamic-client-registration": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "no-broker": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 12 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "reseller-broker": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 12 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "reseller-broker-tax-calculation": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "cancellation-window": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableCancellableOutsideWindow": 3, + "TestOpportunityBookable": 2, + "TestOpportunityBookableCancellableWithinWindow": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "customer-requested-cancellation": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableCancellable": 18, + "TestOpportunityBookableNotCancellable": 1, + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "customer-requested-cancellation-always-allowed": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "seller-requested-cancellation": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:SellerRequestedCancellationSimulateAction" + ] + }, + "seller-requested-cancellation-message": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:SellerRequestedCancellationWithMessageSimulateAction" + ] + }, + "seller-requested-replacement": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:ReplacementSimulateAction" + ] + }, + "multiple-sellers": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 1 + }, + "secondary": { + "TestOpportunityBookable": 5 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "opportunity-feed": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "single-seller": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "test-interface": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "additional-details-capture": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableAdditionalDetails": 9, + "TestOpportunityBookable": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "attendee-details-capture": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableAttendeeDetails": 6, + "TestOpportunityBookable": 2 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "customer-details-capture-identifier": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "customer-details-capture-non-essential": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "anonymous-leasing": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableFiveSpaces": 2, + "TestOpportunityBookableOneSpace": 2, + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "named-leasing": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableFiveSpaces": 2, + "TestOpportunityBookableOneSpace": 2, + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "change-of-logistics-notifications": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 12 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:ChangeOfLogisticsLocationSimulateAction", + "test:ChangeOfLogisticsNameSimulateAction", + "test:ChangeOfLogisticsTimeSimulateAction" + ] + }, + "customer-notice-notifications": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:CustomerNoticeSimulateAction" + ] + }, + "opportunity-attendance-updates": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 8 + } + }, + "testInterfaceActionImplementationRequirements": [ + "test:AttendeeAbsentSimulateAction", + "test:AttendeeAttendedSimulateAction" + ] + }, + "free-opportunities": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableFree": 14 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "non-free-opportunities": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFree": 9, + "TestOpportunityBookable": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "payment-reconciliation-detail-validation": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableFree": 12, + "TestOpportunityBookableUsingPayment": 15, + "TestOpportunityBookable": 5 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "prepayment-optional": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreePrepaymentOptional": 8 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "prepayment-required": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreePrepaymentRequired": 8 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "prepayment-required-unavailable": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreePrepaymentRequired": 1, + "TestOpportunityBookableNonFreePrepaymentUnavailable": 1 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "prepayment-unavailable": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreePrepaymentUnavailable": 9, + "TestOpportunityBookableFree": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "booking-restrictions": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "booking-window": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableWithinValidFromBeforeStartDate": 3, + "TestOpportunityBookable": 2, + "TestOpportunityBookableOutsideValidFromBeforeStartDate": 3 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "business-to-business-tax-calculation-gross": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreeTaxGross": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "business-to-business-tax-calculation-net": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreeTaxNet": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "business-to-consumer-tax-calculation-gross": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreeTaxGross": 8 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "business-to-consumer-tax-calculation-net": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableNonFreeTaxNet": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "terms-of-service-for-booking-system": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookable": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "terms-of-service-for-seller": { + "criteriaRequirements": { + "primary": { + "TestOpportunityBookableSellerTermsOfService": 4 + } + }, + "testInterfaceActionImplementationRequirements": [] + }, + "terms-of-service-with-consent": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + }, + "terms-of-service-with-consent-with-date-modified": { + "criteriaRequirements": {}, + "testInterfaceActionImplementationRequirements": [] + } + } +} \ No newline at end of file