From a164be41a7ee00435c58eecd38fe4fc37653c010 Mon Sep 17 00:00:00 2001 From: Jared Evans Date: Wed, 14 Jan 2026 12:08:19 +0000 Subject: [PATCH] prettifies everything --- README.md | 29 +- index.js | 4 +- src/Arazzo.js | 781 +++++++++--------- src/ArazzoGenerator.js | 562 ++++++------- src/ArazzoPlugin.js | 327 ++++---- src/ArazzoRunner.js | 85 +- src/DocFactory.js | 28 +- src/Document.js | 207 ++--- src/Input.js | 50 +- src/OpenAPI.js | 211 +++-- test/unit/Arazzo.spec.js | 1262 +++++++++++++++++------------ test/unit/ArazzoGenerator.spec.js | 450 +++++----- test/unit/ArrazoRunner.spec.js | 117 +-- test/unit/arazzoPlugin.spec.js | 313 +++---- 14 files changed, 2368 insertions(+), 2058 deletions(-) diff --git a/README.md b/README.md index 7b53a15..8bea5f7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Document your Serverless Framework API workflows with the [OpenAPI Arazzo Workflow Spec](https://www.openapis.org/arazzo-specification). -This will generate an OpenAPI Arazzo Specification (1.0.1) for your Serverless Framework APIs. This requires the [Serverless OpenAPI Documenter](https://github.com/JaredCE/serverless-openapi-documenter) plugin to be installed and used, as the Arazzo Specification makes use of the generated OpenAPI Document. +This will generate an OpenAPI Arazzo Specification (1.0.1) for your Serverless Framework APIs. This requires the [Serverless OpenAPI Documenter](https://github.com/JaredCE/serverless-openapi-documenter) plugin to be installed and used, as the Arazzo Specification makes use of the generated OpenAPI Document. ## Install @@ -50,6 +50,7 @@ To run a specification, you should look at: ## Generating the Arazzo Specification file To generate an Arazzo Specification, you can call the plugin from the CLI like: + ```bash serverless arazzo generate -f json ``` @@ -78,7 +79,7 @@ custom: See [Configuration via the custom property](#configuration-via-the-custom-property) on how to configure by this method. -If you prefer, you might wish to add Configuration on each Handler. You can also seperate the documentation out into a separate file: +If you prefer, you might wish to add Configuration on each Handler. You can also seperate the documentation out into a separate file: ```yml custom: @@ -121,11 +122,11 @@ custom: #### info -Mostly everything is optional in the `info` object. If you don't provide a `title`, it'll pull it from your service name, if you don't provide a `version`, it'll default to '1'. +Mostly everything is optional in the `info` object. If you don't provide a `title`, it'll pull it from your service name, if you don't provide a `version`, it'll default to '1'. #### sourceDescriptions -This section is optional. It allows you to document any extra OpenAPI or Arazzo Specification files that your workflows and steps may require. If you do not document this section, it will end up with a default of: +This section is optional. It allows you to document any extra OpenAPI or Arazzo Specification files that your workflows and steps may require. If you do not document this section, it will end up with a default of: ``` sourceDescriptions: @@ -136,9 +137,9 @@ sourceDescriptions: That is, that it generates the `name` property from the `title` property of the `info` object (or the one that is generated for you if you omitted the `info` object). -The `url` will be that of a local openapi.json file, this is what the [Serverless OpenAPI Documenter](https://github.com/JaredCE/serverless-openapi-documenter) generates by default. This can be changed by the CLI by providing a source argument with the path to a different OpenAPI file. To make this usable, this should really be an accessible URL, so you should switch the `--source` CLI input to be the final resting place of the specification (an S3 bucket perhaps), for running locally it will be fine to keep it as a local location. +The `url` will be that of a local openapi.json file, this is what the [Serverless OpenAPI Documenter](https://github.com/JaredCE/serverless-openapi-documenter) generates by default. This can be changed by the CLI by providing a source argument with the path to a different OpenAPI file. To make this usable, this should really be an accessible URL, so you should switch the `--source` CLI input to be the final resting place of the specification (an S3 bucket perhaps), for running locally it will be fine to keep it as a local location. -If you do provide this section, then any further additions will be added to that of the default `sourceDescription`. This is useful if you need to incorporate a step or workflow that resides in a different API (perhaps a Login service). +If you do provide this section, then any further additions will be added to that of the default `sourceDescription`. This is useful if you need to incorporate a step or workflow that resides in a different API (perhaps a Login service). #### workflows @@ -158,9 +159,9 @@ workflows: steps: ``` -Workflows comprise of one or many workflow objects, one workflow might be for logging in, another might be for resetting a password. They comprise of steps, where one step might be to call an endpoint to login and the next step would be to call an endpoint that allows a user to change their name. +Workflows comprise of one or many workflow objects, one workflow might be for logging in, another might be for resetting a password. They comprise of steps, where one step might be to call an endpoint to login and the next step would be to call an endpoint that allows a user to change their name. -Each workflow object requires a `workflowId` and a set of `steps`. `workflowId` should conform to the Regex `[A-Za-z0-9_\-]+` and should be unique across the Arazzo Specification. +Each workflow object requires a `workflowId` and a set of `steps`. `workflowId` should conform to the Regex `[A-Za-z0-9_\-]+` and should be unique across the Arazzo Specification. ##### inputs @@ -179,7 +180,7 @@ Each workflow object requires a `workflowId` and a set of `steps`. `workflowId` type: string ``` -The `inputs` here will be used in a login step and can be verified by this JSON Schema. If you are to be usng the runner part of this plugin (see [Running](#running-the-arazzo-specification)), then the inputs should map to the input file (see [Input File](#input-file)). +The `inputs` here will be used in a login step and can be verified by this JSON Schema. If you are to be usng the runner part of this plugin (see [Running](#running-the-arazzo-specification)), then the inputs should map to the input file (see [Input File](#input-file)). #### steps @@ -201,7 +202,7 @@ steps: - condition: ``` -Each step object requires a `stepId` which conforms to the Regex `[A-Za-z0-9_\-]+`. The `operationId` should point to an `operationId` within an OpenAPI document that is registered within the `sourceDescriptions` array. If you are using multiple OpenAPI files within the `sourceDescriptions` array, then you will need to reference the `operationId` via: `$sourceDescriptions..` e.g. +Each step object requires a `stepId` which conforms to the Regex `[A-Za-z0-9_\-]+`. The `operationId` should point to an `operationId` within an OpenAPI document that is registered within the `sourceDescriptions` array. If you are using multiple OpenAPI files within the `sourceDescriptions` array, then you will need to reference the `operationId` via: `$sourceDescriptions..` e.g. ```yml sourceDescriptions: @@ -295,8 +296,8 @@ custom: This remains the same as what is discussed within [Configuration via the custom property](#configuration-via-the-custom-property). -* See [info](#info) for how to document Info -* See [sourceDescriptions](#sourcedescriptions) +- See [info](#info) for how to document Info +- See [sourceDescriptions](#sourcedescriptions) Workflows is where it starts to differ: @@ -321,7 +322,7 @@ custom: parameters: ``` -This still matches up to [workflows](#workflows) (without `steps`). How we document steps, now moves to the handlers: +This still matches up to [workflows](#workflows) (without `steps`). How we document steps, now moves to the handlers: ```yml custom: @@ -375,7 +376,7 @@ Much of this is unchanged from [Steps](#steps), however, we are adding a `stepNu `stepNumber` is the order of which the step should be run, steps start from 1, so the above example would be the first step in the **addPet** workflow. -If you don't provide either a `operationId`, `operationPath` or a `workflowId` for the step, the `operationId` will be set to the name of your function. In the above example, the `operationId` would become **addPet**. +If you don't provide either a `operationId`, `operationPath` or a `workflowId` for the step, the `operationId` will be set to the name of your function. In the above example, the `operationId` would become **addPet**. ## Validator diff --git a/index.js b/index.js index 6b94595..219d1ff 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,3 @@ -const ArazzoPlugin = require('./src/ArazzoPlugin') +const ArazzoPlugin = require("./src/ArazzoPlugin"); -module.exports = ArazzoPlugin +module.exports = ArazzoPlugin; diff --git a/src/Arazzo.js b/src/Arazzo.js index 736e67d..dbbd244 100644 --- a/src/Arazzo.js +++ b/src/Arazzo.js @@ -1,476 +1,503 @@ -'use strict'; +"use strict"; -const { parse, test } = require('@swaggerexpert/arazzo-runtime-expression'); -const jp = require('jsonpath'); -const traverse = require('traverse'); +const { parse, test } = require("@swaggerexpert/arazzo-runtime-expression"); +const jp = require("jsonpath"); +const traverse = require("traverse"); -const path = require('node:path') +const path = require("node:path"); -const Document = require('./Document'); -const docFactory = require('./DocFactory'); +const Document = require("./Document"); +const docFactory = require("./DocFactory"); class Arazzo extends Document { - constructor(url, name, options) { - super(url, name, options); - - this.type = 'arazzo'; - this.outputs = {}; - this.loadedSourceDescriptions = {}; - // this.pathToArazzoSpecification = path.resolve(arazzoPath); - + constructor(url, name, options) { + super(url, name, options); + + this.type = "arazzo"; + this.outputs = {}; + this.loadedSourceDescriptions = {}; + // this.pathToArazzoSpecification = path.resolve(arazzoPath); + } + + setMainArazzo() { + this.filePath = path.resolve(this.url); + } + + async runWorkflows(inputFile) { + this.inputFile = inputFile; + await this.getSourceDescriptions(); + await this.getWorkflows(); + + await this.startWorkflows(); + } + + async startWorkflows(index = 0) { + const continueRunning = await this.runWorkflow(index); + if (continueRunning.noMoreWorkflows === false) { + await this.startWorkflows(index + 1); } - - setMainArazzo() { - this.filePath = path.resolve(this.url); + } + + async runWorkflow(index) { + const workflow = await this.JSONPickerToIndex("workflows", index); + + if (workflow) { + this.logger.notice(`Running Workflow: ${workflow.workflowId}`); + this.inputs = await this.inputFile.getWorkflowInputs( + workflow.workflowId, + workflow.inputs, + ); + this.workflow = workflow; + await this.runSteps(); + return { noMoreWorkflows: false }; + } else { + this.logger.notice(`All workflows have run`); + return { noMoreWorkflows: true }; } + } - async runWorkflows(inputFile) { - this.inputFile = inputFile; - await this.getSourceDescriptions(); - await this.getWorkflows(); + async runSteps(index = 0) { + const contineuRunning = await this.runStep(index); - await this.startWorkflows(); - } - - async startWorkflows(index = 0) { - const continueRunning = await this.runWorkflow(index); - if (continueRunning.noMoreWorkflows === false) { - await this.startWorkflows(index+1); - } + if (contineuRunning.noMoreSteps === false) { + await this.runSteps(index + 1); } + } - async runWorkflow(index) { - const workflow = await this.JSONPickerToIndex('workflows', index); - - if (workflow) { - this.logger.notice(`Running Workflow: ${workflow.workflowId}`); - this.inputs = await this.inputFile.getWorkflowInputs(workflow.workflowId, workflow.inputs); - this.workflow = workflow; - await this.runSteps(); - return {noMoreWorkflows: false}; - } else { - this.logger.notice(`All workflows have run`); - return {noMoreWorkflows: true}; - } - } + async runStep(index) { + const step = this.workflow.steps[index]; + if (step) { + this.step = step; + this.logger.notice(`Running Step: ${this.step.stepId}`); - async runSteps(index = 0) { - const contineuRunning = await this.runStep(index); + await this.loadOperationData(); - if (contineuRunning.noMoreSteps === false) { - await this.runSteps(index+1); - } + if (this.openAPISteps) { + await this.runOpenAPIStep(); + } + return { noMoreSteps: false }; + } else { + this.logger.notice(`All steps in ${this.workflow.workflowId} have run`); + return { noMoreSteps: true }; } + } - async runStep(index) { - const step = this.workflow.steps[index]; - if (step) { - this.step = step; - this.logger.notice(`Running Step: ${this.step.stepId}`); + async runOpenAPIStep() { + this.operations = await this.sourceDescriptionFile.buildOperation( + this.inputs, + this.step, + ); - await this.loadOperationData(); + this.mapInputs(); - if (this.openAPISteps) { - await this.runOpenAPIStep(); - } + await this.runOperation(); + } - return {noMoreSteps: false}; - } else { - this.logger.notice(`All steps in ${this.workflow.workflowId} have run`); - return {noMoreSteps: true}; - } - } + async runOperation(retry = 0, retryAfter = 0) { + const sleep = function (ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + }; - async runOpenAPIStep() { - this.operations = await this.sourceDescriptionFile.buildOperation(this.inputs, this.step); + for (const operation of this.operations) { + let url = operation.url; - this.mapInputs(); + if (operation.queryParams.size) { + url += `?${operation.queryParams}`; + } - await this.runOperation(); - } + const options = { + method: operation.operation, + headers: operation.headers, + }; - async runOperation(retry = 0, retryAfter = 0) { - const sleep = function (ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } + if (operation.data) { + options.body = data; + } - for (const operation of this.operations) { - let url = operation.url; + this.logger.notice( + `Making a ${operation.operation.toUpperCase()} call to ${operation.url}`, + ); - if (operation.queryParams.size) { - url += `?${operation.queryParams}` - } + const response = await fetch(url, options); - const options = { - method: operation.operation, - headers: operation.headers, - } + await this.dealWithResponse(response); + // if (response.ok === false) { + // this.logger.error(`Call to ${operation.operation.toUpperCase()} ${operation.url} failed`); - if (operation.data) { - options.body = data; - } + // if (retry > 0) { + // let retryCount = retry--; + // this.logger.notice(`Making attempt number: ${retryCount}`); + // let retryAfterSeconds = retryAfter; + // if (response.headers.has('retry-after')) { + // retryAfterSeconds = response.headers['retry-after']; + // } - this.logger.notice(`Making a ${operation.operation.toUpperCase()} call to ${operation.url}`); + // if (retryAfterSeconds > 0) { + // await sleep(retryAfterSeconds*1000); + // } - const response = await fetch(url, options); + // await this.runOperation(retryCount, retryAfterSeconds); + // } else { + // throw new Error(`Call to ${operation.operation.toUpperCase()} ${operation.url} failed with a ${response.status}`); + // } + // } - await this.dealWithResponse(response); - // if (response.ok === false) { - // this.logger.error(`Call to ${operation.operation.toUpperCase()} ${operation.url} failed`); + // if (this.step.successCriteria) { + // const hasMatchedSuccessCriteria = await this.determineSuccessCriteria(response); - // if (retry > 0) { - // let retryCount = retry--; - // this.logger.notice(`Making attempt number: ${retryCount}`); - // let retryAfterSeconds = retryAfter; - // if (response.headers.has('retry-after')) { - // retryAfterSeconds = response.headers['retry-after']; - // } + // if (hasMatchedSuccessCriteria) { + // this.logger.success(`Making a ${operation.operation.toUpperCase()} call to ${operation.url} matched all the successCriteria`); + // } + // } - // if (retryAfterSeconds > 0) { - // await sleep(retryAfterSeconds*1000); - // } - - // await this.runOperation(retryCount, retryAfterSeconds); - // } else { - // throw new Error(`Call to ${operation.operation.toUpperCase()} ${operation.url} failed with a ${response.status}`); - // } - // } - - // if (this.step.successCriteria) { - // const hasMatchedSuccessCriteria = await this.determineSuccessCriteria(response); - - // if (hasMatchedSuccessCriteria) { - // this.logger.success(`Making a ${operation.operation.toUpperCase()} call to ${operation.url} matched all the successCriteria`); - // } - // } + // if (this.step.outputs) { - // if (this.step.outputs) { - - // } - } + // } } + } - async dealWithResponse(response) { - if (response.ok === false) { - await this.dealWithFailedResponse(response); - } else { - // const passed = await this.dealWithSuccessfulResponse(response); + async dealWithResponse(response) { + if (response.ok === false) { + await this.dealWithFailedResponse(response); + } else { + // const passed = await this.dealWithSuccessfulResponse(response); - // if (!passed) { - // await this.dealWithFailedResponse(response); - // } + // if (!passed) { + // await this.dealWithFailedResponse(response); + // } - await this.dealWithOutputs(response) - } + await this.dealWithOutputs(response); } - - async dealWithOutputs(response) { - const json = await response.json(); - if (this.step?.outputs) { - const outputs = {} - for (const key in this.step.outputs) { - // console.log(key) - - const isARuntimeValue = this.matchesExpectedRunTimeExpression(this.step.outputs[key], '$response.'); - - if (isARuntimeValue) { - const parseResult = parse(this.step.outputs[key]); - const parts = []; - - parseResult.ast.translate(parts); - for (const result of parts) { - if (result.at(0) === 'source') { - // if (result.at(1) === 'body') { - // outputs[key] = json; - // // console.log(JSON.stringify(json)) - // } - if (result.at(1).startsWith('body')) { - console.log(result.at(1)) - let path; - if (result.at(1) === 'body') { - path = '$' - } else { - const splitArr = result.at(1).split('body#/') - path = `$..${splitArr[1]}`; - } - console.log(path) - const value1 = jp.query(json, path); - console.log(value1); - } - - if (result.at(1).startsWith('header')) { - const headerName = parts[3][1] - outputs[key] = response.headers.get(headerName); - } - } - // console.log(result) - // if (result.at(1).at(0) === 'body' && !result?.at(3)) { - // outputs[key] = json; - // } - - // if (result[1].startsWith('header')) { - // outputs[key] = response.headers[result[3][1]]; - // } - } + } + + async dealWithOutputs(response) { + const json = await response.json(); + if (this.step?.outputs) { + const outputs = {}; + for (const key in this.step.outputs) { + // console.log(key) + + const isARuntimeValue = this.matchesExpectedRunTimeExpression( + this.step.outputs[key], + "$response.", + ); + + if (isARuntimeValue) { + const parseResult = parse(this.step.outputs[key]); + const parts = []; + + parseResult.ast.translate(parts); + for (const result of parts) { + if (result.at(0) === "source") { + // if (result.at(1) === 'body') { + // outputs[key] = json; + // // console.log(JSON.stringify(json)) + // } + if (result.at(1).startsWith("body")) { + console.log(result.at(1)); + let path; + if (result.at(1) === "body") { + path = "$"; + } else { + const splitArr = result.at(1).split("body#/"); + path = `$..${splitArr[1]}`; } + console.log(path); + const value1 = jp.query(json, path); + console.log(value1); + } + + if (result.at(1).startsWith("header")) { + const headerName = parts[3][1]; + outputs[key] = response.headers.get(headerName); + } } + // console.log(result) + // if (result.at(1).at(0) === 'body' && !result?.at(3)) { + // outputs[key] = json; + // } - Object.assign(this.outputs, {[this.step.stepId]: outputs}) + // if (result[1].startsWith('header')) { + // outputs[key] = response.headers[result[3][1]]; + // } + } } + } - console.log(this.outputs); + Object.assign(this.outputs, { [this.step.stepId]: outputs }); } - async dealWithFailedResponse(response) { - if (this.workflow.failureActions) { - - } - - if (this.step.failureActions) { + console.log(this.outputs); + } - } + async dealWithFailedResponse(response) { + if (this.workflow.failureActions) { } - async dealWithSuccessfulResponse(response) { - let successCriteriaPassed = false; - - if (this.step.successCriteria) { - successCriteriaPassed = this.dealWithCriteria(response, this.step.successCriteria); - } - - return successCriteriaPassed; + if (this.step.failureActions) { } + } - safeEval(expression, context) { - try { - const func = new Function('data', `return ${expression}`); - return func(context); - } catch (e) { - console.error('Error evaluating expression:', expression, e); - return false; - } - } + async dealWithSuccessfulResponse(response) { + let successCriteriaPassed = false; - dealWithCriteria(response, criteriaArr) { - let hasPassed = false; - const passes = []; - const failures = []; - for (const successCriteria of criteriaArr) { - if (Object.hasOwn(successCriteria, 'type') === false || successCriteria?.type === 'simple') { - let condition = successCriteria.condition; - for (const key in response) { - const passed = this.safeEval(condition, response); - if (passed) passes.push(true); - } - } else { - if (successCriteria?.type === 'regex') { + if (this.step.successCriteria) { + successCriteriaPassed = this.dealWithCriteria( + response, + this.step.successCriteria, + ); + } - } else { + return successCriteriaPassed; + } - } - } + safeEval(expression, context) { + try { + const func = new Function("data", `return ${expression}`); + return func(context); + } catch (e) { + console.error("Error evaluating expression:", expression, e); + return false; + } + } + + dealWithCriteria(response, criteriaArr) { + let hasPassed = false; + const passes = []; + const failures = []; + for (const successCriteria of criteriaArr) { + if ( + Object.hasOwn(successCriteria, "type") === false || + successCriteria?.type === "simple" + ) { + let condition = successCriteria.condition; + for (const key in response) { + const passed = this.safeEval(condition, response); + if (passed) passes.push(true); } - - if (passes === this.step.successCriteria.length) { - hasPassed = true; + } else { + if (successCriteria?.type === "regex") { + } else { } + } + } - return hasPassed; + if (passes === this.step.successCriteria.length) { + hasPassed = true; } - async determineSuccessCriteria(response) { - let matchesAllSuccessCriteria = false; - const successCriteriaMatches = [] - for (const criterionObject of this.step.successCriteria) { - if (Object.entries(criterionObject).length === 1) { - if (test(criterionObject.condition)) { - const parseResult = parse(criterionObject.condition); - parseResult.ast.translate(parts); + return hasPassed; + } - console.log(parseResult) - } else { - if (criterionObject.condition.startsWith('$statusCode')) { - if (response.status == 200) { - successCriteriaMatches.push(true) - } - } - } - } - } + async determineSuccessCriteria(response) { + let matchesAllSuccessCriteria = false; + const successCriteriaMatches = []; + for (const criterionObject of this.step.successCriteria) { + if (Object.entries(criterionObject).length === 1) { + if (test(criterionObject.condition)) { + const parseResult = parse(criterionObject.condition); + parseResult.ast.translate(parts); - if (successCriteriaMatches.every(value => value === true)) { - matchesAllSuccessCriteria = true; + console.log(parseResult); + } else { + if (criterionObject.condition.startsWith("$statusCode")) { + if (response.status == 200) { + successCriteriaMatches.push(true); + } + } } - - return matchesAllSuccessCriteria; + } } - mapInputs() { - this.mapParameters(); - this.mapRequestBody(); + if (successCriteriaMatches.every((value) => value === true)) { + matchesAllSuccessCriteria = true; } - mapParameters() { - const headers = new Headers(); - const queryParams = new URLSearchParams(); - - for (const param of this.step?.parameters) { - const value = this.parseRunTimeExpression(param.value); + return matchesAllSuccessCriteria; + } - switch(param.in) { - case 'header': - headers.append(param.name, value); - break; + mapInputs() { + this.mapParameters(); + this.mapRequestBody(); + } - case 'path': - for (const operation of this.operations) { - operation.url = operation.url.replace(`{${param.name}}`, value) - } - break; + mapParameters() { + const headers = new Headers(); + const queryParams = new URLSearchParams(); - case 'query': - queryParams.append(param.name, value); - break; - } - } + for (const param of this.step?.parameters) { + const value = this.parseRunTimeExpression(param.value); - for (const operation of this.operations) { - operation.headers = headers; - operation.queryParams = queryParams; - } - } + switch (param.in) { + case "header": + headers.append(param.name, value); + break; - mapRequestBody() { - if (this.step?.requestBody) { - const payload = structuredClone(this.step.requestBody.payload); - traverse(payload).forEach((requestValue) => { - const value = this.parseRunTimeExpression(requestValue); - this.update(value); - }); + case "path": + for (const operation of this.operations) { + operation.url = operation.url.replace(`{${param.name}}`, value); + } + break; - for (const operation of this.operations) { - operation.data = payload; - } - } + case "query": + queryParams.append(param.name, value); + break; + } } - parseRunTimeExpression(expression) { - let value = expression; - if (test(value)) { - const parts = [] - const parseResult = parse(value); - parseResult.ast.translate(parts); - console.log(parts); - for (const part of parts) { - if (part[0] === 'name') { - value = this.inputs[part[1]]; - } - } - } - - return value; + for (const operation of this.operations) { + operation.headers = headers; + operation.queryParams = queryParams; } - - async loadOperationData() { - this.sourceDescription = this.getOperationIdSourceDescription(); - - if (!this.loadedSourceDescriptions[this.sourceDescription.name]) { - this.logger.notice(`Getting Source Description for: ${this.sourceDescription.name}`); - this.sourceDescriptionFile = await docFactory.buildDocument( - this.sourceDescription.type, - this.sourceDescription.url, - this.sourceDescription.name, - {parser: this.parser, logger: this.logger} - ); - Object.assign(this.loadedSourceDescriptions, {[this.sourceDescription.name]: true}); - } - - if (this.isAnOperationId) { - // this.logger.notice(`Getting OperationId: ${this.step.operationId}`); - await this.sourceDescriptionFile.getOperationById(this.step.operationId); + } + + mapRequestBody() { + if (this.step?.requestBody) { + const payload = structuredClone(this.step.requestBody.payload); + traverse(payload).forEach((requestValue) => { + const value = this.parseRunTimeExpression(requestValue); + this.update(value); + }); + + for (const operation of this.operations) { + operation.data = payload; + } + } + } + + parseRunTimeExpression(expression) { + let value = expression; + if (test(value)) { + const parts = []; + const parseResult = parse(value); + parseResult.ast.translate(parts); + console.log(parts); + for (const part of parts) { + if (part[0] === "name") { + value = this.inputs[part[1]]; } + } } - getOperationIdSourceDescription() { - const operationOrWorkflowPointer = this.getOperationType(); + return value; + } + + async loadOperationData() { + this.sourceDescription = this.getOperationIdSourceDescription(); + + if (!this.loadedSourceDescriptions[this.sourceDescription.name]) { + this.logger.notice( + `Getting Source Description for: ${this.sourceDescription.name}`, + ); + this.sourceDescriptionFile = await docFactory.buildDocument( + this.sourceDescription.type, + this.sourceDescription.url, + this.sourceDescription.name, + { parser: this.parser, logger: this.logger }, + ); + Object.assign(this.loadedSourceDescriptions, { + [this.sourceDescription.name]: true, + }); + } - if (this.sourceDescriptions.length === 1) { - return this.sourceDescriptions[0] - } else { - if (this.matchesExpectedRunTimeExpression(operationOrWorkflowPointer, '$sourceDescriptions.')) { - const sourceDescription = this.sourceDescriptions.filter((sourceDescription) => { - if (sourceDescription.name === operationOrWorkflowPointer.split('.')[1]) { - return sourceDescription; - } - }); - if (sourceDescription.length === 1) { - return sourceDescription; - } + if (this.isAnOperationId) { + // this.logger.notice(`Getting OperationId: ${this.step.operationId}`); + await this.sourceDescriptionFile.getOperationById(this.step.operationId); + } + } + + getOperationIdSourceDescription() { + const operationOrWorkflowPointer = this.getOperationType(); + + if (this.sourceDescriptions.length === 1) { + return this.sourceDescriptions[0]; + } else { + if ( + this.matchesExpectedRunTimeExpression( + operationOrWorkflowPointer, + "$sourceDescriptions.", + ) + ) { + const sourceDescription = this.sourceDescriptions.filter( + (sourceDescription) => { + if ( + sourceDescription.name === + operationOrWorkflowPointer.split(".")[1] + ) { + return sourceDescription; } + }, + ); + if (sourceDescription.length === 1) { + return sourceDescription; } - - throw new Error(`No known matching source description for ${this.step.operationId}`); + } } - getOperationType() { - let operationOrWorkflowPointer; - if (this.step.operationId) { - operationOrWorkflowPointer = this.step.operationId; - this.isAnOperationId = true; - this.openAPISteps = true; - } else if (this.step.workflowId) { - operationOrWorkflowPointer = this.step.workflowId; - this.isAWorkflowId = true; - } else { - operationOrWorkflowPointer = this.step.operationPath; - this.isAnOperationPath = true; - this.openAPISteps = true; - } - return operationOrWorkflowPointer; + throw new Error( + `No known matching source description for ${this.step.operationId}`, + ); + } + + getOperationType() { + let operationOrWorkflowPointer; + if (this.step.operationId) { + operationOrWorkflowPointer = this.step.operationId; + this.isAnOperationId = true; + this.openAPISteps = true; + } else if (this.step.workflowId) { + operationOrWorkflowPointer = this.step.workflowId; + this.isAWorkflowId = true; + } else { + operationOrWorkflowPointer = this.step.operationPath; + this.isAnOperationPath = true; + this.openAPISteps = true; } + return operationOrWorkflowPointer; + } - matchesExpectedRunTimeExpression(string, runtimeExpression) { - const result = this.parser.parse(string, { peg$library: true }); - - if (result.peg$success) { - if (result.peg$result[0] === runtimeExpression) { - return true; - } - } + matchesExpectedRunTimeExpression(string, runtimeExpression) { + const result = this.parser.parse(string, { peg$library: true }); - return false; + if (result.peg$success) { + if (result.peg$result[0] === runtimeExpression) { + return true; + } } - async getSourceDescriptions() { - const pipeline = this.JSONPicker('sourceDescriptions', this.filePath); + return false; + } - let sourceDescriptions = []; - for await (const { value } of pipeline) { - sourceDescriptions = value.flat(); - } + async getSourceDescriptions() { + const pipeline = this.JSONPicker("sourceDescriptions", this.filePath); - if (sourceDescriptions.length === 0) { - throw new Error('Missing Source Descriptions'); - } + let sourceDescriptions = []; + for await (const { value } of pipeline) { + sourceDescriptions = value.flat(); + } - this.sourceDescriptions = sourceDescriptions; + if (sourceDescriptions.length === 0) { + throw new Error("Missing Source Descriptions"); } - async getWorkflows() { - const pipeline = this.JSONPicker('workflows', this.filePath); + this.sourceDescriptions = sourceDescriptions; + } - let workflows = []; - for await (const { value } of pipeline) { - workflows = value.flat(); - } + async getWorkflows() { + const pipeline = this.JSONPicker("workflows", this.filePath); - if (workflows.length === 0) { - throw new Error('Missing Workflows'); - } + let workflows = []; + for await (const { value } of pipeline) { + workflows = value.flat(); + } - this.workflows = workflows; + if (workflows.length === 0) { + throw new Error("Missing Workflows"); } + + this.workflows = workflows; + } } module.exports = Arazzo; diff --git a/src/ArazzoGenerator.js b/src/ArazzoGenerator.js index 37c6dbf..7e60e10 100644 --- a/src/ArazzoGenerator.js +++ b/src/ArazzoGenerator.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const Ajv = require('ajv'); +const Ajv = require("ajv"); const { Config, lintFromString, @@ -8,357 +8,367 @@ const { createConfig, } = require("@redocly/openapi-core"); -const path = require('node:path'); +const path = require("node:path"); class ArazzoGenerator { - constructor(arazzoDocumentation, options) { - this.arazzoDocumentation = arazzoDocumentation; - - this.arazzo = { - arazzo: options.arazzo - } - - this.viableArazzoKeys = ['http', 'httpApi']; - - this.sls = options?.sls - this.sourceFile = options?.sourceFile; - - this.ajv = new Ajv(); - - try { - this.logger.verbose( - `Trying to resolve Redocly rules from: ${path.resolve( - "options", - "redocly.json" - )}` - ); - this.REDOCLY_RULES = require(path.resolve("options", "redocly-arazzo.json")); - } catch (err) { - this.REDOCLY_RULES = { - "struct": "error", - "sourceDescriptions-name-unique": "error", - "sourceDescriptions-type": "error", - "stepId-unique": "error", - "workflowId-unique": "error" - }; - } + constructor(arazzoDocumentation, options) { + this.arazzoDocumentation = arazzoDocumentation; + + this.arazzo = { + arazzo: options.arazzo, + }; + + this.viableArazzoKeys = ["http", "httpApi"]; + + this.sls = options?.sls; + this.sourceFile = options?.sourceFile; + + this.ajv = new Ajv(); + + try { + this.logger.verbose( + `Trying to resolve Redocly rules from: ${path.resolve( + "options", + "redocly.json", + )}`, + ); + this.REDOCLY_RULES = require( + path.resolve("options", "redocly-arazzo.json"), + ); + } catch (err) { + this.REDOCLY_RULES = { + struct: "error", + "sourceDescriptions-name-unique": "error", + "sourceDescriptions-type": "error", + "stepId-unique": "error", + "workflowId-unique": "error", + }; } + } - parse() { - this.generateInfo(); - this.generateSourceDescriptions(); - this.generateWorkflows(); - } - - generateInfo() { - const info = { - title: this.arazzoDocumentation?.info?.title || this.sls.service.service, - description: this.arazzoDocumentation?.info?.description || null, - summary: this.arazzoDocumentation?.info?.summary || null, - version: this.arazzoDocumentation?.info?.version || '1', - } + parse() { + this.generateInfo(); + this.generateSourceDescriptions(); + this.generateWorkflows(); + } - if (info.description === null) delete info.description; - if (info.summary === null) delete info.summary; + generateInfo() { + const info = { + title: this.arazzoDocumentation?.info?.title || this.sls.service.service, + description: this.arazzoDocumentation?.info?.description || null, + summary: this.arazzoDocumentation?.info?.summary || null, + version: this.arazzoDocumentation?.info?.version || "1", + }; - const extended = this.extendSpecification(this.arazzoDocumentation?.info) - Object.assign(info, extended); + if (info.description === null) delete info.description; + if (info.summary === null) delete info.summary; - this.arazzo.info = info - } + const extended = this.extendSpecification(this.arazzoDocumentation?.info); + Object.assign(info, extended); - generateSourceDescriptions() { - const sourceDescriptions = [] + this.arazzo.info = info; + } - const sourceDescription = { - name: `${this.arazzo.info.title}-openAPI`, - url: this.sourceFile, - type: 'openapi' - } + generateSourceDescriptions() { + const sourceDescriptions = []; - sourceDescriptions.push(sourceDescription); + const sourceDescription = { + name: `${this.arazzo.info.title}-openAPI`, + url: this.sourceFile, + type: "openapi", + }; - if (this.arazzoDocumentation?.sourceDescriptions){ - for (const sourceDescription of this.arazzoDocumentation?.sourceDescriptions) { - const extended = this.extendSpecification(sourceDescription) - sourceDescription.type = sourceDescription.type.toLowerCase() + sourceDescriptions.push(sourceDescription); - Object.assign(sourceDescription, extended) + if (this.arazzoDocumentation?.sourceDescriptions) { + for (const sourceDescription of this.arazzoDocumentation + ?.sourceDescriptions) { + const extended = this.extendSpecification(sourceDescription); + sourceDescription.type = sourceDescription.type.toLowerCase(); - sourceDescriptions.push(sourceDescription); - } - } + Object.assign(sourceDescription, extended); - this.arazzo.sourceDescriptions = sourceDescriptions; + sourceDescriptions.push(sourceDescription); + } } - generateWorkflows() { - const workflows = [] - for (const workflow of this.arazzoDocumentation?.workflows || []) { - const obj = {}; - this.currentWorkflow = workflow; + this.arazzo.sourceDescriptions = sourceDescriptions; + } - obj.workflowId = workflow.workflowId; + generateWorkflows() { + const workflows = []; + for (const workflow of this.arazzoDocumentation?.workflows || []) { + const obj = {}; + this.currentWorkflow = workflow; - if (workflow.summary) obj.summary = workflow.summary; - if (workflow.description) obj.description = workflow.description; - if (workflow.inputs) obj.inputs = workflow.inputs; - if (workflow.dependsOns) obj.dependsOns = workflow.dependsOns; + obj.workflowId = workflow.workflowId; - if (workflow.successActions) { - obj.successActions = this.generateOnSuccess(workflow.successActions); - } + if (workflow.summary) obj.summary = workflow.summary; + if (workflow.description) obj.description = workflow.description; + if (workflow.inputs) obj.inputs = workflow.inputs; + if (workflow.dependsOns) obj.dependsOns = workflow.dependsOns; - if (workflow.failureActions) { - obj.failureActions = this.generateOnFailure(workflow.failureActions); - } + if (workflow.successActions) { + obj.successActions = this.generateOnSuccess(workflow.successActions); + } - if (workflow.outputs) obj.outputs = workflow.outputs; + if (workflow.failureActions) { + obj.failureActions = this.generateOnFailure(workflow.failureActions); + } - if (workflow.parameters) { - obj.parameters = this.generateParameters(workflow.parameters); - } + if (workflow.outputs) obj.outputs = workflow.outputs; - if (this.isStepConfigurationInCustom()) { - obj.steps = this.generateSteps(); - } else { - obj.steps = this.generateStepsFromEvents(); - } - - workflows.push(obj) - } + if (workflow.parameters) { + obj.parameters = this.generateParameters(workflow.parameters); + } - this.arazzo.workflows = workflows - } + if (this.isStepConfigurationInCustom()) { + obj.steps = this.generateSteps(); + } else { + obj.steps = this.generateStepsFromEvents(); + } - isStepConfigurationInCustom() { - return this.arazzoDocumentation.workflows.some(workflowObj => { - if (Object.hasOwn(workflowObj, 'steps')) return true; - }); + workflows.push(obj); } - generateStepsFromEvents() { - const steps = []; - const viableFunctions = this.getViableFunctions(); - for (const viableFunction of viableFunctions) { - for (const event of viableFunction.event) { - const eventKey = Object.keys(event).at(0); - if (event[eventKey]?.arazzo) { - for (const workflow of event[eventKey]?.arazzo.workflows) { - if (workflow.workflowName === this.currentWorkflow.workflowName) { - if (Object.keys(workflow).some(key => ['operationId', 'workflowId', 'operationPath'].includes(key)) === false) { - workflow.operationId = viableFunction.operationName; - } - - const step = this.generateSteps(workflow); - steps.push(step.at(0)); - } - } - } + this.arazzo.workflows = workflows; + } + + isStepConfigurationInCustom() { + return this.arazzoDocumentation.workflows.some((workflowObj) => { + if (Object.hasOwn(workflowObj, "steps")) return true; + }); + } + + generateStepsFromEvents() { + const steps = []; + const viableFunctions = this.getViableFunctions(); + for (const viableFunction of viableFunctions) { + for (const event of viableFunction.event) { + const eventKey = Object.keys(event).at(0); + if (event[eventKey]?.arazzo) { + for (const workflow of event[eventKey]?.arazzo.workflows) { + if (workflow.workflowName === this.currentWorkflow.workflowName) { + if ( + Object.keys(workflow).some((key) => + ["operationId", "workflowId", "operationPath"].includes(key), + ) === false + ) { + workflow.operationId = viableFunction.operationName; + } + + const step = this.generateSteps(workflow); + steps.push(step.at(0)); } + } } - - return steps.sort((a, b) => a.stepNumber - b.stepNumber).map(({stepNumber, ...keepAttrs}) => keepAttrs) - + } } - generateSteps(stepObj) { - const stepsArr = stepObj ? [stepObj] : this.currentWorkflow.steps; + return steps + .sort((a, b) => a.stepNumber - b.stepNumber) + .map(({ stepNumber, ...keepAttrs }) => keepAttrs); + } - const steps = []; + generateSteps(stepObj) { + const stepsArr = stepObj ? [stepObj] : this.currentWorkflow.steps; - for (const step of stepsArr) { - this.currentStep = step; - const obj = { - stepId: step.stepId - }; + const steps = []; - if (step.description) obj.description = step.description; + for (const step of stepsArr) { + this.currentStep = step; + const obj = { + stepId: step.stepId, + }; - if (step.operationId) { - obj.operationId = step.operationId; - } + if (step.description) obj.description = step.description; - if (step.operationPath) { - obj.operationPath = step.operationPath; - } + if (step.operationId) { + obj.operationId = step.operationId; + } - if (step.workflowId) { - obj.workflowId = step.workflowId; - } + if (step.operationPath) { + obj.operationPath = step.operationPath; + } - if (step.parameters) { - obj.parameters = this.generateParameters(step.parameters); - } + if (step.workflowId) { + obj.workflowId = step.workflowId; + } - if (step.requestBody) { - const extended = this.extendSpecification(step.requestBody); - Object.assign(step.requestBody, extended); - obj.requestBody = step.requestBody - } + if (step.parameters) { + obj.parameters = this.generateParameters(step.parameters); + } - if (step.successCriteria) { - obj.successCriteria = this.generateCriteria(step.successCriteria); - } + if (step.requestBody) { + const extended = this.extendSpecification(step.requestBody); + Object.assign(step.requestBody, extended); + obj.requestBody = step.requestBody; + } - if (step.onSuccess) { - obj.onSuccess = this.generateOnSuccess(step.onSuccess); - } + if (step.successCriteria) { + obj.successCriteria = this.generateCriteria(step.successCriteria); + } - if (step.onFailure) { - obj.onFailure = this.generateOnFailure(step.onFailure) - } + if (step.onSuccess) { + obj.onSuccess = this.generateOnSuccess(step.onSuccess); + } - if (step.outputs) { - obj.outputs = step.outputs; - } + if (step.onFailure) { + obj.onFailure = this.generateOnFailure(step.onFailure); + } - if (step.stepNumber) { - obj.stepNumber = step.stepNumber; - } + if (step.outputs) { + obj.outputs = step.outputs; + } - steps.push(obj) - } + if (step.stepNumber) { + obj.stepNumber = step.stepNumber; + } - return steps; + steps.push(obj); } - generateParameters(parametersArr) { - const params = [] - for (const param of parametersArr) { - const extended = this.extendSpecification(param); + return steps; + } - Object.assign(param, extended); + generateParameters(parametersArr) { + const params = []; + for (const param of parametersArr) { + const extended = this.extendSpecification(param); - params.push(param) - } + Object.assign(param, extended); - return params; + params.push(param); } - generateOnSuccess(successObj) { - const obj = { - name: successObj.name, - type: successObj.type, - } - - if (successObj.workflowId) { - obj.workflowId = successObj.workflowId; - } + return params; + } - if (successObj.stepId) { - obj.stepId = successObj.stepId; - } + generateOnSuccess(successObj) { + const obj = { + name: successObj.name, + type: successObj.type, + }; - if (successObj.criteria) { - obj.criteria = this.generateCriteria(successObj.criteria); - } - - return obj; + if (successObj.workflowId) { + obj.workflowId = successObj.workflowId; } - generateOnFailure(failureObj) { - const obj = { - name: failureObj.name, - type: failureObj.type, - } - - if (failureObj.workflowId) { - obj.workflowId = failureObj.workflowId; - } - - if (failureObj.stepId) { - obj.stepId = failureObj.stepId; - } + if (successObj.stepId) { + obj.stepId = successObj.stepId; + } - if (failureObj.type === 'retry' && failureObj.retryAfter) { - obj.retryAfter = failureObj.retryAfter; - } + if (successObj.criteria) { + obj.criteria = this.generateCriteria(successObj.criteria); + } - if (failureObj.type === 'retry' && failureObj.retryLimit) { - obj.retryLimit = failureObj.retryLimit; - } + return obj; + } - if (failureObj.criteria) { - obj.criteria = this.generateCriteria(successObj.criteria); - } + generateOnFailure(failureObj) { + const obj = { + name: failureObj.name, + type: failureObj.type, + }; - return obj; + if (failureObj.workflowId) { + obj.workflowId = failureObj.workflowId; } - generateCriteria(criteriasArr) { - const criterias = [] - for (const criteria of criteriasArr) { - const extended = this.extendSpecification(criteria); - - Object.assign(criteria, extended); + if (failureObj.stepId) { + obj.stepId = failureObj.stepId; + } - criterias.push(criteria) - } + if (failureObj.type === "retry" && failureObj.retryAfter) { + obj.retryAfter = failureObj.retryAfter; + } - return criterias; + if (failureObj.type === "retry" && failureObj.retryLimit) { + obj.retryLimit = failureObj.retryLimit; } - validateSchema(schema) { - this.ajv.validateSchema(schema) + if (failureObj.criteria) { + obj.criteria = this.generateCriteria(successObj.criteria); } - extendSpecification(spec) { - if (spec) { - const obj = {}; - for (const key of Object.keys(spec)) { - if (/^[x\-]/i.test(key)) { - Object.assign(obj, { [key]: spec[key] }); - } - } + return obj; + } - return obj; - } - } + generateCriteria(criteriasArr) { + const criterias = []; + for (const criteria of criteriasArr) { + const extended = this.extendSpecification(criteria); - getViableFunctions() { - const isViableFunction = (functionTypes) => { - return Object.keys(functionTypes).some(functionType => this.viableArazzoKeys.includes(functionType)); - }; + Object.assign(criteria, extended); - const functionNames = this.sls.service.getAllFunctions(); - - return functionNames - .map((functionName) => { - return this.sls.service.getFunction(functionName); - }) - .filter((functionType) => { - if (functionType?.events.some(isViableFunction)) return functionType; - }) - .map((functionType) => { - const event = functionType.events.filter(isViableFunction); - const operationName = functionType.name.split("-").at(-1); - - return { - operationName: operationName, - functionInfo: functionType, - handler: functionType.handler, - name: functionType.name, - event, - }; - }); + criterias.push(criteria); } - async validate() { - const config = await createConfig({ - apis: {}, - rules: this.REDOCLY_RULES, - }); + return criterias; + } - const apiDesc = stringifyYaml(this.arazzo); + validateSchema(schema) { + this.ajv.validateSchema(schema); + } - return await lintFromString({ - source: apiDesc, - config: config, - }).catch((err) => { - throw err; - }); + extendSpecification(spec) { + if (spec) { + const obj = {}; + for (const key of Object.keys(spec)) { + if (/^[x\-]/i.test(key)) { + Object.assign(obj, { [key]: spec[key] }); + } + } + + return obj; } + } + + getViableFunctions() { + const isViableFunction = (functionTypes) => { + return Object.keys(functionTypes).some((functionType) => + this.viableArazzoKeys.includes(functionType), + ); + }; + + const functionNames = this.sls.service.getAllFunctions(); + + return functionNames + .map((functionName) => { + return this.sls.service.getFunction(functionName); + }) + .filter((functionType) => { + if (functionType?.events.some(isViableFunction)) return functionType; + }) + .map((functionType) => { + const event = functionType.events.filter(isViableFunction); + const operationName = functionType.name.split("-").at(-1); + + return { + operationName: operationName, + functionInfo: functionType, + handler: functionType.handler, + name: functionType.name, + event, + }; + }); + } + + async validate() { + const config = await createConfig({ + apis: {}, + rules: this.REDOCLY_RULES, + }); + + const apiDesc = stringifyYaml(this.arazzo); + + return await lintFromString({ + source: apiDesc, + config: config, + }).catch((err) => { + throw err; + }); + } } module.exports = ArazzoGenerator; diff --git a/src/ArazzoPlugin.js b/src/ArazzoPlugin.js index e2ae1bb..cd01d02 100644 --- a/src/ArazzoPlugin.js +++ b/src/ArazzoPlugin.js @@ -1,215 +1,216 @@ -'use strict'; +"use strict"; const chalk = require("chalk"); const yaml = require("js-yaml"); -const fs = require('fs/promises') +const fs = require("fs/promises"); -const ArazzoGenerator = require('./ArazzoGenerator'); -const ArazzoRunner = require('./ArazzoRunner'); +const ArazzoGenerator = require("./ArazzoGenerator"); +const ArazzoRunner = require("./ArazzoRunner"); const Logger = require("./Logger"); -const serverlessSchema = require('../resources/serverlessSchema.json') +const serverlessSchema = require("../resources/serverlessSchema.json"); class ArazzoPlugin { - constructor(serverless, options, {log={}} = {}) { - this.serverless = serverless; - this.logOutput = log; - - this.logger = new Logger(this.serverless.version[0], this.logOutput); - - this.commands = { - arazzo: { - commands: { - generate: { - lifecycleEvents: ['serverless'], - usage: 'Generate Arazzo Specification', - options: { - output: { - usage: 'Arazzo file output location [default: arazzo.json]', - shortcut: 'o', - type: 'string', - }, - format: { - usage: 'Arazzo file format (yaml|json) [default: json]', - shortcut: 'f', - type: 'string', - }, - source: { - usage: 'The default OpenAPI Source file to use [default: openapi.json]', - shortcut: 's', - type: 'string', - } - } - }, - run: { - lifecycleEvents: ['serverless'], - usage: 'Run an Arazzo Specification', - options: { - source: { - usage: 'The default Arazzo Specification Source file to use [default: arazzo.json]', - shortcut: 's', - type: 'string', - }, - input: { - usage: 'The input variables to fill in things like Query Strings and Path parameters [default: input.json]', - shortcut: 'i', - type: 'string', - }, - }, - }, - }, + constructor(serverless, options, { log = {} } = {}) { + this.serverless = serverless; + this.logOutput = log; + + this.logger = new Logger(this.serverless.version[0], this.logOutput); + + this.commands = { + arazzo: { + commands: { + generate: { + lifecycleEvents: ["serverless"], + usage: "Generate Arazzo Specification", + options: { + output: { + usage: "Arazzo file output location [default: arazzo.json]", + shortcut: "o", + type: "string", + }, + format: { + usage: "Arazzo file format (yaml|json) [default: json]", + shortcut: "f", + type: "string", + }, + source: { + usage: + "The default OpenAPI Source file to use [default: openapi.json]", + shortcut: "s", + type: "string", + }, }, - }; + }, + run: { + lifecycleEvents: ["serverless"], + usage: "Run an Arazzo Specification", + options: { + source: { + usage: + "The default Arazzo Specification Source file to use [default: arazzo.json]", + shortcut: "s", + type: "string", + }, + input: { + usage: + "The input variables to fill in things like Query Strings and Path parameters [default: input.json]", + shortcut: "i", + type: "string", + }, + }, + }, + }, + }, + }; - this.hooks = { - "arazzo:generate:serverless": this.generate.bind(this), - "arazzo:run:serverless": this.run.bind(this), - }; + this.hooks = { + "arazzo:generate:serverless": this.generate.bind(this), + "arazzo:run:serverless": this.run.bind(this), + }; - // this.serverless.configSchemaHandler.defineCustomProperties(serverlessSchema); - } + // this.serverless.configSchemaHandler.defineCustomProperties(serverlessSchema); + } - async generate() { - this.logger.notice( - chalk.bold.underline("Arazzo v1 Specification Generation") - ); + async generate() { + this.logger.notice( + chalk.bold.underline("Arazzo v1 Specification Generation"), + ); - this.generateArazzo = true; - this.processCLIInput(); + this.generateArazzo = true; + this.processCLIInput(); - await this.arazzoGeneration() - .catch((err) => { - throw new this.serverless.classes.Error(err); - }); - } + await this.arazzoGeneration().catch((err) => { + throw new this.serverless.classes.Error(err); + }); + } - async run() { - this.logger.notice( - chalk.bold.underline("Arazzo v1 Specification Runner") - ); + async run() { + this.logger.notice(chalk.bold.underline("Arazzo v1 Specification Runner")); - this.runArazzo = true; - this.processCLIInput(); + this.runArazzo = true; + this.processCLIInput(); - const runner = new ArazzoRunner( - `./${this.config.source}`, - { - logger: this.logger, - inputFile: this.config.input - } - ); + const runner = new ArazzoRunner(`./${this.config.source}`, { + logger: this.logger, + inputFile: this.config.input, + }); - await runner.runArazzoWorkflows() - .catch((err) => { - throw new this.serverless.classes.Error(err); - }); + await runner.runArazzoWorkflows().catch((err) => { + throw new this.serverless.classes.Error(err); + }); - this.logger.success("Arazzo Specification Successfully Run"); - } + this.logger.success("Arazzo Specification Successfully Run"); + } - async arazzoGeneration() { - const generator = new ArazzoGenerator( - this.serverless.service.custom.arazzo, - { - arazzo: this.config.arazzoVersion, - sls: this.serverless, - sourceFile: this.config.source - } - ); + async arazzoGeneration() { + const generator = new ArazzoGenerator( + this.serverless.service.custom.arazzo, + { + arazzo: this.config.arazzoVersion, + sls: this.serverless, + sourceFile: this.config.source, + }, + ); - this.logger.notice(`Generating Arazzo Specification`); - generator.parse(); + this.logger.notice(`Generating Arazzo Specification`); + generator.parse(); - this.logger.notice(`Validating generated Arazzo Specification`); + this.logger.notice(`Validating generated Arazzo Specification`); - await generator.validate().catch(err => { - this.logger.error( - `ERROR: An error was thrown validating the Arazzo Specification` - ); + await generator.validate().catch((err) => { + this.logger.error( + `ERROR: An error was thrown validating the Arazzo Specification`, + ); - throw new this.serverless.classes.Error(err); - }) + throw new this.serverless.classes.Error(err); + }); - this.logger.success("Arazzo Specification Successfully Generated"); - - this.arazzoSpecification = generator.arazzo; - - await this.writeArazzoFile(); - } + this.logger.success("Arazzo Specification Successfully Generated"); - async writeArazzoFile() { - let output; + this.arazzoSpecification = generator.arazzo; - if (this.config.format === 'yml') { - output = yaml.dump(this.arazzoSpecification) - } else { - output = JSON.stringify(this.arazzoSpecification, null) - } + await this.writeArazzoFile(); + } - await fs.writeFile(this.config.file, output); + async writeArazzoFile() { + let output; - this.logger.success("Arazzo Specification Successfully Written"); + if (this.config.format === "yml") { + output = yaml.dump(this.arazzoSpecification); + } else { + output = JSON.stringify(this.arazzoSpecification, null); } - processCLIInput() { - const config = { - format: "json", - file: "arazzo.json", - arazzoVersion: "1.0.1", - validationWarn: false, - source: this.generateArazzo ? 'openapi.json' : 'arazzo.json', - input: 'input.json', - }; - - if (this.serverless.processedInput?.options?.format?.toLowerCase() === 'yaml') {this.serverless.processedInput.options.format = 'yml';} + await fs.writeFile(this.config.file, output); + + this.logger.success("Arazzo Specification Successfully Written"); + } + + processCLIInput() { + const config = { + format: "json", + file: "arazzo.json", + arazzoVersion: "1.0.1", + validationWarn: false, + source: this.generateArazzo ? "openapi.json" : "arazzo.json", + input: "input.json", + }; + + if ( + this.serverless.processedInput?.options?.format?.toLowerCase() === "yaml" + ) { + this.serverless.processedInput.options.format = "yml"; + } - config.format = this.serverless.processedInput?.options?.format?.toLowerCase() || "json"; + config.format = + this.serverless.processedInput?.options?.format?.toLowerCase() || "json"; - if (["yml", "json"].indexOf(config.format.toLowerCase()) < 0) { - throw new this.serverless.classes.Error( - 'Invalid Output Format Specified - must be one of "yaml" or "json"' - ); - } + if (["yml", "json"].indexOf(config.format.toLowerCase()) < 0) { + throw new this.serverless.classes.Error( + 'Invalid Output Format Specified - must be one of "yaml" or "json"', + ); + } - if (this.serverless.processedInput.options.output) { - config.file = `${this.serverless.processedInput.options.output}`; - } + if (this.serverless.processedInput.options.output) { + config.file = `${this.serverless.processedInput.options.output}`; + } - if (this.serverless.processedInput.options.source) { - config.source = this.serverless.processedInput.options.source; - } + if (this.serverless.processedInput.options.source) { + config.source = this.serverless.processedInput.options.source; + } - if (this.serverless.processedInput.options.input) { - config.input = this.serverless.processedInput.options.input; - } + if (this.serverless.processedInput.options.input) { + config.input = this.serverless.processedInput.options.input; + } - this.config = config; + this.config = config; - this.outputOptions() - } + this.outputOptions(); + } - outputOptions() { - if (this.generateArazzo) { - this.logger.notice( - ` + outputOptions() { + if (this.generateArazzo) { + this.logger.notice( + ` ${chalk.bold.green("[OPTIONS]")} Arazzo Version: "${chalk.bold.green(String(this.config.arazzoVersion))}" Format: "${chalk.bold.green(this.config.format)}" Output File: "${chalk.bold.green(this.config.file)}" Source File: "${chalk.bold.green(this.config.source)}" - ` - ) - } else { - this.logger.notice( - ` + `, + ); + } else { + this.logger.notice( + ` ${chalk.bold.green("[OPTIONS]")} Source File: "${chalk.bold.green(this.config.source)}" Input File: "${chalk.bold.green(this.config.input)}" - ` - ) - } + `, + ); } + } } module.exports = ArazzoPlugin; diff --git a/src/ArazzoRunner.js b/src/ArazzoRunner.js index c04f3dc..64c8355 100644 --- a/src/ArazzoRunner.js +++ b/src/ArazzoRunner.js @@ -1,61 +1,60 @@ -'use strict'; +"use strict"; -const peggy = require('peggy'); -const { chain } = require('stream-chain'); -const Pick = require('stream-json/filters/Pick'); -const { streamValues } = require('stream-json/streamers/StreamValues'); +const peggy = require("peggy"); +const { chain } = require("stream-chain"); +const Pick = require("stream-json/filters/Pick"); +const { streamValues } = require("stream-json/streamers/StreamValues"); -const fs = require('node:fs'); -const fsp = require('node:fs/promises'); -const path = require('node:path'); +const fs = require("node:fs"); +const fsp = require("node:fs/promises"); +const path = require("node:path"); -const Arazzo = require('./Arazzo'); -const Input = require('./Input'); +const Arazzo = require("./Arazzo"); +const Input = require("./Input"); class ArazzoRunner { - constructor(pathToArazzoSpecification = './arazzo.json', options) { - this.pathToArazzoSpecification = pathToArazzoSpecification; + constructor(pathToArazzoSpecification = "./arazzo.json", options) { + this.pathToArazzoSpecification = pathToArazzoSpecification; - this.logger = options.logger; + this.logger = options.logger; - this.openAPISteps = false; + this.openAPISteps = false; - this.inputFile = new Input(options.inputFile, 'inputs'); - } + this.inputFile = new Input(options.inputFile, "inputs"); + } - async runArazzoWorkflows() { - await this.loadPeggyRules(); + async runArazzoWorkflows() { + await this.loadPeggyRules(); - this.arazzoDocument = new Arazzo( - this.pathToArazzoSpecification, - 'mainArazzo', - { - parser: this.parser, - logger: this.logger - } - ); + this.arazzoDocument = new Arazzo( + this.pathToArazzoSpecification, + "mainArazzo", + { + parser: this.parser, + logger: this.logger, + }, + ); - this.arazzoDocument.setMainArazzo() + this.arazzoDocument.setMainArazzo(); - await this.arazzoDocument.runWorkflows(this.inputFile); - } + await this.arazzoDocument.runWorkflows(this.inputFile); + } - async loadPeggyRules() { - const peggyPath = path.join(__dirname, '..', 'resources', 'rules.peggy'); - const peggyRuleSet = await fsp.readFile(peggyPath); + async loadPeggyRules() { + const peggyPath = path.join(__dirname, "..", "resources", "rules.peggy"); + const peggyRuleSet = await fsp.readFile(peggyPath); - this.parser = peggy.generate(peggyRuleSet.toString()); - } + this.parser = peggy.generate(peggyRuleSet.toString()); + } - - JSONPicker(key, file) { - const pipeline = chain([ - fs.createReadStream(file), - Pick.withParser({filter: key}), - streamValues() - ]); - return pipeline - } + JSONPicker(key, file) { + const pipeline = chain([ + fs.createReadStream(file), + Pick.withParser({ filter: key }), + streamValues(), + ]); + return pipeline; + } } module.exports = ArazzoRunner; diff --git a/src/DocFactory.js b/src/DocFactory.js index 43678f1..2716cbf 100644 --- a/src/DocFactory.js +++ b/src/DocFactory.js @@ -1,23 +1,23 @@ -'use strict'; +"use strict"; -const Arazzo = require('./Arazzo'); -const OpenAPI = require('./OpenAPI'); +const Arazzo = require("./Arazzo"); +const OpenAPI = require("./OpenAPI"); class DocumentFactory { - constructor() {} + constructor() {} - async buildDocument(type, path, name, options) { - let document; - if (type === 'openapi') { - document = new OpenAPI(path, name, options); - } else { - document = new Arazzo(path, name, options); - } + async buildDocument(type, path, name, options) { + let document; + if (type === "openapi") { + document = new OpenAPI(path, name, options); + } else { + document = new Arazzo(path, name, options); + } - await document.loadDocument(); + await document.loadDocument(); - return document; - } + return document; + } } module.exports = new DocumentFactory(); diff --git a/src/Document.js b/src/Document.js index d68a7e9..34eff82 100644 --- a/src/Document.js +++ b/src/Document.js @@ -1,121 +1,124 @@ -'use strict'; +"use strict"; const { bundleDocument, bundleFromString, - createConfig + createConfig, } = require("@redocly/openapi-core"); -const { chain } = require('stream-chain'); -const Pick = require('stream-json/filters/Pick'); -const { streamArray } = require('stream-json/streamers/StreamArray'); -const { streamValues } = require('stream-json/streamers/StreamValues'); - -const fs = require('node:fs'); -const fsp = require('node:fs/promises'); -const path = require('node:path'); +const { chain } = require("stream-chain"); +const Pick = require("stream-json/filters/Pick"); +const { streamArray } = require("stream-json/streamers/StreamArray"); +const { streamValues } = require("stream-json/streamers/StreamValues"); +const fs = require("node:fs"); +const fsp = require("node:fs/promises"); +const path = require("node:path"); class Document { - constructor(url, name, {parser, logger}) { - this.url = url; - this.name = name; - this.parser = parser; - this.logger = logger; + constructor(url, name, { parser, logger }) { + this.url = url; + this.name = name; + this.parser = parser; + this.logger = logger; + } + + async loadDocument() { + const response = await fetch(this.url); + + if (!response.ok) { + throw new Error(`Error fetching document from ${this.url}`); } - async loadDocument() { - const response = await fetch(this.url); - - if (!response.ok) { - throw new Error(`Error fetching document from ${this.url}`); - } - - let data = await response.json(); - - await this.writeDocument(data) + let data = await response.json(); + + await this.writeDocument(data); + } + + async writeDocument(data) { + let document = data; + if (this.type === "openapi") { + const config = await createConfig({}); + const bundledData = await bundleFromString({ + source: JSON.stringify(data), + dereference: true, + config: config, + }); + document = bundledData.bundle.parsed; } - async writeDocument(data) { - let document = data; - if (this.type === 'openapi') { - const config = await createConfig({}) - const bundledData = await bundleFromString({source: JSON.stringify(data), dereference: true, config: config}) - document = bundledData.bundle.parsed; - } - - this.fileName = `${this.name}.json`; - await fsp.writeFile(this.fileName, JSON.stringify(document)); - this.filePath = path.resolve(this.fileName); + this.fileName = `${this.name}.json`; + await fsp.writeFile(this.fileName, JSON.stringify(document)); + this.filePath = path.resolve(this.fileName); + } + + async readStreamFromURL(key) { + try { + const response = await fetch(this.url); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + return chain([ + response.body, // ReadableStream from fetch + Pick.withParser({ filter: key }), + streamValues(), // Change to streamObject() if JSON is an object + ]); + } catch (err) { + console.error("Error reading stream:", err); } - - async readStreamFromURL(key) { - try { - const response = await fetch(this.url); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - return chain([ - response.body, // ReadableStream from fetch - Pick.withParser({filter: key}), - streamValues(), // Change to streamObject() if JSON is an object - ]); - } catch (err) { - console.error("Error reading stream:", err); - } + } + + matchesExpectedRunTimeExpression(string, runtimeExpression) { + const result = this.parser.parse(string, { peg$library: true }); + if (result.peg$success) { + if (result.peg$result[0] === runtimeExpression) { + return true; + } } - matchesExpectedRunTimeExpression(string, runtimeExpression) { - const result = this.parser.parse(string, { peg$library: true }); - if (result.peg$success) { - if (result.peg$result[0] === runtimeExpression) { - return true; - } + return false; + } + + JSONPickerToIndex(key, index, file = this.filePath) { + return new Promise((resolve, reject) => { + const pipeline = chain([ + fs.createReadStream(path.resolve(file)), + Pick.withParser({ filter: key }), + streamArray(), + ]); + + pipeline.on("data", ({ key, value }) => { + if (key === index) { + resolve(value); + pipeline.destroy(); } - - return false; - } - - JSONPickerToIndex(key, index, file = this.filePath) { - return new Promise((resolve, reject) => { - const pipeline = chain([ - fs.createReadStream(path.resolve(file)), - Pick.withParser({filter: key}), - streamArray() - ]); - - pipeline.on('data', ({key, value}) => { - if (key === index) { - resolve(value); - pipeline.destroy(); - } - }); - - pipeline.on('error', (err) => { - reject(err); - }); - - pipeline.on('end', () => { - resolve(null); - }) - }); - } - - JSONPicker(key, file) { - let pipeline; - // if (this.httpPath) { - // pipeline = this.readStreamFromURL(key) - // } else { - pipeline = chain([ - fs.createReadStream(path.resolve(file)), - Pick.withParser({filter: key}), - streamValues() - ]); - // } - - return pipeline; - } + }); + + pipeline.on("error", (err) => { + reject(err); + }); + + pipeline.on("end", () => { + resolve(null); + }); + }); + } + + JSONPicker(key, file) { + let pipeline; + // if (this.httpPath) { + // pipeline = this.readStreamFromURL(key) + // } else { + pipeline = chain([ + fs.createReadStream(path.resolve(file)), + Pick.withParser({ filter: key }), + streamValues(), + ]); + // } + + return pipeline; + } } module.exports = Document; diff --git a/src/Input.js b/src/Input.js index c4c52c8..7a42317 100644 --- a/src/Input.js +++ b/src/Input.js @@ -1,42 +1,44 @@ -'use strict'; +"use strict"; -const Ajv = require("ajv") +const Ajv = require("ajv"); -const path = require('node:path') +const path = require("node:path"); -const Document = require('./Document'); +const Document = require("./Document"); class Input extends Document { - constructor(filePath, name) { - super(filePath, name, {}); + constructor(filePath, name) { + super(filePath, name, {}); - this.type = 'input'; + this.type = "input"; - this.filePath = path.resolve(filePath); + this.filePath = path.resolve(filePath); - this.ajv = new Ajv() - } - - async getWorkflowInputs(workflowId, schema) { - const pipeline = this.JSONPicker(workflowId, this.filePath); + this.ajv = new Ajv(); + } - for await (const { value } of pipeline) { - this.validateInputs(value, schema) - this.inputs = value; - } + async getWorkflowInputs(workflowId, schema) { + const pipeline = this.JSONPicker(workflowId, this.filePath); - return this.inputs; + for await (const { value } of pipeline) { + this.validateInputs(value, schema); + this.inputs = value; } - validateInputs(value, schema) { - const validate = this.ajv.compile(schema); + return this.inputs; + } + + validateInputs(value, schema) { + const validate = this.ajv.compile(schema); - const valid = validate(value); + const valid = validate(value); - if (!valid) { - throw new Error('Input values do not match Input schema', { cause: validate.errors }); - } + if (!valid) { + throw new Error("Input values do not match Input schema", { + cause: validate.errors, + }); } + } } module.exports = Input; diff --git a/src/OpenAPI.js b/src/OpenAPI.js index 465ef93..566e0fb 100644 --- a/src/OpenAPI.js +++ b/src/OpenAPI.js @@ -1,121 +1,120 @@ -'use strict'; +"use strict"; -const Document = require('./Document'); +const Document = require("./Document"); class OpenAPI extends Document { - constructor(url, name, options) { - super(url, name, options); - - this.type = 'openapi'; - - } - - async getOperationById(operationId) { - const pipeline = await this.JSONPicker('paths', this.filePath); - - for await (const { value } of pipeline) { - for (let key in value) { - for (let operation in value[key]) { - if (value[key][operation]?.operationId === operationId) { - this.path = key; - this.operation = operation; - this.operationDetails = value[key][operation] - } - } - } + constructor(url, name, options) { + super(url, name, options); + + this.type = "openapi"; + } + + async getOperationById(operationId) { + const pipeline = await this.JSONPicker("paths", this.filePath); + + for await (const { value } of pipeline) { + for (let key in value) { + for (let operation in value[key]) { + if (value[key][operation]?.operationId === operationId) { + this.path = key; + this.operation = operation; + this.operationDetails = value[key][operation]; + } } + } + } - if (this.path === undefined) { - throw new Error(`The OperationId: ${operationId} does not exist`) - } + if (this.path === undefined) { + throw new Error(`The OperationId: ${operationId} does not exist`); } + } - async buildOperation(inputs, step) { - await this.getServers(); - // this.mapInputs(inputs, step) - this.buildOperations(); + async buildOperation(inputs, step) { + await this.getServers(); + // this.mapInputs(inputs, step) + this.buildOperations(); - // console.log(this.operations) - return this.operations; - } + // console.log(this.operations) + return this.operations; + } - async getServers() { - const pipeline = await this.JSONPicker('servers', this.filePath); + async getServers() { + const pipeline = await this.JSONPicker("servers", this.filePath); - for await (const { value } of pipeline) { - this.servers = value; - } + for await (const { value } of pipeline) { + this.servers = value; } - - // mapInputs(inputs, step) { - // if (step.parameters) { - // this.mapParamsToInputs(inputs, step); - // } - - // if (step.requestBody) { - // this.mapRequestBodyToInputs(inputs, step); - // } - // } - - // mapParamsToInputs(inputs, step) { - // const headers = new Headers(); - // const queryParams = new URLSearchParams(); - - // for (const param of step.parameters) { - // if (this.matchesExpectedRunTimeExpression(param.value, '$inputs.')) { - // const inputName = param.value.split('.')[1]; - // const inputValue = inputs[inputName]; - - // if (param.in === 'header') { - // headers.append(param.name, inputValue); - // } else if (param.in === 'query') { - // queryParams.append(param.name, inputValue); - // } else if (param.in === 'path') { - // this.path = this.path.replace(`{${inputName}}`, inputValue) - // } - // } - // } - - // this.headers = headers; - // this.queryParams = queryParams; - // } - - // mapRequestBodyToInputs(inputs, step) { - // if (step.requestBody.contentType || Object.keys(this.operationDetails.requestBody.content) === 1) { - // for (const contentType in this.operationDetails.requestBody.content) { - // if (step.requestBody.contentType === contentType) { - // if (contentType === 'application/json') { - // const payload = structuredClonestep.requestBody.payload; - // traverse(payload).forEach(function(value) { - // if (this.matchesExpectedRunTimeExpression(value, '$inputs.')) { - // const inputName = param.value.split('.')[1]; - // const inputValue = inputs[inputName]; - - // this.update(inputValue); - // } - // }); - // this.payload = payload; - // } - // } - // } - // } else { - // throw new Error(`Too many contentTypes on ${this.operationDetails.operationId}, please add the targeted contentType to the Arazzo Documentation`); - // } - // } - - buildOperations() { - this.operations = [] - - for (const server of this.servers) { - this.operations.push({ - url: `${server.url}${this.path}`, - operation: this.operation, - // headers: this.headers, - // queryParams: this.queryParams, - // payload: this?.payload || null - }); - } + } + + // mapInputs(inputs, step) { + // if (step.parameters) { + // this.mapParamsToInputs(inputs, step); + // } + + // if (step.requestBody) { + // this.mapRequestBodyToInputs(inputs, step); + // } + // } + + // mapParamsToInputs(inputs, step) { + // const headers = new Headers(); + // const queryParams = new URLSearchParams(); + + // for (const param of step.parameters) { + // if (this.matchesExpectedRunTimeExpression(param.value, '$inputs.')) { + // const inputName = param.value.split('.')[1]; + // const inputValue = inputs[inputName]; + + // if (param.in === 'header') { + // headers.append(param.name, inputValue); + // } else if (param.in === 'query') { + // queryParams.append(param.name, inputValue); + // } else if (param.in === 'path') { + // this.path = this.path.replace(`{${inputName}}`, inputValue) + // } + // } + // } + + // this.headers = headers; + // this.queryParams = queryParams; + // } + + // mapRequestBodyToInputs(inputs, step) { + // if (step.requestBody.contentType || Object.keys(this.operationDetails.requestBody.content) === 1) { + // for (const contentType in this.operationDetails.requestBody.content) { + // if (step.requestBody.contentType === contentType) { + // if (contentType === 'application/json') { + // const payload = structuredClonestep.requestBody.payload; + // traverse(payload).forEach(function(value) { + // if (this.matchesExpectedRunTimeExpression(value, '$inputs.')) { + // const inputName = param.value.split('.')[1]; + // const inputValue = inputs[inputName]; + + // this.update(inputValue); + // } + // }); + // this.payload = payload; + // } + // } + // } + // } else { + // throw new Error(`Too many contentTypes on ${this.operationDetails.operationId}, please add the targeted contentType to the Arazzo Documentation`); + // } + // } + + buildOperations() { + this.operations = []; + + for (const server of this.servers) { + this.operations.push({ + url: `${server.url}${this.path}`, + operation: this.operation, + // headers: this.headers, + // queryParams: this.queryParams, + // payload: this?.payload || null + }); } + } } module.exports = OpenAPI; diff --git a/test/unit/Arazzo.spec.js b/test/unit/Arazzo.spec.js index ceb3e74..de0dae0 100644 --- a/test/unit/Arazzo.spec.js +++ b/test/unit/Arazzo.spec.js @@ -1,544 +1,770 @@ -'use strict'; +"use strict"; -const { - bundleFromString, -} = require("@redocly/openapi-core"); +const { bundleFromString } = require("@redocly/openapi-core"); const expect = require("chai").expect; -const peggy = require('peggy'); -const nock = require('nock'); -const sinon = require('sinon'); +const peggy = require("peggy"); +const nock = require("nock"); +const sinon = require("sinon"); -const path = require('node:path'); -const fs = require('node:fs'); -const fsp = require('node:fs/promises'); +const path = require("node:path"); +const fs = require("node:fs"); +const fsp = require("node:fs/promises"); -const openAPIMock = require('../mocks/petStoreOpenAPI.json'); +const openAPIMock = require("../mocks/petStoreOpenAPI.json"); -const Input = require('../../src/Input.js'); -const Logger = require('../../src/Logger.js'); -const OpenAPI = require('../../src/OpenAPI.js'); - -const Arazzo = require('../../src/Arazzo'); +const Input = require("../../src/Input.js"); +const Logger = require("../../src/Logger.js"); +const OpenAPI = require("../../src/OpenAPI.js"); +const Arazzo = require("../../src/Arazzo"); describe(`Arazzo Document`, function () { - let parser; - before(async () => { - const peggyPath = path.join(__dirname, '..', '..', 'resources', 'rules.peggy'); - const peggyRuleSet = await fsp.readFile(peggyPath); - - parser = peggy.generate(peggyRuleSet.toString()); - }) - - - - const logger = new Logger( - '3', - { - notice: (str) => {}, - error: (str) => {}, - success: (str) => {}, - verbose: (str) => {}, - } + let parser; + before(async () => { + const peggyPath = path.join( + __dirname, + "..", + "..", + "resources", + "rules.peggy", ); - - describe(`constructor`, function () { - it(`should set the type to arazzo`, function() { - const expected = new Arazzo('./arazzo.json', 'arazzo', {logger: logger, parser}); - - expect(expected).to.be.instanceOf(Arazzo); - expect(expected.type).to.be.equal('arazzo'); - }); + const peggyRuleSet = await fsp.readFile(peggyPath); + + parser = peggy.generate(peggyRuleSet.toString()); + }); + + const logger = new Logger("3", { + notice: (str) => {}, + error: (str) => {}, + success: (str) => {}, + verbose: (str) => {}, + }); + + describe(`constructor`, function () { + it(`should set the type to arazzo`, function () { + const expected = new Arazzo("./arazzo.json", "arazzo", { + logger: logger, + parser, + }); + + expect(expected).to.be.instanceOf(Arazzo); + expect(expected.type).to.be.equal("arazzo"); }); - - describe(`setMainArazzo`, function () { - it(`should correctly set the filePath`, function() { - const arazzo = new Arazzo('./arazzo.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - expect(arazzo.filePath).to.be.equal(`${path.resolve(__dirname, '..', '..')}/arazzo.json`); - }); + }); + + describe(`setMainArazzo`, function () { + it(`should correctly set the filePath`, function () { + const arazzo = new Arazzo("./arazzo.json", "arazzo", { + logger: logger, + parser, + }); + arazzo.setMainArazzo(); + + expect(arazzo.filePath).to.be.equal( + `${path.resolve(__dirname, "..", "..")}/arazzo.json`, + ); }); - - - - xdescribe(`runWorkflows`, function () { - describe(`OpenAPI SourceDescriptions`, function () { - xdescribe(`singular step`, function () { - it(`return successfully when there are no more steps to run`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', - etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - nock('http://petstore.swagger.io:80', {"encodedQueryParams":true}) - .get('/v2/pet/findByStatus') - .query({"status":"pending"}) - .reply(200, [{"id":9515,"category":{"id":7792,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":7284,"name":"string"},{"id":9936,"name":"string"}],"status":"pending"},{"id":1109,"name":"Test9_02012026","photoUrls":[],"tags":[],"status":"pending"},{"id":1003,"name":"Lucy","photoUrls":["url1"],"tags":[],"status":"pending"},{"id":5619,"category":{"id":5381,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":9192,"name":"string"},{"id":8297,"name":"string"}],"status":"pending"},{"id":100002,"name":"Simple Cat","photoUrls":["https://example.com/cat.jpg"],"tags":[],"status":"pending"},{"id":922337203685477600,"name":"chedder-89811222","photoUrls":["./dog.png"],"tags":[],"status":"pending"},{"id":1088293,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-1088293","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"},{"id":745215,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-745215","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"}], { - 'access-control-allow-headers': 'Content-Type, api_key, Authorization', - 'access-control-allow-methods': 'GET, POST, DELETE, PUT', - 'access-control-allow-origin': '*', - connection: 'keep-alive', - 'content-type': 'application/json', - date: 'Fri, 02 Jan 2026 13:05:43 GMT', - server: 'Jetty(9.2.9.v20150224)', - 'transfer-encoding': 'chunked' - }); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - const expected = await arazzo.runWorkflows(inputFile); - console.log(expected) - } catch (err) { - expect(err).to.not.be.instanceOf(Error); - } - }); - }); - - xdescribe(`multiple Steps in one workflow`, function () { - it(`returns successfully when there are no more steps to run`, async function() { - nock.recorder.rec(); - - // nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - // .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - // .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - // 'accept-ranges': 'bytes', - // 'access-control-allow-origin': '*', - // 'cache-control': 'max-age=300', - // connection: 'keep-alive', - // 'content-encoding': 'gzip', - // 'content-length': '3189', - // 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - // 'content-type': 'text/plain; charset=utf-8', - // 'cross-origin-resource-policy': 'cross-origin', - // date: 'Fri, 02 Jan 2026 12:03:42 GMT', - // etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - // expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - // 'source-age': '0', - // 'strict-transport-security': 'max-age=31536000', - // vary: 'Authorization,Accept-Encoding', - // via: '1.1 varnish', - // 'x-cache': 'HIT', - // 'x-cache-hits': '1', - // 'x-content-type-options': 'nosniff', - // 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - // 'x-frame-options': 'deny', - // 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - // 'x-served-by': 'cache-lhr-egll1980089-LHR', - // 'x-timer': 'S1767355422.386688,VS0,VE1', - // 'x-xss-protection': '1; mode=block' - // }); - - // nock('http://petstore.swagger.io:80', {"encodedQueryParams":true}) - // .get('/v2/pet/findByStatus') - // .query({"status":"pending"}) - // .reply(200, [{"id":9515,"category":{"id":7792,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":7284,"name":"string"},{"id":9936,"name":"string"}],"status":"pending"},{"id":1109,"name":"Test9_02012026","photoUrls":[],"tags":[],"status":"pending"},{"id":1003,"name":"Lucy","photoUrls":["url1"],"tags":[],"status":"pending"},{"id":5619,"category":{"id":5381,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":9192,"name":"string"},{"id":8297,"name":"string"}],"status":"pending"},{"id":100002,"name":"Simple Cat","photoUrls":["https://example.com/cat.jpg"],"tags":[],"status":"pending"},{"id":922337203685477600,"name":"chedder-89811222","photoUrls":["./dog.png"],"tags":[],"status":"pending"},{"id":1088293,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-1088293","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"},{"id":745215,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-745215","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"}], { - // 'access-control-allow-headers': 'Content-Type, api_key, Authorization', - // 'access-control-allow-methods': 'GET, POST, DELETE, PUT', - // 'access-control-allow-origin': '*', - // connection: 'keep-alive', - // 'content-type': 'application/json', - // date: 'Fri, 02 Jan 2026 13:05:43 GMT', - // server: 'Jetty(9.2.9.v20150224)', - // 'transfer-encoding': 'chunked' - // }); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMockWithTwoSteps.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - const expected = await arazzo.runWorkflows(inputFile); - console.log(expected) - } catch (err) { - expect(err).to.not.be.instanceOf(Error); - } - }); - }); - - - xit(`should throw an error when a step fails onFailure at step level is included and is set to retry and all retries are exhausted`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', - etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - nock('http://petstore.swagger.io:80', {"encodedQueryParams":true}) - .get('/v2/pet/findByStatus') - .query({"status":"pending"}) - .times(3) - .reply(404); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMockWithRetryOnFailure.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - const spy = sinon.spy(arazzo, 'runOperation'); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(spy.callCount).to.be.equal(3); - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`Call to GET http://petstore.swagger.io/v2/pet/findByStatus failed with a 404`); - } - - spy.restore(); - }); - - xit(`should throw an error when a step fails and onFailure is omitted`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', - etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - nock('http://petstore.swagger.io:80', {"encodedQueryParams":true}) - .get('/v2/pet/findByStatus') - .query({"status":"pending"}) - .reply(404); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`Call to GET http://petstore.swagger.io/v2/pet/findByStatus failed with a 404`); - } - }); - - it(`should throw an error when the operationId does not exist in the OpenAPI document`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', - etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - const missingOperationIdOpenAPIFile = structuredClone(openAPIMock); - delete missingOperationIdOpenAPIFile.paths["/pet/findByStatus"]; - - const stub = sinon.stub(OpenAPI.prototype, 'writeDocument').resolves(); - OpenAPI.prototype.fileName = 'Arazzo-Workflow-for-Petstore-openAPI.json'; - OpenAPI.prototype.filePath = path.resolve(__dirname, '../..', 'Arazzo-Workflow-for-Petstore-openAPI.json'); - await fsp.writeFile('./Arazzo-Workflow-for-Petstore-openAPI.json', JSON.stringify(missingOperationIdOpenAPIFile)); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`The OperationId: findPetsByStatus does not exist`); - } - - stub.restore(); - }); - - xit(`should throw an error if redocly can not bundle the document`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', - etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - const stub = sinon.stub(bundleFromString).rejects(new Error('sinon threw an error when bundling the document')); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`sinon threw an error when bundling the document`); - } - - stub.restore(); - }); - }); - - it(`should throw an error when writing a downloaded sourceDescription file fails`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { - 'accept-ranges': 'bytes', - 'access-control-allow-origin': '*', - 'cache-control': 'max-age=300', - connection: 'keep-alive', - 'content-encoding': 'gzip', - 'content-length': '3189', - 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", - 'content-type': 'text/plain; charset=utf-8', - 'cross-origin-resource-policy': 'cross-origin', - date: 'Fri, 02 Jan 2026 12:03:42 GMT', + }); + + xdescribe(`runWorkflows`, function () { + describe(`OpenAPI SourceDescriptions`, function () { + xdescribe(`singular step`, function () { + it(`return successfully when there are no more steps to run`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get( + "/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json", + ) + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', - expires: 'Fri, 02 Jan 2026 12:08:42 GMT', - 'source-age': '0', - 'strict-transport-security': 'max-age=31536000', - vary: 'Authorization,Accept-Encoding', - via: '1.1 varnish', - 'x-cache': 'HIT', - 'x-cache-hits': '1', - 'x-content-type-options': 'nosniff', - 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', - 'x-frame-options': 'deny', - 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', - 'x-served-by': 'cache-lhr-egll1980089-LHR', - 'x-timer': 'S1767355422.386688,VS0,VE1', - 'x-xss-protection': '1; mode=block' - }); - - const stub = sinon.stub(fsp, 'writeFile').rejects(new Error('sinon threw an error when writing a sourceDescription file')); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`sinon threw an error when writing a sourceDescription file`); - } - - stub.restore(); + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": + "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + nock("http://petstore.swagger.io:80", { encodedQueryParams: true }) + .get("/v2/pet/findByStatus") + .query({ status: "pending" }) + .reply( + 200, + [ + { + id: 9515, + category: { id: 7792, name: "string" }, + name: "doggie", + photoUrls: ["string", "string"], + tags: [ + { id: 7284, name: "string" }, + { id: 9936, name: "string" }, + ], + status: "pending", + }, + { + id: 1109, + name: "Test9_02012026", + photoUrls: [], + tags: [], + status: "pending", + }, + { + id: 1003, + name: "Lucy", + photoUrls: ["url1"], + tags: [], + status: "pending", + }, + { + id: 5619, + category: { id: 5381, name: "string" }, + name: "doggie", + photoUrls: ["string", "string"], + tags: [ + { id: 9192, name: "string" }, + { id: 8297, name: "string" }, + ], + status: "pending", + }, + { + id: 100002, + name: "Simple Cat", + photoUrls: ["https://example.com/cat.jpg"], + tags: [], + status: "pending", + }, + { + id: 922337203685477600, + name: "chedder-89811222", + photoUrls: ["./dog.png"], + tags: [], + status: "pending", + }, + { + id: 1088293, + category: { id: 1, name: "Dogs" }, + name: "UpdatedWorkflowDog-1088293", + photoUrls: ["https://example.com/photo1.jpg"], + tags: [{ id: 1, name: "test-tag" }], + status: "pending", + }, + { + id: 745215, + category: { id: 1, name: "Dogs" }, + name: "UpdatedWorkflowDog-745215", + photoUrls: ["https://example.com/photo1.jpg"], + tags: [{ id: 1, name: "test-tag" }], + status: "pending", + }, + ], + { + "access-control-allow-headers": + "Content-Type, api_key, Authorization", + "access-control-allow-methods": "GET, POST, DELETE, PUT", + "access-control-allow-origin": "*", + connection: "keep-alive", + "content-type": "application/json", + date: "Fri, 02 Jan 2026 13:05:43 GMT", + server: "Jetty(9.2.9.v20150224)", + "transfer-encoding": "chunked", + }, + ); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + const expected = await arazzo.runWorkflows(inputFile); + console.log(expected); + } catch (err) { + expect(err).to.not.be.instanceOf(Error); + } }); - - it(`should throw an error when there is more than one sourceDescription and it is incorrectly referenced`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore-simple.json') - .reply(404); - - const inputFile = new Input('./test/mocks/validInputById.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/arazzoMockIncorrectlyReferencedSourceDescription.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`No known matching source description for $sourceDescriptions.Arazzo-Workflow-for-simple-petstore-openAP.getById`); - } + }); + + xdescribe(`multiple Steps in one workflow`, function () { + it(`returns successfully when there are no more steps to run`, async function () { + nock.recorder.rec(); + + // nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) + // .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') + // .reply(200, ["1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000"], { + // 'accept-ranges': 'bytes', + // 'access-control-allow-origin': '*', + // 'cache-control': 'max-age=300', + // connection: 'keep-alive', + // 'content-encoding': 'gzip', + // 'content-length': '3189', + // 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; sandbox", + // 'content-type': 'text/plain; charset=utf-8', + // 'cross-origin-resource-policy': 'cross-origin', + // date: 'Fri, 02 Jan 2026 12:03:42 GMT', + // etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + // expires: 'Fri, 02 Jan 2026 12:08:42 GMT', + // 'source-age': '0', + // 'strict-transport-security': 'max-age=31536000', + // vary: 'Authorization,Accept-Encoding', + // via: '1.1 varnish', + // 'x-cache': 'HIT', + // 'x-cache-hits': '1', + // 'x-content-type-options': 'nosniff', + // 'x-fastly-request-id': 'e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be', + // 'x-frame-options': 'deny', + // 'x-github-request-id': '9733:2549FA:1BB33B8:30A9114:6957ADA3', + // 'x-served-by': 'cache-lhr-egll1980089-LHR', + // 'x-timer': 'S1767355422.386688,VS0,VE1', + // 'x-xss-protection': '1; mode=block' + // }); + + // nock('http://petstore.swagger.io:80', {"encodedQueryParams":true}) + // .get('/v2/pet/findByStatus') + // .query({"status":"pending"}) + // .reply(200, [{"id":9515,"category":{"id":7792,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":7284,"name":"string"},{"id":9936,"name":"string"}],"status":"pending"},{"id":1109,"name":"Test9_02012026","photoUrls":[],"tags":[],"status":"pending"},{"id":1003,"name":"Lucy","photoUrls":["url1"],"tags":[],"status":"pending"},{"id":5619,"category":{"id":5381,"name":"string"},"name":"doggie","photoUrls":["string","string"],"tags":[{"id":9192,"name":"string"},{"id":8297,"name":"string"}],"status":"pending"},{"id":100002,"name":"Simple Cat","photoUrls":["https://example.com/cat.jpg"],"tags":[],"status":"pending"},{"id":922337203685477600,"name":"chedder-89811222","photoUrls":["./dog.png"],"tags":[],"status":"pending"},{"id":1088293,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-1088293","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"},{"id":745215,"category":{"id":1,"name":"Dogs"},"name":"UpdatedWorkflowDog-745215","photoUrls":["https://example.com/photo1.jpg"],"tags":[{"id":1,"name":"test-tag"}],"status":"pending"}], { + // 'access-control-allow-headers': 'Content-Type, api_key, Authorization', + // 'access-control-allow-methods': 'GET, POST, DELETE, PUT', + // 'access-control-allow-origin': '*', + // connection: 'keep-alive', + // 'content-type': 'application/json', + // date: 'Fri, 02 Jan 2026 13:05:43 GMT', + // server: 'Jetty(9.2.9.v20150224)', + // 'transfer-encoding': 'chunked' + // }); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMockWithTwoSteps.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + const expected = await arazzo.runWorkflows(inputFile); + console.log(expected); + } catch (err) { + expect(err).to.not.be.instanceOf(Error); + } }); + }); + + xit(`should throw an error when a step fails onFailure at step level is included and is set to retry and all retries are exhausted`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", + etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + nock("http://petstore.swagger.io:80", { encodedQueryParams: true }) + .get("/v2/pet/findByStatus") + .query({ status: "pending" }) + .times(3) + .reply(404); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMockWithRetryOnFailure.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + const spy = sinon.spy(arazzo, "runOperation"); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(spy.callCount).to.be.equal(3); + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `Call to GET http://petstore.swagger.io/v2/pet/findByStatus failed with a 404`, + ); + } - it(`should throw an error when loading a sourceDescription from a URL fails`, async function() { - nock('https://raw.githubusercontent.com:443', {"encodedQueryParams":true}) - .get('/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json') - .reply(404); - - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); - - const arazzo = new Arazzo('./test/mocks/workingArazzoMock.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); + spy.restore(); + }); + + xit(`should throw an error when a step fails and onFailure is omitted`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", + etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + nock("http://petstore.swagger.io:80", { encodedQueryParams: true }) + .get("/v2/pet/findByStatus") + .query({ status: "pending" }) + .reply(404); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `Call to GET http://petstore.swagger.io/v2/pet/findByStatus failed with a 404`, + ); + } + }); + + it(`should throw an error when the operationId does not exist in the OpenAPI document`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", + etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + const missingOperationIdOpenAPIFile = structuredClone(openAPIMock); + delete missingOperationIdOpenAPIFile.paths["/pet/findByStatus"]; + + const stub = sinon.stub(OpenAPI.prototype, "writeDocument").resolves(); + OpenAPI.prototype.fileName = + "Arazzo-Workflow-for-Petstore-openAPI.json"; + OpenAPI.prototype.filePath = path.resolve( + __dirname, + "../..", + "Arazzo-Workflow-for-Petstore-openAPI.json", + ); + await fsp.writeFile( + "./Arazzo-Workflow-for-Petstore-openAPI.json", + JSON.stringify(missingOperationIdOpenAPIFile), + ); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `The OperationId: findPetsByStatus does not exist`, + ); + } - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`Error fetching document from https://raw.githubusercontent.com/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json`); - } - }); + stub.restore(); + }); + + xit(`should throw an error if redocly can not bundle the document`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", + etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + const stub = sinon + .stub(bundleFromString) + .rejects( + new Error("sinon threw an error when bundling the document"), + ); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `sinon threw an error when bundling the document`, + ); + } - it(`should throw an error when a workflow does not have steps`, async function() { - const inputFile = new Input('./test/mocks/validInput.json', 'inputs'); + stub.restore(); + }); + }); - const arazzo = new Arazzo('./test/mocks/arazzoMockMissingSteps.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); + it(`should throw an error when writing a downloaded sourceDescription file fails`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply( + 200, + [ + "1f8b0800000000000013ed5ddd73dbb8117ff75fb1c3eb436fc696ec5cda074faf53274e3a9a4b134f1cb7d7c964ee607245e142020c004a5633fedf3b00488ae0b7be1c39c93df8241104f60bbf5d2c16c8e723008f27c84842bd73f07e1a9d8d4ebd63fd2b6553ee9dc3e72300002f40e90b9a28ca996ef76e46255009042489930841a298a3802b545271917f1f01fc97a7e0130653ca02e0a982583f26b7fae3f58284210a200adecf944acec763697f1a51fee1cfb59f7e042e8033784f853f9a0a44c6031c3154c7f043d6aae1ad3115fef8c711c04b2e4069c22dcdc7b0cc684b25829a219084c2475cc2ef32419f92e8e4232e7f07c541a154b645aa665cd0ff112d0898d248a190232330006f8e4266023a1b9d668204f0145511ea5f73867331150d50c4f2cdf41ac59cfaa6659d0bd3669cbfe173a688af0a05017818131ae97749421592f81fab973dd3e63e7b37a23e3289e5771989cdb01709f167084f0ada01bc5444258a168bc5889856232ec271d6971cbf9a3c7ff1fafac5c993d1e968a6e2281bf2281bd6b30621bd73786f9e7c6eee3ec924332a713e7fb2eaec83e90cef140a46a24beecb561b7dd96e71b914ddb15d7959b215091b68cec595a05ac9a932fc8b398aa59a511666632f796a35bf7aa5918f5e5e8af77b38c8655668be467fd908eb835ef83e4aa9edbf98d65c045a899d9da6b21070bdcf37090a3379642615b7f56602c9bad2f275391a2ea0c2b412a266259bd206599e280997ca252cb710630c1f4a23cb348e89581a4906011060b8800495019419d649adb0587ec473b14d0233c383e0aa647a866c416254d90c2b932150265c4f51876c00efe9e95f2a3fd56998b0398968009425a9f24a4def8bcff76596d14f0555cb62c6d8ff2a63e473fc378da646740b41159e27666e688a4960bf7c681cd1e5ee538a523de3c1b2cadf9f044e350f3f8c7d1e279c215372bc6a4f518eb5108faa03140c799ae7b5757d9304442110067847a5d2f33f7175355ccfa9e96b17aa3e1daceac925c83449228a81a3f063b7c3a7bd1d5ea102c6154c79ca3abbea37c37f6bcaaccfc53b1fedefdf88353aae5bc3d1588752cf96d78aa8b4ac692fc40dcc5503a934000fb74b90b6d37663fd571a296a023ed312e6244a519a28ea1621117c4e030c6041d50c7c1ec704246a835518805482b25076d8bbe64c935270d76af6edfa5cf9b64a0fe629355c7c4a512cab8f2a9c5e3b0caa1951c010038ddeb7083e6792062830802917592458ed51ab970ad4ac299162e529de25110fb0f9a1f4671893dac4d04a5d26863f2204a9f2a019541857e77fe54dab87daab9a2496c6c660c89cd088dc46a86740822c302f80277914942da924bc29492365082bdead34bb3f6afbd6369b5ae0ecc9003893a9895ea66904859155f5a3836864aa49ca4423a06fde1adfc551b3405b9504fd8a826e65412b66d8412b68d12cd6e65fee6b365366f60f69e4f7f570db6e74153734dc4596b1ef60dc509fbf78679dc12ebd8579a3d3571857a19bade521467023cd6b67c7faef13f3f72703b47a254e59381ae444de55e85bd78554f883f51c881e5d7b0beb1be0b6d6feb0ddc377f03e4838fb0ede7539ac05de8a840786dc0e8226027d8d85d9bc6f47f5cf09aa4970bf1348376989db254c2e3be0fc2daa543093f3a52c8cb0baacadc07088ea0ad5b3e524d806820d93cd18ac33353d103cb9043ecd732ec2d0bf1e08f7e32c650ac35ae80fe04db98889ca9afcf5a9d76eca5f237eae8f070f83743ba06b0f98f4d03997d56a6db01de5f6b82bd42409fded239a463d01ed2a1db751eed5e6e3346c691ca06c957bb531a89ea7101045b64cd1fd87aad94b2ee28340bb3c5b21b37485a5b2d6f5a303bfaf3d67dd06ca2e249f2c168b132de2935444c87c1e1815d6d4d40185850af9ed1fe8aba6645022b4a92b5ad342d12233db9690b0a2163b0f03d02f693bd5d3d08d201ae96b5e8d409383b03c57d3a2c3a8ca96f43ba16b1b177254fdb482bf002354b83e005e9af73200dc0ce4ecd09dfb10fde096237e23bccd90049d09d42989e4fa00d5a4a55697bd1750d61e999aac71a6bfc70fc10715d77c494c3f2affd2b4381ba749c449308949e8945b6c16c9d8cea4de5aa4a6c70d0316ddcb4b1ae141442a3c8b4d1efdbc78e075d95e164017097ddb10e7d76553ffdee2c51e75a0c57d85ea442a8124de34be6adf6c5b19e22d657a8a6f1325b810642438a66c8e4c715166be234764eb515ab0274ffed83594ed97e2b03de355e22826899ef859b0a5e356b340f99412a6a889343bf3499382a1edca211ec954ed0dd1c15402513d0889aefac2f501e098357320f2a7277b0c72f79836689a11a67c6da023ee9c0e5711f14d8d8fe9d1ec4c6d115e27bab737c20d801f8161ef233768c5d06f5d0f9f1d1c4ad9b0c5c65a71746de8e629b4adbf6b115ba79f5b5f6443055491859d6966aad89a9b2415fe8ce8bd88c6357b35821ce633cd28e3cfe67f8377573aa1c2eeaf585ab302dadead165da96e359f4f75506209196cc3e452dadca5d9c782bfff0c674058007ffb19ce4e47f046cd50e4854b0b1a45102243bbc35e94cff5b85aa3b96d376f3229ee23a13945e5cfbe6042b3d620a68cc6a67aeaacfe8cdce5cf4e1fe972e33bd43fb6ad20c3436fd2e4a8d27d7796b113e86c9e715f50977049159d63f1c4e0db085e6348ccef5c33cb4e9cc715f8838bab09a0105c74a19f95404f38f650e0a73d9b15640d022d9d870981bb42b9473073dc28c21c6719b6c0304d5be6d27381da5e9dc3312da7f074611d67d1d2980467680af36608110f43d4bb60a697ae8239df0c7623b75e7e6cb0adbcf24b8f2fba3512db4d706bf51d185541c37a7fdd6056f733b67ad5fbd217a6ae6b57662921a2526974d26d337c0ee91c99dd73856a19d91a4be1952dca82f089d9c87da4a639e0dc87e6d66a684db5bea28e161f8d5635dddf95ea2835e2212da353c76ab34b95af78282d8850961f335c4a85f1667a3354753b86fe404893633e374642832aa967d615dada01bdf6b7e2da71e4b393cd6222e5828b96b06f28b3792f2b5eb51ff72324ba02feae9a0bde3fe7eb456dbb5a9bdaa280a67cb6f7ebc95ba2f0e4158d69d3dab53e9e4fa2486f5f0898e973c2248af802833c58aa045afda273c4d7954eef4fa6f7af777f3d7971975081f2e462aa9cf8b29d5f73089532b879f71c163364a0f847d4c7274d479bf3daba9fe5b0aa473f51345e7f1bf141d3137dc53c0f5b7d3f988a8131e63a8ba71ca4c705f434aea586fa32ee9c9cdec699e983fd7e2a04325559d28044295dd458cfb5f1541de6a2a743b69f73450d4c4d77c9f79f68af5fd02058b8d7cda419a211e5b3e5eb4a17bb0d1606a44db4fb3461425bcad81e2ed3e39c3927cae01b71aa5f30e1dbb4603e887cef40c2f688bb3b4a5dddc84d72beedf75c74c1475e3afb30492a5b25b6dfb5c8007871a165bfd5f53b069075cdf2214df21b49f395a7cc6669be61fb345dd336dba67998596ba9fce2b3b63d28d8d326ca179eba5fcaa31ce57fede569abc9540c93c9ceb989e04da52aababf4adfd548a47eb67607a56e85db5c0aea46cb9f3debacf0a20ab80b7e6086e72c11d41ce6872492a80010d26db3a40e392be3248f3d99b9e211a77df6af7ff98a6c5fd34b60c48d70393445f29613f0718d1b9be93c7ad0d7668d43659434e87ca5bce2324b5b879b5ce3347517afd583d802ef0ac525e5007f7e74461e816ee1ee0a4683cfed501816bcba9104387a86ee4a16347e1bdd61195d3c3940aa95e6fd54544b6ed21bf5e74c3d78bc4f8e63dcc38db827ead86da9d69953e06a26c3780198f998db48df5bb7167ddf2df91f0b00d7feff8a025d021a0ab5a56ac5540a578efbdedfed8189ce23722924e28bd5759d6c25012bc61d1b2ba1ca839b5267f01bd2b9b3abe6ea4c2aa93be33d72b9b98818721ed081856126e1da3e94a9a069b28535b745b2f515a089224f5e5558db28e0b6f36db2eccd7663be45291fa36cc4e18ecb41967ce75b3bc93705057ff66e785ca7737b44686dd37176e83363d97c596cfce6d0dcbbebdf96b4f8b81ac9f0d1d698c52bae759fb3ba8cbeda8d4b1e7944e94978635086f4e01f527803ad6eceb5d09d36eee7dd9f3fd90d068c50da7a7adf5d5132024086c1eb3f14aeae67c941b7d578bcbf6a3a5cdefb5db65726f88b45f95caa8ea49be36913af3213f0b77ada9746744f57c6c13d0e847e528d59b467c510f5762ad8d86f209cff9570e6e7aefe737e38d034a225e4373e9f3a4f14462f958ef3978310fe874a9cf7a189c3777e513dfe729ab1f7c2c9d013eb75f6c7bf3cb10ddd5cc7875aab0419c24a1bf38d75674dc68e1de66e1ea56a7c58eee8ffe0fa561a61773630000", + ], + { + "accept-ranges": "bytes", + "access-control-allow-origin": "*", + "cache-control": "max-age=300", + connection: "keep-alive", + "content-encoding": "gzip", + "content-length": "3189", + "content-security-policy": + "default-src 'none'; style-src 'unsafe-inline'; sandbox", + "content-type": "text/plain; charset=utf-8", + "cross-origin-resource-policy": "cross-origin", + date: "Fri, 02 Jan 2026 12:03:42 GMT", + etag: 'W/"6f618be854ed64cd6a2cb850eefecf34866e637d9e73287a9d9d64a8ca3d54b3"', + expires: "Fri, 02 Jan 2026 12:08:42 GMT", + "source-age": "0", + "strict-transport-security": "max-age=31536000", + vary: "Authorization,Accept-Encoding", + via: "1.1 varnish", + "x-cache": "HIT", + "x-cache-hits": "1", + "x-content-type-options": "nosniff", + "x-fastly-request-id": "e357bbd9481ed9b72541f1d5e4d6c921c6dbf9be", + "x-frame-options": "deny", + "x-github-request-id": "9733:2549FA:1BB33B8:30A9114:6957ADA3", + "x-served-by": "cache-lhr-egll1980089-LHR", + "x-timer": "S1767355422.386688,VS0,VE1", + "x-xss-protection": "1; mode=block", + }, + ); + + const stub = sinon + .stub(fsp, "writeFile") + .rejects( + new Error( + "sinon threw an error when writing a sourceDescription file", + ), + ); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `sinon threw an error when writing a sourceDescription file`, + ); + } + + stub.restore(); + }); - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal(`Cannot read properties of undefined (reading '0')`); - } - }); + it(`should throw an error when there is more than one sourceDescription and it is incorrectly referenced`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get( + "/readmeio/oas-examples/refs/heads/main/3.1/json/petstore-simple.json", + ) + .reply(404); + + const inputFile = new Input("./test/mocks/validInputById.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/arazzoMockIncorrectlyReferencedSourceDescription.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `No known matching source description for $sourceDescriptions.Arazzo-Workflow-for-simple-petstore-openAP.getById`, + ); + } + }); - it(`should throw an error when an invalid input file is attached and does not conform to the workflow schema`, async function() { - const inputFile = new Input('./test/mocks/invalidInput.json', 'inputs'); + it(`should throw an error when loading a sourceDescription from a URL fails`, async function () { + nock("https://raw.githubusercontent.com:443", { + encodedQueryParams: true, + }) + .get("/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json") + .reply(404); + + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/workingArazzoMock.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `Error fetching document from https://raw.githubusercontent.com/readmeio/oas-examples/refs/heads/main/3.1/json/petstore.json`, + ); + } + }); - const arazzo = new Arazzo('./test/mocks/arazzoMockWithInvalidInputs.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); + it(`should throw an error when a workflow does not have steps`, async function () { + const inputFile = new Input("./test/mocks/validInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/arazzoMockMissingSteps.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + `Cannot read properties of undefined (reading '0')`, + ); + } + }); - try { - await arazzo.runWorkflows(inputFile); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal('Input values do not match Input schema'); - } - }); + it(`should throw an error when an invalid input file is attached and does not conform to the workflow schema`, async function () { + const inputFile = new Input("./test/mocks/invalidInput.json", "inputs"); + + const arazzo = new Arazzo( + "./test/mocks/arazzoMockWithInvalidInputs.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(inputFile); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal( + "Input values do not match Input schema", + ); + } + }); - it(`should throw an error when workflows are omitted`, async function() { - const arazzo = new Arazzo('./test/mocks/arazzoMockMissingWorkflows.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal('Missing Workflows'); - } - }); + it(`should throw an error when workflows are omitted`, async function () { + const arazzo = new Arazzo( + "./test/mocks/arazzoMockMissingWorkflows.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal("Missing Workflows"); + } + }); - it(`should throw an error when sourceDescriptions are omitted`, async function() { - const arazzo = new Arazzo('./test/mocks/arazzoMockMissingSourceDescriptions.json', 'arazzo', {logger: logger, parser}); - arazzo.setMainArazzo(); - - try { - await arazzo.runWorkflows(); - throw new Error('Expected promise to reject but it resolved'); - } catch (err) { - expect(err).to.be.instanceOf(Error); - expect(err.message).to.be.equal('Missing Source Descriptions'); - } - }); + it(`should throw an error when sourceDescriptions are omitted`, async function () { + const arazzo = new Arazzo( + "./test/mocks/arazzoMockMissingSourceDescriptions.json", + "arazzo", + { logger: logger, parser }, + ); + arazzo.setMainArazzo(); + + try { + await arazzo.runWorkflows(); + throw new Error("Expected promise to reject but it resolved"); + } catch (err) { + expect(err).to.be.instanceOf(Error); + expect(err.message).to.be.equal("Missing Source Descriptions"); + } }); + }); }); diff --git a/test/unit/ArazzoGenerator.spec.js b/test/unit/ArazzoGenerator.spec.js index 8d07ca6..c0ea203 100644 --- a/test/unit/ArazzoGenerator.spec.js +++ b/test/unit/ArazzoGenerator.spec.js @@ -1,264 +1,282 @@ -'use strict'; +"use strict"; const expect = require("chai").expect; -const sinon = require('sinon'); +const sinon = require("sinon"); -const ArazzoGenerator = require('../../src/ArazzoGenerator.js'); +const ArazzoGenerator = require("../../src/ArazzoGenerator.js"); -const mockArazzo = require('../mocks/arazzoMock.json'); -const mockArazzoEvents = require('../mocks/arazzoMockEvents.json'); -const mockEvents = require('../mocks/mockEvents.json') -const sls = require('../mocks/sls.js') +const mockArazzo = require("../mocks/arazzoMock.json"); +const mockArazzoEvents = require("../mocks/arazzoMockEvents.json"); +const mockEvents = require("../mocks/mockEvents.json"); +const sls = require("../mocks/sls.js"); describe(`Arazzo Generator`, function () { - const options = { - arazzo: '1.0.1', - sourceFile: './openapi.json', - sls - } - - describe(`constructor`, function () { - it(`generate an instance of Arazzo Generator`, function() { - const expected = new ArazzoGenerator(mockArazzo, options); - - expect(expected).to.be.instanceOf(ArazzoGenerator); - expect(expected).to.have.property('arazzo'); - expect(expected.arazzo).to.have.property('arazzo'); - }); + const options = { + arazzo: "1.0.1", + sourceFile: "./openapi.json", + sls, + }; + + describe(`constructor`, function () { + it(`generate an instance of Arazzo Generator`, function () { + const expected = new ArazzoGenerator(mockArazzo, options); + + expect(expected).to.be.instanceOf(ArazzoGenerator); + expect(expected).to.have.property("arazzo"); + expect(expected.arazzo).to.have.property("arazzo"); }); - - describe(`parse`, function () { - it(`generates an expected azarro specification from configuration on the custom property`, function() { - const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - - azarroGenerator.parse() - - expect(azarroGenerator.arazzo.info).to.have.property('title', 'Arazzo Pet Store'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.be.an('array'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(1); - expect(azarroGenerator.arazzo.workflows).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows).to.have.lengthOf(1); - expect(azarroGenerator.arazzo.workflows[0]).to.have.property('steps'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); - }); - - it(`generates an expected azarro specification from configuration on the functions property`, function() { - const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); - - azarroGenerator.parse() - - expect(azarroGenerator.arazzo.info).to.have.property('title', 'Arazzo Pet Store'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.be.an('array'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(1); - expect(azarroGenerator.arazzo.workflows).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows).to.have.lengthOf(1); - expect(azarroGenerator.arazzo.workflows[0]).to.have.property('steps'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); - }); + }); + + describe(`parse`, function () { + it(`generates an expected azarro specification from configuration on the custom property`, function () { + const azarroGenerator = new ArazzoGenerator(mockArazzo, options); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo.info).to.have.property( + "title", + "Arazzo Pet Store", + ); + expect(azarroGenerator.arazzo.sourceDescriptions).to.be.an("array"); + expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(1); + expect(azarroGenerator.arazzo.workflows).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows).to.have.lengthOf(1); + expect(azarroGenerator.arazzo.workflows[0]).to.have.property("steps"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); }); - describe(`generateInfo`, function () { - it(`Uses the serverless service as the title if not defined`, function() { - const infoTestingMock = structuredClone(mockArazzo) - delete infoTestingMock.info.title; - - const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - - azarroGenerator.parse() - - - expect(azarroGenerator.arazzo.info).to.have.property('title', 'Test API'); - }); - - it(`does not generate a description if not defined`, function() { - const infoTestingMock = structuredClone(mockArazzo) - delete infoTestingMock.info.description; - - const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - - azarroGenerator.parse() - - - expect(azarroGenerator.arazzo.info).to.not.have.property('description'); - }); - - it(`does not generate a sumamry if not defined`, function() { - const infoTestingMock = structuredClone(mockArazzo) - delete infoTestingMock.info.summary; - - const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - - azarroGenerator.parse() - - - expect(azarroGenerator.arazzo.info).to.have.property('summary'); - }); - - it(`uses a UUID of a version is not defined`, function() { - const infoTestingMock = structuredClone(mockArazzo) - delete infoTestingMock.info.version; - const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - - - azarroGenerator.parse() - - - expect(azarroGenerator.arazzo.info).to.have.property('version'); - }); - - it(`extends the info definition when extended properties are passed in`, function() { - const infoTestingMock = structuredClone(mockArazzo) - infoTestingMock.info['x-extended-test'] = 'extended test'; - const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - - - azarroGenerator.parse() - - - expect(azarroGenerator.arazzo.info).to.have.property('x-extended-test'); - }); + it(`generates an expected azarro specification from configuration on the functions property`, function () { + const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo.info).to.have.property( + "title", + "Arazzo Pet Store", + ); + expect(azarroGenerator.arazzo.sourceDescriptions).to.be.an("array"); + expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(1); + expect(azarroGenerator.arazzo.workflows).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows).to.have.lengthOf(1); + expect(azarroGenerator.arazzo.workflows[0]).to.have.property("steps"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); }); + }); - describe(`generateSourceDescriptions`, function () { - it(`generates a default sourceDescription`, function() { - const azarroGenerator = new ArazzoGenerator(mockArazzo, options); + describe(`generateInfo`, function () { + it(`Uses the serverless service as the title if not defined`, function () { + const infoTestingMock = structuredClone(mockArazzo); + delete infoTestingMock.info.title; - azarroGenerator.parse() + const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); + azarroGenerator.parse(); - expect(azarroGenerator.arazzo).to.have.property('sourceDescriptions'); - }); - - it(`generates a default sourceDescription with user provided ones when documented`, function() { - const sourceDescriptionTestingMock = structuredClone(mockArazzo) - sourceDescriptionTestingMock.sourceDescriptions = [ - { - name: 'LoginOpenAPI', - url: './login-openapi.json', - type: 'openapi' - } - ] - const azarroGenerator = new ArazzoGenerator(sourceDescriptionTestingMock, options); - - azarroGenerator.parse() - + expect(azarroGenerator.arazzo.info).to.have.property("title", "Test API"); + }); - expect(azarroGenerator.arazzo).to.have.property('sourceDescriptions'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(2); - }); + it(`does not generate a description if not defined`, function () { + const infoTestingMock = structuredClone(mockArazzo); + delete infoTestingMock.info.description; - it(`generates a default sourceDescription with user provided ones when documented with extended fields`, function() { - const sourceDescriptionTestingMock = structuredClone(mockArazzo) - sourceDescriptionTestingMock.sourceDescriptions = [ - { - name: 'LoginOpenAPI', - url: './login-openapi.json', - type: 'openapi', - 'x-extended-field': 'extended test' - } - ] - const azarroGenerator = new ArazzoGenerator(sourceDescriptionTestingMock, options); + const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - azarroGenerator.parse() + azarroGenerator.parse(); + expect(azarroGenerator.arazzo.info).to.not.have.property("description"); + }); - expect(azarroGenerator.arazzo).to.have.property('sourceDescriptions'); - expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(2); + it(`does not generate a sumamry if not defined`, function () { + const infoTestingMock = structuredClone(mockArazzo); + delete infoTestingMock.info.summary; - let hasExtendedField = false; - for (const sourceDescription of azarroGenerator.arazzo.sourceDescriptions) { - if (sourceDescription.hasOwnProperty('x-extended-field')) { - hasExtendedField = true - } - } + const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - expect(hasExtendedField).to.be.true; + azarroGenerator.parse(); - }); + expect(azarroGenerator.arazzo.info).to.have.property("summary"); }); - describe(`generateWorkflows`, function () { - it(`generates the expected workflows`, function() { - const azarroGenerator = new ArazzoGenerator(mockArazzo, options); + it(`uses a UUID of a version is not defined`, function () { + const infoTestingMock = structuredClone(mockArazzo); + delete infoTestingMock.info.version; + const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - azarroGenerator.parse() + azarroGenerator.parse(); - expect(azarroGenerator.arazzo).to.have.property('workflows'); - expect(azarroGenerator.arazzo.workflows).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0]).to.have.property('steps'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - }); - - it(`generates the expected steps`, function() { - const azarroGenerator = new ArazzoGenerator(mockArazzo, options); + expect(azarroGenerator.arazzo.info).to.have.property("version"); + }); - azarroGenerator.parse() + it(`extends the info definition when extended properties are passed in`, function () { + const infoTestingMock = structuredClone(mockArazzo); + infoTestingMock.info["x-extended-test"] = "extended test"; + const azarroGenerator = new ArazzoGenerator(infoTestingMock, options); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); - const expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; - expect(expectedStep).to.have.property('successCriteria') - expect(expectedStep).to.have.property('outputs') - expect(expectedStep.outputs).to.have.property('token') - }); + azarroGenerator.parse(); - it(`generates the expected steps in the correct order when using event configuration`, function() { - const getAllFunctionsStub = sinon.stub(sls.service, 'getAllFunctions').returns (['getPet', 'login']); - const getFunctionStub = sinon.stub(sls.service, 'getFunction').onFirstCall().returns(mockEvents[0]).onSecondCall().returns(mockEvents[1]); + expect(azarroGenerator.arazzo.info).to.have.property("x-extended-test"); + }); + }); - const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); + describe(`generateSourceDescriptions`, function () { + it(`generates a default sourceDescription`, function () { + const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - azarroGenerator.parse() + azarroGenerator.parse(); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(2); - let expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; - expect(expectedStep).to.have.property('requestBody'); - expect(expectedStep).to.not.have.property('stepNumber'); - expectedStep = azarroGenerator.arazzo.workflows[0].steps[1]; - expect(expectedStep).to.have.property('parameters'); - expect(expectedStep).to.not.have.property('stepNumber'); + expect(azarroGenerator.arazzo).to.have.property("sourceDescriptions"); + }); + it(`generates a default sourceDescription with user provided ones when documented`, function () { + const sourceDescriptionTestingMock = structuredClone(mockArazzo); + sourceDescriptionTestingMock.sourceDescriptions = [ + { + name: "LoginOpenAPI", + url: "./login-openapi.json", + type: "openapi", + }, + ]; + const azarroGenerator = new ArazzoGenerator( + sourceDescriptionTestingMock, + options, + ); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo).to.have.property("sourceDescriptions"); + expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(2); + }); - getAllFunctionsStub.restore(); - getFunctionStub.restore(); - }); + it(`generates a default sourceDescription with user provided ones when documented with extended fields`, function () { + const sourceDescriptionTestingMock = structuredClone(mockArazzo); + sourceDescriptionTestingMock.sourceDescriptions = [ + { + name: "LoginOpenAPI", + url: "./login-openapi.json", + type: "openapi", + "x-extended-field": "extended test", + }, + ]; + const azarroGenerator = new ArazzoGenerator( + sourceDescriptionTestingMock, + options, + ); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo).to.have.property("sourceDescriptions"); + expect(azarroGenerator.arazzo.sourceDescriptions).to.have.lengthOf(2); + + let hasExtendedField = false; + for (const sourceDescription of azarroGenerator.arazzo + .sourceDescriptions) { + if (sourceDescription.hasOwnProperty("x-extended-field")) { + hasExtendedField = true; + } + } + + expect(hasExtendedField).to.be.true; + }); + }); - it(`generates the expected steps in the correct order when using event configuration and ignore unviable operations`, function() { - const mockEventsWithUnviableOperations = structuredClone(mockEvents); - Object.assign(mockEventsWithUnviableOperations.at(1).events.at(0), {sqs: {arn: 'abc', enabled: true}}) + describe(`generateWorkflows`, function () { + it(`generates the expected workflows`, function () { + const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - const getAllFunctionsStub = sinon.stub(sls.service, 'getAllFunctions').returns (['getPet', 'login']); - const getFunctionStub = sinon.stub(sls.service, 'getFunction').onFirstCall().returns(mockEvents[0]).onSecondCall().returns(mockEvents[1]); + azarroGenerator.parse(); - const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); + expect(azarroGenerator.arazzo).to.have.property("workflows"); + expect(azarroGenerator.arazzo.workflows).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0]).to.have.property("steps"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + }); - azarroGenerator.parse() + it(`generates the expected steps`, function () { + const azarroGenerator = new ArazzoGenerator(mockArazzo, options); - expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an('array'); - expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(2); - let expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; - expect(expectedStep).to.have.property('requestBody'); - expectedStep = azarroGenerator.arazzo.workflows[0].steps[1]; - expect(expectedStep).to.have.property('parameters'); + azarroGenerator.parse(); + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(1); + const expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; + expect(expectedStep).to.have.property("successCriteria"); + expect(expectedStep).to.have.property("outputs"); + expect(expectedStep.outputs).to.have.property("token"); + }); - getAllFunctionsStub.restore(); - getFunctionStub.restore(); - }); + it(`generates the expected steps in the correct order when using event configuration`, function () { + const getAllFunctionsStub = sinon + .stub(sls.service, "getAllFunctions") + .returns(["getPet", "login"]); + const getFunctionStub = sinon + .stub(sls.service, "getFunction") + .onFirstCall() + .returns(mockEvents[0]) + .onSecondCall() + .returns(mockEvents[1]); + + const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(2); + let expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; + expect(expectedStep).to.have.property("requestBody"); + expect(expectedStep).to.not.have.property("stepNumber"); + expectedStep = azarroGenerator.arazzo.workflows[0].steps[1]; + expect(expectedStep).to.have.property("parameters"); + expect(expectedStep).to.not.have.property("stepNumber"); + + getAllFunctionsStub.restore(); + getFunctionStub.restore(); + }); - xit(`throws an error when an inputs schema is invalid`, function() { - const invalidSchemaTest = structuredClone(mockArazzo); - invalidSchemaTest.workflows[0].inputs = {typpes: 'object', properties: {string: {type: 'string'}}} + it(`generates the expected steps in the correct order when using event configuration and ignore unviable operations`, function () { + const mockEventsWithUnviableOperations = structuredClone(mockEvents); + Object.assign(mockEventsWithUnviableOperations.at(1).events.at(0), { + sqs: { arn: "abc", enabled: true }, + }); + + const getAllFunctionsStub = sinon + .stub(sls.service, "getAllFunctions") + .returns(["getPet", "login"]); + const getFunctionStub = sinon + .stub(sls.service, "getFunction") + .onFirstCall() + .returns(mockEvents[0]) + .onSecondCall() + .returns(mockEvents[1]); + + const azarroGenerator = new ArazzoGenerator(mockArazzoEvents, options); + + azarroGenerator.parse(); + + expect(azarroGenerator.arazzo.workflows[0].steps).to.be.an("array"); + expect(azarroGenerator.arazzo.workflows[0].steps).to.have.lengthOf(2); + let expectedStep = azarroGenerator.arazzo.workflows[0].steps[0]; + expect(expectedStep).to.have.property("requestBody"); + expectedStep = azarroGenerator.arazzo.workflows[0].steps[1]; + expect(expectedStep).to.have.property("parameters"); + + getAllFunctionsStub.restore(); + getFunctionStub.restore(); + }); - const azarroGenerator = new ArazzoGenerator(invalidSchemaTest, options); + xit(`throws an error when an inputs schema is invalid`, function () { + const invalidSchemaTest = structuredClone(mockArazzo); + invalidSchemaTest.workflows[0].inputs = { + typpes: "object", + properties: { string: { type: "string" } }, + }; - azarroGenerator.parse() + const azarroGenerator = new ArazzoGenerator(invalidSchemaTest, options); + azarroGenerator.parse(); - expect(azarroGenerator.arazzo).to.have.property('workflows'); - }); + expect(azarroGenerator.arazzo).to.have.property("workflows"); }); + }); }); diff --git a/test/unit/ArrazoRunner.spec.js b/test/unit/ArrazoRunner.spec.js index 3639cb1..c60e01f 100644 --- a/test/unit/ArrazoRunner.spec.js +++ b/test/unit/ArrazoRunner.spec.js @@ -1,66 +1,81 @@ -'use strict'; +"use strict"; const expect = require("chai").expect; -const sinon = require('sinon'); +const sinon = require("sinon"); -const fsp = require('node:fs/promises'); +const fsp = require("node:fs/promises"); -const Arazzo = require('../../src/Arazzo.js'); -const Logger = require('../../src/Logger.js'); -const ArazzoRunner = require('../../src/ArazzoRunner.js'); +const Arazzo = require("../../src/Arazzo.js"); +const Logger = require("../../src/Logger.js"); +const ArazzoRunner = require("../../src/ArazzoRunner.js"); describe(`Arazzo Runner`, function () { - const logger = new Logger( - '3', - { - notice: (str) => {}, - error: (str) => {}, - success: (str) => {}, - verbose: (str) => {}, - } - ); - - describe(`constructor`, function () { - it(`correctly sets the path of the Arazzo Specification File when not passed in `, function() { - const expected = new ArazzoRunner(undefined, {logger, inputFile: './input.json'}); - - expect(expected.pathToArazzoSpecification).to.be.equal('./arazzo.json'); - }); - - it(`correctly sets the path of the Arazzo Specification File when passed in `, function() { - const expected = new ArazzoRunner('./my-arazzo.json', {logger, inputFile: './input.json'}); - - expect(expected.pathToArazzoSpecification).to.be.equal('./my-arazzo.json'); - }); + const logger = new Logger("3", { + notice: (str) => {}, + error: (str) => {}, + success: (str) => {}, + verbose: (str) => {}, + }); + + describe(`constructor`, function () { + it(`correctly sets the path of the Arazzo Specification File when not passed in `, function () { + const expected = new ArazzoRunner(undefined, { + logger, + inputFile: "./input.json", + }); + + expect(expected.pathToArazzoSpecification).to.be.equal("./arazzo.json"); }); - describe(`runArazzoWorkflows`, function () { - it(`should run an Arazzo Workflow as expected`, async function() { - const arazzoStub = sinon.stub(Arazzo.prototype, 'runWorkflows').resolves(); + it(`correctly sets the path of the Arazzo Specification File when passed in `, function () { + const expected = new ArazzoRunner("./my-arazzo.json", { + logger, + inputFile: "./input.json", + }); - const runner = new ArazzoRunner(undefined, {logger, inputFile: './input.json'}); - - try { - await runner.runArazzoWorkflows(); - } catch (err) { - throw new Error('Err not expected'); - } - - arazzoStub.restore(); - }); + expect(expected.pathToArazzoSpecification).to.be.equal( + "./my-arazzo.json", + ); + }); + }); + + describe(`runArazzoWorkflows`, function () { + it(`should run an Arazzo Workflow as expected`, async function () { + const arazzoStub = sinon + .stub(Arazzo.prototype, "runWorkflows") + .resolves(); + + const runner = new ArazzoRunner(undefined, { + logger, + inputFile: "./input.json", + }); + + try { + await runner.runArazzoWorkflows(); + } catch (err) { + throw new Error("Err not expected"); + } + + arazzoStub.restore(); + }); - it(`throws an error reading the peggy rules fails`, async function() { - const readFileStub = sinon.stub(fsp, 'readFile').rejects(new Error('Error thrown from sinon')); + it(`throws an error reading the peggy rules fails`, async function () { + const readFileStub = sinon + .stub(fsp, "readFile") + .rejects(new Error("Error thrown from sinon")); - const runner = new ArazzoRunner(undefined, {logger, inputFile: './input.json'}); + const runner = new ArazzoRunner(undefined, { + logger, + inputFile: "./input.json", + }); - try { - await runner.runArazzoWorkflows(); - } catch (err) { - expect(err).to.be.instanceOf(Error); - } + try { + await runner.runArazzoWorkflows(); + } catch (err) { + expect(err).to.be.instanceOf(Error); + } - readFileStub.restore(); - }); + readFileStub.restore(); }); + }); }); diff --git a/test/unit/arazzoPlugin.spec.js b/test/unit/arazzoPlugin.spec.js index 4324435..1b4869d 100644 --- a/test/unit/arazzoPlugin.spec.js +++ b/test/unit/arazzoPlugin.spec.js @@ -1,197 +1,206 @@ -'use strict'; +"use strict"; const expect = require("chai").expect; -const sinon = require('sinon'); +const sinon = require("sinon"); -const ArazzoPlugin = require('../../src/ArazzoPlugin.js'); -const ArazzoRunner = require('../../src/ArazzoRunner.js'); +const ArazzoPlugin = require("../../src/ArazzoPlugin.js"); +const ArazzoRunner = require("../../src/ArazzoRunner.js"); -const arazzoMock = require('../mocks/arazzoMock.json'); +const arazzoMock = require("../mocks/arazzoMock.json"); describe(`Arazzo Plugin`, function () { - let sls, logOutput; - - beforeEach(function() { - sls = { - version: '3.4.0', - service: { - service: "test-service", - provider: { - stage: "test", - }, - getAllFunctions: () => {return []}, - getFunction: () => {}, - custom: { - arazzo: arazzoMock - } - }, - variables: { - service: { - custom: {} - }, - }, - classes: { - Error: class ServerlessError { - constructor(err) { - return new Error(err); - } - }, - }, - processedInput: { - options: { - format: "json", - }, - }, - configSchemaHandler: { - defineCustomProperties: () => {}, - }, - } - - logOutput = { - log: { - notice: (str) => {}, - error: (str) => {}, - success: (str) => {}, - verbose: (str) => {}, - }, - }; + let sls, logOutput; + + beforeEach(function () { + sls = { + version: "3.4.0", + service: { + service: "test-service", + provider: { + stage: "test", + }, + getAllFunctions: () => { + return []; + }, + getFunction: () => {}, + custom: { + arazzo: arazzoMock, + }, + }, + variables: { + service: { + custom: {}, + }, + }, + classes: { + Error: class ServerlessError { + constructor(err) { + return new Error(err); + } + }, + }, + processedInput: { + options: { + format: "json", + }, + }, + configSchemaHandler: { + defineCustomProperties: () => {}, + }, + }; + + logOutput = { + log: { + notice: (str) => {}, + error: (str) => {}, + success: (str) => {}, + verbose: (str) => {}, + }, + }; + }); + + describe(`constructor`, function () { + it(`generate an instance of ArazzoPlugin`, function () { + const expected = new ArazzoPlugin(sls, {}, logOutput); + + expect(expected).to.be.instanceOf(ArazzoPlugin); }); + }); - describe(`constructor`, function () { - it(`generate an instance of ArazzoPlugin`, function() { - const expected = new ArazzoPlugin(sls, {}, logOutput); + describe(`Arazzo Generation`, function () { + it(`should generate an Arazzo Specification`, async function () { + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - expect(expected).to.be.instanceOf(ArazzoPlugin); - }); - }); - - describe(`Arazzo Generation`, function () { - it(`should generate an Arazzo Specification`, async function() { - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + const writeFileStub = sinon + .stub(arazzoPlugin, "writeArazzoFile") + .resolves(); - const writeFileStub = sinon.stub(arazzoPlugin, 'writeArazzoFile').resolves(); + arazzoPlugin.processCLIInput(); - arazzoPlugin.processCLIInput(); + await arazzoPlugin.arazzoGeneration().catch((err) => { + console.error(err); + }); - await arazzoPlugin.arazzoGeneration().catch(err => { - console.error(err); - }); + expect(writeFileStub.called).to.be.true; - expect(writeFileStub.called).to.be.true; - - writeFileStub.restore(); - }); + writeFileStub.restore(); + }); - it(`should throw an error if writing the arazzo file rejects`, async function() { - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + it(`should throw an error if writing the arazzo file rejects`, async function () { + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - const writeFileStub = sinon.stub(arazzoPlugin, 'writeArazzoFile').rejects(new Error('Thrown from sinon')); + const writeFileStub = sinon + .stub(arazzoPlugin, "writeArazzoFile") + .rejects(new Error("Thrown from sinon")); - arazzoPlugin.processCLIInput(); + arazzoPlugin.processCLIInput(); - try { - await arazzoPlugin.arazzoGeneration(); - } catch (err) { - expect(err).to.be.instanceOf(Error); - } + try { + await arazzoPlugin.arazzoGeneration(); + } catch (err) { + expect(err).to.be.instanceOf(Error); + } - writeFileStub.restore(); - }); + writeFileStub.restore(); }); + }); - describe(`Arazzo Runner`, function () { - it(`should run an Arazzo Specification`, async function() { - const stub = sinon.stub(ArazzoRunner.prototype, 'runArazzoWorkflows').resolves() + describe(`Arazzo Runner`, function () { + it(`should run an Arazzo Specification`, async function () { + const stub = sinon + .stub(ArazzoRunner.prototype, "runArazzoWorkflows") + .resolves(); - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - await arazzoPlugin.run().catch(err => { - console.error(err); - }); + await arazzoPlugin.run().catch((err) => { + console.error(err); + }); - stub.restore(); - }); + stub.restore(); + }); - it(`should throw an error if the ArazzoRunner rejects`, async function() { - const stub = sinon.stub(ArazzoRunner.prototype, 'runArazzoWorkflows').rejects(new Error('Thrown from sinon')) + it(`should throw an error if the ArazzoRunner rejects`, async function () { + const stub = sinon + .stub(ArazzoRunner.prototype, "runArazzoWorkflows") + .rejects(new Error("Thrown from sinon")); - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - try { - await arazzoPlugin.run(); - } catch (err) { - expect(err).to.be.instanceOf(Error); - } + try { + await arazzoPlugin.run(); + } catch (err) { + expect(err).to.be.instanceOf(Error); + } - stub.restore(); - }); + stub.restore(); }); + }); - describe(`process CLI Input`, function () { - it(`should default the file output to arazzo.json`, function() { - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); - - expect(arazzoPlugin.config.file).to.be.eql('arazzo.json'); - }); - - it(`should set the user file output when set`, function() { - sls.processedInput.options.output = 'jared.json' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); - - expect(arazzoPlugin.config.file).to.be.eql('jared.json'); - }); + describe(`process CLI Input`, function () { + it(`should default the file output to arazzo.json`, function () { + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - it(`should set the user file output when set and using yaml`, function() { - sls.processedInput.options.output = 'jared.yml' - sls.processedInput.options.format = 'yaml' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); + expect(arazzoPlugin.config.file).to.be.eql("arazzo.json"); + }); - expect(arazzoPlugin.config.file).to.be.eql('jared.yml'); - }); + it(`should set the user file output when set`, function () { + sls.processedInput.options.output = "jared.json"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - it(`should correctly set the file output for json`, function() { - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); + expect(arazzoPlugin.config.file).to.be.eql("jared.json"); + }); - expect(arazzoPlugin.config.format).to.be.eql('json'); - }); + it(`should set the user file output when set and using yaml`, function () { + sls.processedInput.options.output = "jared.yml"; + sls.processedInput.options.format = "yaml"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - it(`should correctly set the file output for yaml`, function() { - sls.processedInput.options.format = 'yaml' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); + expect(arazzoPlugin.config.file).to.be.eql("jared.yml"); + }); + it(`should correctly set the file output for json`, function () { + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - expect(arazzoPlugin.config.format).to.be.eql('yml'); - }); + expect(arazzoPlugin.config.format).to.be.eql("json"); + }); - it(`should correctly set the openAPI source file`, function() { - sls.processedInput.options.source = 'openAPI2.json' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); + it(`should correctly set the file output for yaml`, function () { + sls.processedInput.options.format = "yaml"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); + expect(arazzoPlugin.config.format).to.be.eql("yml"); + }); - expect(arazzoPlugin.config.source).to.be.eql('openAPI2.json'); - }); + it(`should correctly set the openAPI source file`, function () { + sls.processedInput.options.source = "openAPI2.json"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - it(`should correctly set the input file`, function() { - sls.processedInput.options.input = 'inputs.json' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); + expect(arazzoPlugin.config.source).to.be.eql("openAPI2.json"); + }); + it(`should correctly set the input file`, function () { + sls.processedInput.options.input = "inputs.json"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); - expect(arazzoPlugin.config.input).to.be.eql('inputs.json'); - }); + expect(arazzoPlugin.config.input).to.be.eql("inputs.json"); + }); - it(`should throw an error if a file format other than yaml or json is tried`, function() { - expect(function () { - sls.processedInput.options.format = 'docx' - const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); - arazzoPlugin.processCLIInput(); - }).to.throw(`Invalid Output Format Specified - must be one of "yaml" or "json"`); - }); + it(`should throw an error if a file format other than yaml or json is tried`, function () { + expect(function () { + sls.processedInput.options.format = "docx"; + const arazzoPlugin = new ArazzoPlugin(sls, {}, logOutput); + arazzoPlugin.processCLIInput(); + }).to.throw( + `Invalid Output Format Specified - must be one of "yaml" or "json"`, + ); }); + }); });