@contract-case/case-core-plugin-function
Version:
ContractCase core function plugin, allowing validation of arbitrary functions
107 lines • 6.72 kB
JavaScript
;
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