@pact-foundation/pact
Version:
Pact for all things Javascript
269 lines • 10.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsynchronousMessageWithTransport = exports.AsynchronousMessageWithPluginContents = exports.AsynchronousMessageWithContent = exports.AsynchronousMessageBuilder = exports.AsynchronousMessageWithPlugin = exports.UnconfiguredAsynchronousMessage = void 0;
const ramda_1 = require("ramda");
const logger_1 = __importDefault(require("../../common/logger"));
const matchingRules_1 = require("../../common/matchingRules");
const configurationError_1 = __importDefault(require("../../errors/configurationError"));
const display_1 = require("../../v3/display");
const defaultPactDir = './pacts';
class UnconfiguredAsynchronousMessage {
pact;
interaction;
opts;
cleanupFn;
message;
constructor(pact, interaction, opts, cleanupFn) {
this.pact = pact;
this.interaction = interaction;
this.opts = opts;
this.cleanupFn = cleanupFn;
this.message = {
requestIsBinary: false,
interaction: this.interaction,
};
}
expectsToReceive(description, builder) {
this.interaction.expectsToReceive(description);
builder(new AsynchronousMessageBuilder(this.pact, this.message, this.opts));
return new AsynchronousMessageWithContent(this.pact, this.message, this.opts, this.cleanupFn);
}
given(state, parameters) {
if (parameters) {
this.message.interaction.givenWithParams(state, JSON.stringify(parameters));
}
else {
this.message.interaction.given(state);
}
return this;
}
pending(pending = true) {
this.message.interaction.setPending(pending);
return this;
}
comment(comment) {
if (typeof comment === 'string') {
this.message.interaction.addTextComment(comment);
return this;
}
this.message.interaction.setComment(comment.key, comment.value);
return this;
}
testName(name) {
this.message.interaction.setInteractionTestName(name);
return this;
}
reference(group, name, value) {
this.message.interaction.addInteractionReference(group, name, value);
return this;
}
usingPlugin(config) {
this.pact.addPlugin(config.plugin, config.version);
return new AsynchronousMessageWithPlugin(this.pact, this.message, this.opts, this.cleanupFn);
}
}
exports.UnconfiguredAsynchronousMessage = UnconfiguredAsynchronousMessage;
class AsynchronousMessageWithPlugin {
pact;
message;
opts;
cleanupFn;
constructor(pact, message, opts, cleanupFn) {
this.pact = pact;
this.message = message;
this.opts = opts;
this.cleanupFn = cleanupFn;
}
expectsToReceive(description) {
this.message.interaction.expectsToReceive(description);
return this;
}
usingPlugin(config) {
this.pact.addPlugin(config.plugin, config.version);
return this;
}
withPluginContents(contents, contentType) {
this.message.interaction.withPluginRequestInteractionContents(contentType, contents);
return new AsynchronousMessageWithPluginContents(this.pact, this.message, this.opts, this.cleanupFn);
}
}
exports.AsynchronousMessageWithPlugin = AsynchronousMessageWithPlugin;
class AsynchronousMessageBuilder {
pact;
message;
opts;
constructor(pact, message, opts) {
this.pact = pact;
this.message = message;
this.opts = opts;
}
withMetadata(metadata) {
if ((0, ramda_1.isEmpty)(metadata)) {
throw new configurationError_1.default('You must provide valid metadata for the Message, or none at all');
}
(0, ramda_1.forEachObjIndexed)((v, k) => {
this.message.interaction.withMetadata(`${k}`, JSON.stringify(v));
}, metadata);
return this;
}
withContent(contentType, body) {
this.message.interaction.withBinaryContents(body, contentType);
this.message.requestIsBinary = true;
return this;
}
withJSONContent(content) {
if ((0, ramda_1.isEmpty)(content)) {
throw new configurationError_1.default('You must provide a valid JSON document or primitive for the Message.');
}
this.message.interaction.withContents(JSON.stringify(content), 'application/json');
return this;
}
withMatchingRules(rules) {
(0, matchingRules_1.validateRules)(rules);
const ffiRules = (0, matchingRules_1.convertRulesToFFI)(rules);
this.message.interaction.withMatchingRules(JSON.stringify(ffiRules));
return this;
}
}
exports.AsynchronousMessageBuilder = AsynchronousMessageBuilder;
class AsynchronousMessageWithContent {
pact;
message;
opts;
cleanupFn;
constructor(pact, message, opts, cleanupFn) {
this.pact = pact;
this.message = message;
this.opts = opts;
this.cleanupFn = cleanupFn;
}
executeTest(integrationTest) {
return executeNonTransportTest(this.pact, this.opts, this.message, integrationTest, this.cleanupFn);
}
}
exports.AsynchronousMessageWithContent = AsynchronousMessageWithContent;
class AsynchronousMessageWithPluginContents {
pact;
message;
opts;
cleanupFn;
constructor(pact, message, opts, cleanupFn) {
this.pact = pact;
this.message = message;
this.opts = opts;
this.cleanupFn = cleanupFn;
}
executeTest(integrationTest) {
return executeNonTransportTest(this.pact, this.opts, this.message, integrationTest, this.cleanupFn);
}
startTransport(transport, address, // IP Address or hostname
config) {
const port = this.pact.pactffiCreateMockServerForTransport(address, transport, config ? JSON.stringify(config) : '');
return new AsynchronousMessageWithTransport(this.pact, this.message, this.opts, port, address, this.cleanupFn);
}
}
exports.AsynchronousMessageWithPluginContents = AsynchronousMessageWithPluginContents;
class AsynchronousMessageWithTransport {
pact;
message;
opts;
port;
address;
cleanupFn;
constructor(pact, message, opts, port, address, cleanupFn) {
this.pact = pact;
this.message = message;
this.opts = opts;
this.port = port;
this.address = address;
this.cleanupFn = cleanupFn;
}
// TODO: this is basically the same as the HTTP variant, except only with a different test function wrapper
// extract these into smaller, testable chunks and re-use them
async executeTest(integrationTest) {
let val;
let error;
try {
// TODO: need to pull this body from the plugin interaction
val = await integrationTest({ port: this.port, address: this.address }, {});
}
catch (e) {
error = e instanceof Error ? e : new Error(String(e));
}
const matchingResults = this.pact.mockServerMismatches(this.port);
const errors = (0, display_1.filterMissingFeatureFlag)(matchingResults);
const success = this.pact.mockServerMatchedSuccessfully(this.port);
// Scenario: Pact validation failed
if (!success && errors.length > 0) {
let errorMessage = 'Test failed for the following reasons:';
errorMessage += `\n\n ${(0, display_1.generateMockServerError)(matchingResults, '\t')}`;
cleanup(false, this.pact, this.opts, this.cleanupFn, this.port, true);
// If the tests throws an error, we need to rethrow the error, but print out
// any additional mock server errors to help the user understand what happened
// (The proximate cause here is often the HTTP 500 from the mock server,
// where the HTTP client then throws)
if (error) {
logger_1.default.error(errorMessage);
throw error;
}
// Test didn't throw, so we need to ensure the test fails
return Promise.reject(new Error(errorMessage));
}
// Scenario: test threw an error, but Pact validation was OK (error in client or test)
if (error) {
cleanup(false, this.pact, this.opts, this.cleanupFn, this.port, true);
throw error;
}
// Scenario: Pact validation passed, test didn't throw - return the callback value
cleanup(true, this.pact, this.opts, this.cleanupFn, this.port, true);
return val;
}
}
exports.AsynchronousMessageWithTransport = AsynchronousMessageWithTransport;
const cleanup = (success, pact, opts, cleanupFn, port, transport = false) => {
if (success) {
if (transport && port) {
pact.writePactFileForPluginServer(port, opts.dir || defaultPactDir, true);
}
else {
pact.writePactFile(opts.dir || defaultPactDir);
}
}
if (port) {
pact.cleanupMockServer(port);
}
pact.cleanupPlugins();
cleanupFn();
};
const executeNonTransportTest = async (pact, opts, message, integrationTest, cleanupFn) => {
let val;
let error;
try {
const rawInteraction = JSON.parse(message.interaction.reifyMessage());
const { content } = rawInteraction.contents;
const m = {
contents: {
content: !message.requestIsBinary
? content
: Buffer.from(content, 'base64'),
},
metadata: rawInteraction.metadata,
};
val = await integrationTest(m);
}
catch (e) {
error = e instanceof Error ? e : new Error(String(e));
}
// Scenario: test threw an error, but Pact validation was OK (error in client or test)
if (error) {
cleanup(false, pact, opts, cleanupFn);
throw error;
}
// Scenario: Pact validation passed, test didn't throw - return the callback value
cleanup(true, pact, opts, cleanupFn);
return val;
};
//# sourceMappingURL=asynchronousMessage.js.map