Skip to content

Commit

Permalink
feat(configuration): return configuration from setup()
Browse files Browse the repository at this point in the history
- Pact.setup() now returns a PactOptionsComplete object,
  containing the full configuration of the Pact provider
- Minor refactor of the setup() function to dynamically
  load the port from the mock server

Fixes #259
  • Loading branch information
mefellows committed Feb 9, 2019
1 parent 8baf35f commit 3ab235d
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 29 deletions.
11 changes: 6 additions & 5 deletions examples/e2e/consumer.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
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,
() => [])
}

// 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)
}
Expand Down Expand Up @@ -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')
}
Expand All @@ -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)
Expand Down
10 changes: 6 additions & 4 deletions examples/e2e/test/consumer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand Down
11 changes: 6 additions & 5 deletions examples/typescript/test/get-dog.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
25 changes: 23 additions & 2 deletions src/pact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
});
Expand All @@ -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", () => {
Expand Down
48 changes: 35 additions & 13 deletions src/pact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: "",
Expand All @@ -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,
Expand Down Expand Up @@ -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<void> {
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<void>((resolve, reject) => this.server.start().then(() => resolve(), () => reject())));
public setup(): Promise<PactOptionsComplete> {
return this.checkPort()
.then(() => this.startServer())
.then((opts) => {
this.setupMockService()
return Promise.resolve(opts)
})
}

/**
Expand Down Expand Up @@ -188,6 +183,33 @@ export class Pact {
public removeInteractions(): Promise<string> {
return this.mockService.removeInteractions();
}

private checkPort(): Promise<void> {
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<PactOptionsComplete> {
return new Promise<PactOptionsComplete>(
(resolve, reject) =>
this.server.start()
.then(
() => {
this.opts.port = this.server.options.port || this.opts.port
resolve(this.opts)
},
() => reject()))
}
}

export * from "./messageConsumerPact";
Expand Down

0 comments on commit 3ab235d

Please sign in to comment.