@pact-foundation/pact
Version:
Pact for all things Javascript
311 lines • 13.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const fs = require("node:fs");
const net = require("node:net");
const path = require("node:path");
const v3_1 = require("./v3");
const v4_1 = require("./v4");
describe('V4 Pact', () => {
let pact;
const pactFilePath = path.resolve(process.cwd(), 'pacts', 'v4consumer-v4provider.json');
const interactionByDescription = (description) => {
const pactJson = JSON.parse(fs.readFileSync(pactFilePath, 'utf8'));
const matches = pactJson.interactions.filter((item) => item.description === description);
expect(matches, `expected exactly one interaction for "${description}"`).toHaveLength(1);
const [interaction] = matches;
return interaction;
};
const expectCommentsToContain = (interaction, reason, textComment, testName) => {
const comments = interaction.comments;
expect(comments.reason).toBe(reason);
expect(comments.text).toContain(textComment);
expect(comments.testname).toBe(testName);
};
const expectReferenceToEqual = (interaction, group, name, value) => {
const comments = interaction.comments;
const references = comments.references;
expect(references[group][name]).toBe(value);
};
beforeEach(() => {
pact = new v4_1.PactV4({
consumer: 'v4consumer',
provider: 'v4provider',
});
});
describe('HTTP req/res contract', () => {
it('generates a pact', () => pact
.addInteraction()
.given('some state')
.given('a second state')
.uponReceiving('a standard HTTP req/res')
.withRequest('POST', '/', (builder) => {
builder
.jsonBody({
foo: 'bar',
})
.headers({
'x-foo': 'x-bar',
});
})
.willRespondWith(200, (builder) => {
builder
.jsonBody({
foo: 'bar',
})
.headers({
'x-foo': 'x-bar',
});
})
.executeTest(async (server) => axios_1.default.post(server.url, {
foo: 'bar',
}, {
headers: {
'x-foo': 'x-bar',
},
})));
it('generates a pact with interaction metadata', async () => {
const description = 'v4 metadata http req/res interaction';
await pact
.addInteraction()
.given('some state')
.given('a second state')
.uponReceiving(description)
.pending()
.comment({ key: 'reason', value: 'covered by HTTP metadata test' })
.comment('second note from HTTP metadata test')
.testName('http metadata test name')
.withRequest('POST', '/', (builder) => {
builder
.jsonBody({
foo: 'bar',
})
.headers({
'x-foo': 'x-bar',
});
})
.willRespondWith(200, (builder) => {
builder
.jsonBody({
foo: 'bar',
})
.headers({
'x-foo': 'x-bar',
});
})
.executeTest(async (server) => axios_1.default.post(server.url, {
foo: 'bar',
}, {
headers: {
'x-foo': 'x-bar',
},
}));
const interaction = interactionByDescription(description);
expect(interaction.pending).toBe(true);
expectCommentsToContain(interaction, 'covered by HTTP metadata test', 'second note from HTTP metadata test', 'http metadata test name');
});
it('records interaction references in the pact file', async () => {
const description = 'v4 http interaction with references';
await pact
.addInteraction()
.uponReceiving(description)
.reference('Jira', 'TICKET-123', 'https://jira.example.com/TICKET-123')
.withRequest('GET', '/')
.willRespondWith(200)
.executeTest(async (server) => axios_1.default.get(server.url));
const interaction = interactionByDescription(description);
expectReferenceToEqual(interaction, 'Jira', 'TICKET-123', 'https://jira.example.com/TICKET-123');
});
it('supports regex matcher for response content-type with optional charset', () => pact
.addInteraction()
.uponReceiving('a response with regex content-type header matcher')
.withRequest('GET', '/')
.willRespondWith(200, (builder) => {
builder
.headers({
'Content-Type': v3_1.MatchersV3.regex(/^application\/json(;\s?charset=[\w-]+)?$/i, 'application/json'),
})
.jsonBody({
foo: 'bar',
});
})
.executeTest(async (server) => {
const response = await axios_1.default.get(server.url);
expect(response.data).toEqual({ foo: 'bar' });
}));
it('supports regex matcher for request content-type with optional charset', () => pact
.addInteraction()
.uponReceiving('a request with regex content-type header matcher')
.withRequest('POST', '/', (builder) => {
builder
.headers({
'Content-Type': v3_1.MatchersV3.regex(/^application\/json(;\s?charset=[\w-]+)?$/i, 'application/json'),
})
.jsonBody({
foo: 'bar',
});
})
.willRespondWith(200, (builder) => {
builder.jsonBody({
ok: true,
});
})
.executeTest((server) => axios_1.default.post(server.url, {
foo: 'bar',
})));
});
describe('Asynchronous message contract', () => {
it('generates a pact with interaction metadata', async () => {
const description = 'v4 metadata async interaction message';
await pact
.addAsynchronousInteraction()
.given('an async message state')
.pending()
.comment({ key: 'reason', value: 'covered by async metadata test' })
.comment('second note from async metadata test')
.testName('async metadata test name')
.expectsToReceive(description, (builder) => {
builder.withJSONContent({
event: 'user.created',
});
})
.executeTest(async () => Promise.resolve());
const interaction = interactionByDescription(description);
expect(interaction.pending).toBe(true);
expectCommentsToContain(interaction, 'covered by async metadata test', 'second note from async metadata test', 'async metadata test name');
});
it('records interaction references in the pact file', async () => {
const description = 'v4 async interaction with references';
await pact
.addAsynchronousInteraction()
.reference('GitHub', 'PR-456', 'https://github.com/example/repo/pull/456')
.expectsToReceive(description, (builder) => {
builder.withJSONContent({ event: 'user.created' });
})
.executeTest(async () => Promise.resolve());
const interaction = interactionByDescription(description);
expectReferenceToEqual(interaction, 'GitHub', 'PR-456', 'https://github.com/example/repo/pull/456');
});
});
describe('Synchronous message contract', () => {
it('generates a pact with interaction metadata', async () => {
const description = 'v4 metadata sync interaction message';
await pact
.addSynchronousInteraction(description)
.given('a synchronous message state')
.pending()
.comment({ key: 'reason', value: 'covered by sync metadata test' })
.comment('second note from sync metadata test')
.testName('sync metadata test name')
.withRequest((builder) => {
builder.withJSONContent({
request: 'ping',
});
})
.withResponse((builder) => {
builder.withJSONContent({
response: 'pong',
});
})
.executeTest(async () => Promise.resolve());
const interaction = interactionByDescription(description);
expect(interaction.pending).toBe(true);
expectCommentsToContain(interaction, 'covered by sync metadata test', 'second note from sync metadata test', 'sync metadata test name');
});
it('records interaction references in the pact file', async () => {
const description = 'v4 sync interaction with references';
await pact
.addSynchronousInteraction(description)
.reference('Jira', 'TICKET-789', 'https://jira.example.com/TICKET-789')
.withRequest((builder) => {
builder.withJSONContent({ request: 'ping' });
})
.withResponse((builder) => {
builder.withJSONContent({ response: 'pong' });
})
.executeTest(async () => Promise.resolve());
const interaction = interactionByDescription(description);
expectReferenceToEqual(interaction, 'Jira', 'TICKET-789', 'https://jira.example.com/TICKET-789');
});
});
describe('Plugin test', () => {
describe('Using the MATT plugin', () => {
const parseMattMessage = (raw) => raw.replace(/(MATT)+/g, '').trim();
const generateMattMessage = (raw) => `MATT${raw}MATT`;
describe('HTTP interface', () => {
it('generates a pact', async () => {
const mattRequest = `{"request": {"body": "hello"}}`;
const mattResponse = `{"response":{"body":"world"}}`;
await pact
.addInteraction()
.given('the Matt protocol exists')
.uponReceiving('an HTTP request to /matt')
.usingPlugin({
plugin: 'matt',
version: '0.1.1',
})
.withRequest('POST', '/matt', (builder) => {
builder.pluginContents('application/matt', mattRequest);
})
.willRespondWith(200, (builder) => {
builder.pluginContents('application/matt', mattResponse);
})
.executeTest((mockserver) => axios_1.default
.request({
baseURL: mockserver.url,
headers: {
'content-type': 'application/matt',
Accept: 'application/matt',
},
data: generateMattMessage('hello'),
method: 'POST',
url: '/matt',
})
.then((res) => {
expect(parseMattMessage(res.data)).toBe('world');
}));
});
});
describe('Synchronous Message (TCP) ', () => {
describe('with MATT protocol', () => {
const HOST = '127.0.0.1';
const sendMattMessageTCP = (message, host, port) => {
const socket = net.connect({
port,
host,
});
const res = socket.write(`${generateMattMessage(message)}\n`);
if (!res) {
throw Error('unable to connect to host');
}
return new Promise((resolve) => {
socket.on('data', (data) => {
resolve(parseMattMessage(data.toString()));
socket.destroy();
});
});
};
it('generates a pact', () => {
const mattMessage = `{"request": {"body": "hellotcp"}, "response":{"body":"tcpworld"}}`;
return pact
.addSynchronousInteraction('a MATT message')
.usingPlugin({
plugin: 'matt',
version: '0.1.1',
})
.withPluginContents(mattMessage, 'application/matt')
.startTransport('matt', HOST)
.executeTest(async (tc) => {
const message = await sendMattMessageTCP('hellotcp', HOST, tc.port);
expect(message).toBe('tcpworld');
});
});
});
});
});
});
});
//# sourceMappingURL=pact.integration.spec.js.map