Skip to content

Commit

Permalink
feat: add consumer version selectors and pending pacts to verification
Browse files Browse the repository at this point in the history
- add support for Pending Pacts flag
- add support for consumer version selectors

See pact-foundation/pact_broker#320 for background
  • Loading branch information
mefellows committed Mar 25, 2020
1 parent ed38800 commit 29042a5
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 28 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ pact.verifyPacts({
| `providerBaseUrl` | true | string | Running API provider host endpoint. |
| `pactBrokerUrl` | false | string | Base URL of the Pact Broker from which to retrieve the pacts. Required if `pactUrls` not given. |
| `provider` | false | string | Name of the provider if fetching from a Broker |
| `consumerVersionSelectors` | false | ConsumerVersionSelector\|array | Use [Selectors](https://docs.pact.io/selectors) to is a way we specify which pacticipants and versions we want to use when configuring verifications. |
| `consumerVersionTag` | false | string\|array | Retrieve the latest pacts with given tag(s) |
| `providerVersionTag` | false | string\|array | Tag(s) to apply to the provider application |
| `pactUrls` | false | array | Array of local pact file paths or HTTP-based URLs. Required if _not_ using a Pact Broker. |
Expand Down
56 changes: 56 additions & 0 deletions src/spawn/arguments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ describe('Pact Util Spec', () => {
expect(result).to.include('--pact-urls');
expect(result).to.include('http://idontexist');
});
describe("and the argument's value is also an object", () => {
it('should serialise the argument value to a JSON string', () => {
const result = argsHelper.toArgumentsArray(
{
consumerVersionSelectors: [
{
all: true,
tag: 'prod',
},
{
tag: 'bar',
},
],
},
{ consumerVersionSelectors: '--consumer-version-selector' },
);

expect(result)
.to.be.an('array')
.that.includes('--consumer-version-selector')
.and.includes('{"all":true,"tag":"prod"}')
.and.includes('{"tag":"bar"}');
expect(result.length).to.be.equal(4);
});
});
});
describe('when called with an array', () => {
describe('with one element', () => {
Expand Down Expand Up @@ -96,6 +121,37 @@ describe('Pact Util Spec', () => {
]);
});
});
describe("and an argument's value is an object", () => {
it('should serialise the argument value to a JSON string', () => {
const result = argsHelper.toArgumentsArray(
[
{
consumerVersionSelectors: [
{
all: true,
tag: 'prod',
},
],
},
{
consumerVersionSelectors: [
{
tag: 'foo',
},
],
},
],
{ consumerVersionSelectors: '--consumer-version-selector' },
);

expect(result)
.to.be.an('array')
.that.includes('--consumer-version-selector')
.and.includes('{"all":true,"tag":"prod"}')
.and.includes('{"tag":"foo"}');
expect(result.length).to.be.equal(4);
});
});
});

