diff --git a/examples/e2e/consumer.js b/examples/e2e/consumer.js index 657006319..bfee42c40 100644 --- a/examples/e2e/consumer.js +++ b/examples/e2e/consumer.js @@ -1,13 +1,14 @@ const express = require('express') const request = require('superagent') const server = express() -const API_HOST = process.env.API_HOST || 'http://localhost:8081' + +const getApiEndpoint = () => process.env.API_HOST || 'http://localhost:8081' // Fetch animals who are currently 'available' from the // Animal Service const availableAnimals = () => { return request - .get(`${API_HOST}/animals/available`) + .get(`${getApiEndpoint()}/animals/available`) .then(res => res.body, () => []) } @@ -15,7 +16,7 @@ const availableAnimals = () => { // Find animals by their ID from the Animal Service const getAnimalById = (id) => { return request - .get(`${API_HOST}/animals/${id}`) + .get(`${getApiEndpoint()}/animals/${id}`) .then(res => res.body, () => null) } @@ -55,7 +56,7 @@ const suggestion = mate => { // Creates a mate for suggestions const createMateForDates = (mate) => { return request - .post(`${API_HOST}/animals`) + .post(`${getApiEndpoint()}/animals`) .send(mate) .set('Content-Type', 'application/json; charset=utf-8') } @@ -67,7 +68,7 @@ server.get('/suggestions/:animalId', (req, res) => { res.end() } - request(`${API_HOST}/animals/${req.params.animalId}`, (err, r) => { + request(`${getApiEndpoint()}/animals/${req.params.animalId}`, (err, r) => { if (!err && r.statusCode === 200) { suggestion(r.body).then(suggestions => { res.json(suggestions) diff --git a/examples/e2e/test/consumer.spec.js b/examples/e2e/test/consumer.spec.js index 4d2acbbc7..01a99e39c 100644 --- a/examples/e2e/test/consumer.spec.js +++ b/examples/e2e/test/consumer.spec.js @@ -3,7 +3,6 @@ const chai = require('chai') const chaiAsPromised = require('chai-as-promised') const expect = chai.expect const { Pact, Matchers } = require('../../../dist/pact'); -const MOCK_SERVER_PORT = 1234 const LOG_LEVEL = process.env.LOG_LEVEL || 'WARN' chai.use(chaiAsPromised) @@ -12,7 +11,7 @@ describe('Pact', () => { const provider = new Pact({ consumer: 'Matching Service', provider: 'Animal Profile Service', - port: MOCK_SERVER_PORT, + // port: 1234, // You can set the port explicitly here or dynamically (see setup() below) log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'), dir: path.resolve(process.cwd(), 'pacts'), logLevel: LOG_LEVEL, @@ -88,7 +87,10 @@ describe('Pact', () => { // to act like the Provider // It also sets up expectations for what requests are to come, and will fail // if the calls are not seen. - before(() => provider.setup()) + before(() => provider.setup().then(opts => { + // Get a dynamic port from the runtime + process.env.API_HOST = `http://localhost:${opts.port}` + })) // After each individual test (one or more interactions) // we validate that the correct request came through. @@ -98,7 +100,7 @@ describe('Pact', () => { // Configure and import consumer API // Note that we update the API endpoint to point at the Mock Service - process.env.API_HOST = `http://localhost:${MOCK_SERVER_PORT}` + const { createMateForDates, suggestion, diff --git a/examples/typescript/test/get-dog.spec.ts b/examples/typescript/test/get-dog.spec.ts index 0ec963a14..18db3d760 100644 --- a/examples/typescript/test/get-dog.spec.ts +++ b/examples/typescript/test/get-dog.spec.ts @@ -14,22 +14,23 @@ chai.use(chaiAsPromised); describe("The Dog API", () => { const url = "http://localhost"; - const port = 8993; - const dogService = new DogService({ url, port }); + let dogService: DogService; const provider = new Pact({ - port, + // port, log: path.resolve(process.cwd(), "logs", "mockserver-integration.log"), dir: path.resolve(process.cwd(), "pacts"), spec: 2, consumer: "MyConsumer", provider: "MyProvider", - pactfileWriteMode: "merge" + pactfileWriteMode: "merge", }); const EXPECTED_BODY = [{ dog: 1 }, { dog: 2 }]; - before(() => provider.setup()); + before(() => provider.setup().then((opts) => { + dogService = new DogService({ url, port: opts.port }); + })); after(() => provider.finalize()); diff --git a/src/pact.spec.ts b/src/pact.spec.ts index 4fe731dbf..42a2b9b3e 100644 --- a/src/pact.spec.ts +++ b/src/pact.spec.ts @@ -127,7 +127,7 @@ describe("Pact", () => { startStub.rejects(); const b = Object.create(Pact.prototype) as any as PactType; b.opts = fullOpts; - b.server = { start: startStub } as any; + b.server = { start: startStub, options: { port: 1234 } } as any; return expect(b.setup()).to.eventually.be.rejected; }); }); @@ -139,10 +139,31 @@ describe("Pact", () => { startStub.resolves(); const b = Object.create(Pact.prototype) as any as PactType; b.opts = fullOpts; - b.server = { start: startStub } as any; + b.server = { start: startStub, options: { port: 1234 } } as any; return expect(b.setup()).to.eventually.be.fulfilled; }); }); + describe("when server is properly configured", () => { + it("should return the current configuration", () => { + // TODO: actually test is pact-node is starting instead of stubbing it + const startStub = sandbox.stub(PactServer.prototype, "start"); + startStub.resolves(); + const b = Object.create(Pact.prototype) as any as PactType; + b.opts = fullOpts; + b.server = { start: startStub, options: { port: 1234 } } as any; + return expect(b.setup()).to.eventually.include({ + consumer: "A", + provider: "B", + port: 1234, + host: "127.0.0.1", + ssl: false, + logLevel: "info", + spec: 2, + cors: false, + pactfileWriteMode: "overwrite", + }); + }); + }); }); describe("#addInteraction", () => { diff --git a/src/pact.ts b/src/pact.ts index 32836f161..7fb851f63 100644 --- a/src/pact.ts +++ b/src/pact.ts @@ -25,7 +25,6 @@ import { Server } from "@pact-foundation/pact-node/src/server"; * @param {PactOptions} opts * @return {@link PactProvider} */ -// TODO: move this to its own module export class Pact { public static defaults = { consumer: "", @@ -35,7 +34,6 @@ export class Pact { log: path.resolve(process.cwd(), "logs", "pact.log"), logLevel: "info", pactfileWriteMode: "overwrite", - port: 1234, provider: "", spec: 2, ssl: false, @@ -70,29 +68,26 @@ export class Pact { host: this.opts.host, log: this.opts.log, pactFileWriteMode: this.opts.pactfileWriteMode, - port: this.opts.port, + port: config.port, // allow to be undefined provider: this.opts.provider, spec: this.opts.spec, ssl: this.opts.ssl, sslcert: this.opts.sslcert, sslkey: this.opts.sslkey, }); - - logger.info(`Setting up Pact with Consumer "${this.opts.consumer}" and Provider "${this.opts.provider}" - using mock service on Port: "${this.opts.port}"`); - - this.mockService = new MockService(undefined, undefined, this.opts.port, this.opts.host, - this.opts.ssl, this.opts.pactfileWriteMode); } /** * Start the Mock Server. * @returns {Promise} */ - public setup(): Promise { - return isPortAvailable(this.opts.port, this.opts.host) - // Need to wrap it this way until we remove q.Promise from pact-node - .then(() => new Promise((resolve, reject) => this.server.start().then(() => resolve(), (e: any) => reject(e)))) + public setup(): Promise { + return this.checkPort() + .then(() => this.startServer()) + .then((opts) => { + this.setupMockService() + return Promise.resolve(opts) + }) } /** @@ -188,6 +183,33 @@ export class Pact { public removeInteractions(): Promise { return this.mockService.removeInteractions(); } + + private checkPort(): Promise { + if (this.server && this.server.options.port) { + return isPortAvailable(this.server.options.port, this.opts.host) + } + return Promise.resolve() + } + + private setupMockService(): void { + logger.info(`Setting up Pact with Consumer "${this.opts.consumer}" and Provider "${this.opts.provider}" + using mock service on Port: "${this.opts.port}"`); + + this.mockService = new MockService(undefined, undefined, this.opts.port, this.opts.host, + this.opts.ssl, this.opts.pactfileWriteMode); + } + + private startServer(): Promise { + return new Promise( + (resolve, reject) => + this.server.start() + .then( + () => { + this.opts.port = this.server.options.port || this.opts.port + resolve(this.opts) + }, + (e: any) => reject(e))) + } } export * from "./messageConsumerPact";