UNPKG

@contract-case/case-core-plugin-function

Version:

ContractCase core function plugin, allowing validation of arbitrary functions

107 lines 6.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionResultMatcherExecutor = exports.isSuccessResult = void 0; const case_plugin_base_1 = require("@contract-case/case-plugin-base"); const entities_1 = require("../entities"); const isSuccessResult = (matcher) => // this has to be !== undefined, since it could legitimately be 'null', indicating a void return 'success' in matcher && matcher.success !== undefined; exports.isSuccessResult = isSuccessResult; const isFunctionFailure = (maybeFailure) => (0, entities_1.isObject)(maybeFailure) && 'errorClassName' in maybeFailure && typeof maybeFailure['errorClassName'] === 'string'; const strip = (matcher, matchContext) => (0, exports.isSuccessResult)(matcher) ? { // We have to stringify this, as the contract of `strip()` // is to return an example that would pass the matcher if it were // passed in as an actual to check success: JSON.stringify(matchContext.descendAndStrip(matcher.success, (0, case_plugin_base_1.addLocation)(`returnValue`, matchContext))), } : { errorClassName: matchContext.descendAndStrip(matcher.errorClassName, (0, case_plugin_base_1.addLocation)(`errorClassName`, matchContext)), ...('message' in matcher ? { message: matchContext.descendAndStrip(matcher.message, (0, case_plugin_base_1.addLocation)(`message`, matchContext)), } : {}), }; const describe = (matcher, context) => (0, exports.isSuccessResult)(matcher) ? `returns ${context.descendAndDescribe(matcher.success, (0, case_plugin_base_1.addLocation)(`returnValue`, context))}` : `throwing a ${JSON.parse(context.descendAndDescribe(matcher.errorClassName, (0, case_plugin_base_1.addLocation)(`errorClassName`, context)))}${'message' in matcher ? ` with message: ${context.descendAndDescribe(matcher.message, (0, case_plugin_base_1.addLocation)(`message`, context))}` : ''}`; const parseActualSuccess = (actual, matchContext) => { if (!(0, entities_1.isObject)(actual)) { throw new case_plugin_base_1.CaseCoreError(`FunctionResultMatcher check() received a non-object response from a function. This indicates a bug in the function wrapper lib. What was returned was: ${actual}`); } if (typeof actual['success'] !== 'string') { const message = "The function return value (success) wasn't a string. This is a bug in the language specific wrapper."; matchContext.logger.error(`${message} The actual value was:`, actual); throw new case_plugin_base_1.CaseCoreError(message, matchContext); } try { return JSON.parse(actual['success']); } catch (e) { const message = "The function return value didn't parse as JSON. This is a bug in the language specific wrapper."; matchContext.logger.error(`${message} The error was:`, e, 'The actual was:', actual['success']); throw new case_plugin_base_1.CaseCoreError(message, matchContext, e.stack); } }; const check = async (matcher, matchContext, actual) => Promise.resolve(() => { }).then(() => { if (!(0, entities_1.isObject)(actual)) { throw new case_plugin_base_1.CaseCoreError(`FunctionResultMatcher check() received a non-object response from a function. This indicates a bug in the function wrapper lib. What was returned was: ${actual}`); } if ((0, exports.isSuccessResult)(matcher)) { // We're expecting success if ('success' in actual) { const parsedActual = parseActualSuccess(actual, matchContext); return matchContext.descendAndCheck(matcher.success, (0, case_plugin_base_1.addLocation)(`returnValue`, matchContext), parsedActual); } // and it wasn't a success if (isFunctionFailure(actual)) { matchContext.logger.error(`Expected the function to return success, but it failed with an error (${actual.errorClassName})`); if (actual.stack) { matchContext.logger.error('Stack trace was:', actual.stack); } return [ (0, case_plugin_base_1.matchingError)(matcher, `Expected the function to return success, but it failed with an error`, actual, matchContext, matchContext.descendAndStrip(matcher, (0, case_plugin_base_1.addLocation)(':strippingExpected', matchContext)), { actual: 'Function threw an error', expected: 'Successfully returned', }), ]; } throw new case_plugin_base_1.CaseCoreError(`FunctionResultMatcher check() received an invalid response from a function. This indicates a bug in the function wrapper lib. What was returned was: ${actual}`); } else { // We're expecting failure if ('errorClassName' in actual) { return matchContext.descendAndCheck(matcher.errorClassName, (0, case_plugin_base_1.addLocation)(`thrownErrorKind`, matchContext), actual['errorClassName']); } // But it was a success return [ (0, case_plugin_base_1.matchingError)(matcher, `Expected the function to throw an error, but it returned successfully`, actual['success'], matchContext, matchContext.descendAndStrip(matcher, (0, case_plugin_base_1.addLocation)(':strippingExpected', matchContext)), { actual: 'Successfully returned', expected: 'Error thrown', }), ]; } }); const validate = (matcher, matchContext) => Promise.resolve().then(() => { if ((0, exports.isSuccessResult)(matcher)) { return matchContext.descendAndValidate(matcher.success, (0, case_plugin_base_1.addLocation)(`returnValue`, matchContext)); } if (!('errorClassName' in matcher)) { matchContext.logger.error(`FunctionSuccess or FunctionResult matcher must have a 'success' or an 'errorClassName' field. Matcher was:`, matcher); throw new case_plugin_base_1.CaseConfigurationError(`FunctionSuccess or FunctionResult matcher must have a 'success' or an 'errorClassName' field`, matchContext, 'BAD_INTERACTION_DEFINITION'); } return Promise.resolve() .then(() => matchContext.descendAndValidate(matcher.errorClassName, (0, case_plugin_base_1.addLocation)('errorClassName', matchContext))) .then(async () => { if ('message' in matcher && matcher.message != null) { await matchContext.descendAndValidate(matcher.errorClassName, (0, case_plugin_base_1.addLocation)('errorClassName', matchContext)); } }); }); exports.FunctionResultMatcherExecutor = { describe, check, strip, validate }; //# sourceMappingURL=FunctionResultMatcher.js.map