UNPKG

@pact-foundation/pact

Version:
269 lines 10.3 kB
"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