it('should make DEFAULT values first, everything else after', () => {
Expand Down
15 changes: 10 additions & 5 deletions src/spawn/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ export type SpawnArguments = Array<SpawnArgument> | SpawnArgument;
export const DEFAULT_ARG = 'DEFAULT';
export const PACT_NODE_NO_VALUE = 'PACT_NODE_NO_VALUE';

const valFor = (v: string): Array<string> =>
v !== PACT_NODE_NO_VALUE ? [v] : [];
const valFor = (v: SpawnArgument): Array<string> => {
if (typeof v === 'string') {
return v !== PACT_NODE_NO_VALUE ? [v] : [];
} else {
return [JSON.stringify(v)];
}
};

const mapFor = (mapping: string, v: string): Array<string> =>
mapping === DEFAULT_ARG ? valFor(v) : [mapping].concat(valFor(v));

const convertValue = (
mapping: string,
v: string | Array<string>,
mapping: string,
v: SpawnArgument | Array<SpawnArgument>,
): Array<string> => {
if (v && mapping) {
return checkTypes.array(v)
Expand Down Expand Up @@ -66,7 +71,7 @@ export class Arguments {
.reduce(
(
acc: Array<string>,
value: string | Array<string>,
value: SpawnArguments | Array<SpawnArguments>,
key: string,
): Array<string> =>
mappings[key] === DEFAULT_ARG
Expand Down
67 changes: 44 additions & 23 deletions src/verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ export class Verifier {
pactBrokerToken: '--broker-token',
consumerVersionTag: '--consumer-version-tag',
providerVersionTag: '--provider-version-tag',
consumerVersionSelector: '--consumer-version-selector',
publishVerificationResult: '--publish-verification-results',
providerVersion: '--provider-app-version',
provider: '--provider',
enablePending: '--enable-pending',
customProviderHeaders: '--custom-provider-header',
verbose: '--verbose',
monkeypatch: '--monkeypatch',
Expand All @@ -50,6 +52,7 @@ export class Verifier {
options.timeout = options.timeout || 30000;
options.consumerVersionTag = options.consumerVersionTag || [];
options.providerVersionTag = options.providerVersionTag || [];
options.consumerVersionSelector = options.consumerVersionSelector || [];

if (
options.consumerVersionTag &&
Expand Down Expand Up @@ -140,52 +143,56 @@ export class Verifier {
}

if (options.providerBaseUrl) {
checkTypes.assert.string(options.providerBaseUrl);
checkTypes.assert.string(options.providerBaseUrl);
}

if (options.publishVerificationResult) {
checkTypes.assert.boolean(options.publishVerificationResult);
checkTypes.assert.boolean(options.publishVerificationResult);
}

if (options.publishVerificationResult && !options.providerVersion) {
throw new Error(
'Must provide both or none of publishVerificationResults and providerVersion.',
);
}
throw new Error(
'Must provide both or none of publishVerificationResults and providerVersion.',
);
}

if (options.providerVersion) {
checkTypes.assert.string(options.providerVersion);
}
if (options.providerVersion) {
checkTypes.assert.string(options.providerVersion);
}

if (options.format) {
checkTypes.assert.string(options.format);
checkTypes.assert.match(options.format, /^(xml|json|progress)$/i);
if (options.format) {
checkTypes.assert.string(options.format);
checkTypes.assert.match(options.format, /^(xml|json|progress)$/i);
if (options.format.toLowerCase() === 'xml') {
options.format = 'RspecJunitFormatter';
options.format = 'RspecJunitFormatter';
}
}

if (options.out) {
checkTypes.assert.string(options.out);
checkTypes.assert.string(options.out);
}

if ((options.enablePending !== undefined)) {
checkTypes.assert.boolean(options.enablePending);
}

if (options.tags) {
logger.warn(
"'tags' has been deprecated as at v8.0.0, please use 'consumerVersionTag' instead",
);
}
logger.warn(
"'tags' has been deprecated as at v8.0.0, please use 'consumerVersionTag' instead",
);
}

checkTypes.assert.positive(options.timeout as number);
checkTypes.assert.positive(options.timeout as number);

if (options.monkeypatch) {
if (options.monkeypatch) {
checkTypes.assert.string(options.monkeypatch);
try {
fs.statSync(path.normalize(options.monkeypatch)).isFile();
fs.statSync(path.normalize(options.monkeypatch)).isFile();
} catch (e) {
throw new Error(
`Monkeypatch ruby file not found at path: ${options.monkeypatch}`,
);
}
);
}
}

this.options = options;
Expand Down Expand Up @@ -219,6 +226,18 @@ export class Verifier {
// Creates a new instance of the pact server with the specified option
export default (options: VerifierOptions): Verifier => new Verifier(options);

// A ConsumerVersionSelector is a way we specify which pacticipants and
// versions we want to use when configuring verifications.
//
// See https://docs.pact.io/selectors for more
export interface ConsumerVersionSelector {
pacticipant?: string;
tag?: string;
version?: string;
latest?: boolean;
all?: boolean;
}

export interface VerifierOptions {
providerBaseUrl: string;
provider?: string;
Expand All @@ -230,9 +249,11 @@ export interface VerifierOptions {
pactBrokerToken?: string;
consumerVersionTag?: string | string[];
providerVersionTag?: string | string[];
consumerVersionSelector?: ConsumerVersionSelector[];
customProviderHeaders?: string[];
publishVerificationResult?: boolean;
providerVersion?: string;
enablePending?: boolean;
timeout?: number;
tags?: string[];
verbose?: boolean;
Expand Down

0 comments on commit 29042a5

Please sign in to comment.