@pact-foundation/pact
Version:
Pact for all things Javascript
221 lines • 11.5 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PactV3 = void 0;
var ramda_1 = require("ramda");
var pact_core_1 = require("@pact-foundation/pact-core");
var fs = require("fs");
var package_json_1 = require("../../package.json");
var types_1 = require("./types");
var matchers_1 = require("./matchers");
var display_1 = require("./display");
var logger_1 = __importDefault(require("../common/logger"));
var ffi_1 = require("./ffi");
var readBinaryData = function (file) {
try {
var body = fs.readFileSync(file);
return body;
}
catch (e) {
throw new Error("unable to read file for binary request: ".concat(e.message));
}
};
var PactV3 = /** @class */ (function () {
function PactV3(opts) {
this.states = [];
this.opts = opts;
this.setup();
}
// JSON object interface for V3, to aid with migration from the previous major version
PactV3.prototype.addInteraction = function (interaction) {
var _this = this;
if (interaction.uponReceiving === '') {
throw new Error("must provide a valid interaction description via 'uponReceiving'");
}
(interaction.states || []).forEach(function (s) {
_this.given(s.description, s.parameters);
});
this.uponReceiving(interaction.uponReceiving);
this.withRequest(interaction.withRequest);
this.willRespondWith(interaction.willRespondWith);
return this;
};
// TODO: this currently must be called before other methods, else it won't work
PactV3.prototype.given = function (providerState, parameters) {
if (parameters) {
var json = JSON.stringify(parameters);
// undefined arguments not supported (invalid JSON)
if (json === undefined) {
throw new Error("Invalid provider state parameter received. Parameters must not be undefined. Received: ".concat(parameters));
}
// Check nested objects
var jsonParsed = JSON.parse(json);
if (!(0, ramda_1.equals)(parameters, jsonParsed)) {
throw new Error("Invalid provider state parameter received. Parameters must not contain undefined values. Received: ".concat(parameters));
}
}
this.states.push({ description: providerState, parameters: parameters });
return this;
};
PactV3.prototype.uponReceiving = function (description) {
var _this = this;
this.interaction = this.pact.newInteraction(description);
this.states.forEach(function (s) {
if (s.parameters) {
_this.interaction.givenWithParams(s.description, JSON.stringify(s.parameters));
}
else {
_this.interaction.given(s.description);
}
});
return this;
};
PactV3.prototype.withRequest = function (req) {
if (req.body) {
this.interaction.withRequestBody((0, matchers_1.matcherValueOrString)(req.body), req.contentType ||
(0, ffi_1.contentTypeFromHeaders)(req.headers, 'application/json'));
}
(0, ffi_1.setRequestDetails)(this.interaction, req);
return this;
};
PactV3.prototype.withRequestBinaryFile = function (req, contentType, file) {
var body = readBinaryData(file);
this.interaction.withRequestBinaryBody(body, contentType);
(0, ffi_1.setRequestDetails)(this.interaction, req);
return this;
};
PactV3.prototype.withRequestMultipartFileUpload = function (req, contentType, file, mimePartName) {
this.interaction.withRequestMultipartBody(contentType, file, mimePartName);
(0, ffi_1.setRequestDetails)(this.interaction, req);
return this;
};
PactV3.prototype.willRespondWith = function (res) {
(0, ffi_1.setResponseDetails)(this.interaction, res);
if (res.body) {
this.interaction.withResponseBody((0, matchers_1.matcherValueOrString)(res.body), res.contentType ||
(0, ffi_1.contentTypeFromHeaders)(res.headers, 'application/json'));
}
this.states = [];
return this;
};
PactV3.prototype.withResponseBinaryFile = function (res, contentType, file) {
var body = readBinaryData(file);
this.interaction.withResponseBinaryBody(body, contentType);
(0, ffi_1.setResponseDetails)(this.interaction, res);
return this;
};
PactV3.prototype.withResponseMultipartFileUpload = function (res, contentType, file, mimePartName) {
this.interaction.withResponseMultipartBody(contentType, file, mimePartName);
(0, ffi_1.setResponseDetails)(this.interaction, res);
return this;
};
PactV3.prototype.executeTest = function (testFn) {
return __awaiter(this, void 0, void 0, function () {
var scheme, host, port, server, val, error, e_1, matchingResults, errors, success, errorMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
scheme = this.opts.tls ? 'https' : 'http';
host = this.opts.host || '127.0.0.1';
port = this.pact.createMockServer(host, this.opts.port, this.opts.tls);
server = { port: port, url: "".concat(scheme, "://").concat(host, ":").concat(port), id: 'unknown' };
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, testFn(server)];
case 2:
val = _a.sent();
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
error = e_1;
return [3 /*break*/, 4];
case 4:
matchingResults = this.pact.mockServerMismatches(port);
errors = (0, display_1.filterMissingFeatureFlag)(matchingResults);
success = this.pact.mockServerMatchedSuccessfully(port);
// Scenario: Pact validation failed
if (!success && errors.length > 0) {
errorMessage = 'Test failed for the following reasons:';
errorMessage += "\n\n ".concat((0, display_1.generateMockServerError)(matchingResults, '\t'));
this.cleanup(false, server);
// 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 [2 /*return*/, Promise.reject(new Error(errorMessage))];
}
// Scenario: test threw an error, but Pact validation was OK (error in client or test)
if (error) {
this.cleanup(false, server);
throw error;
}
// Scenario: Pact validation passed, test didn't throw - return the callback value
this.cleanup(true, server);
return [2 /*return*/, val];
}
});
});
};
PactV3.prototype.cleanup = function (success, server) {
if (success) {
this.pact.writePactFile(this.opts.dir || './pacts');
}
this.pact.cleanupMockServer(server.port);
this.setup();
};
// reset the internal state
// (this.pact cannot be re-used between tests)
PactV3.prototype.setup = function () {
var _a, _b;
this.states = [];
this.pact = (0, pact_core_1.makeConsumerPact)(this.opts.consumer, this.opts.provider, (_a = this.opts.spec) !== null && _a !== void 0 ? _a : types_1.SpecificationVersion.SPECIFICATION_VERSION_V3, (_b = this.opts.logLevel) !== null && _b !== void 0 ? _b : 'info');
this.pact.addMetadata('pact-js', 'version', package_json_1.version);
};
return PactV3;
}());
exports.PactV3 = PactV3;
//# sourceMappingURL=pact.js.map