Skip to content

Commit

Permalink
feat: replace ruby core with rust core in stable pact http package
Browse files Browse the repository at this point in the history
  • Loading branch information
mefellows committed Jul 5, 2022
1 parent 65b3908 commit 7b7d415
Show file tree
Hide file tree
Showing 16 changed files with 890 additions and 1,025 deletions.
2 changes: 2 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* add binary content support to messages via `WithBinaryContent`
* add text content support to messages via `WithTextContent`
* Remove all Ruby dependencies in DSLs
* `pactfileWriteMode` still supports the same options, however the behaviour of `overwrite` is such now that it will overwrite the pact file _per test_, not pact run of Pact. This is because there is no longer a single long running mock server as per previous versions. Set to `merge` or leave blank for a sensible default.
* As per the change to `pactfileWriteMode` this also means pact files should be cleared out prior to each test, to avoid extraneous interactions in a pact file.

# beta.56

Expand Down
10 changes: 10 additions & 0 deletions src/common/net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,13 @@ export const isPortAvailable = (port: number, host: string): Promise<void> =>
return portCheck(port, host);
})
);

export const freePort = (): Promise<number> => {
return new Promise((res) => {
const s = net.createServer();
s.listen(0, () => {
const port = s.address().port;
s.close(() => res(port));
});
});
};
21 changes: 21 additions & 0 deletions src/common/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SpecificationVersion } from '../../v3';

export const numberToSpec = (
spec?: number,
defaultSpec: SpecificationVersion = SpecificationVersion.SPECIFICATION_VERSION_V2
): SpecificationVersion => {
if (!spec) {
return defaultSpec;
}

switch (spec) {
case 2:
return SpecificationVersion.SPECIFICATION_VERSION_V2;
case 3:
return SpecificationVersion.SPECIFICATION_VERSION_V3;
case 4:
return SpecificationVersion.SPECIFICATION_VERSION_V4;
default:
throw new Error(`invalid pact specification version supplied: ${spec}`);
}
};
8 changes: 5 additions & 3 deletions src/dsl/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import { isNil, extend, omitBy, isUndefined } from 'lodash';
import gql from 'graphql-tag';
import { Interaction, InteractionState } from './interaction';
import { Interaction, InteractionStateComplete } from './interaction';
import { regex } from './matchers';
import GraphQLQueryError from '../errors/graphQLQueryError';
import ConfigurationError from '../errors/configurationError';
Expand Down Expand Up @@ -93,7 +93,9 @@ export class GraphQLInteraction extends Interaction {
/**
* Returns the interaction object created.
*/
public json(): InteractionState {
public json(): InteractionStateComplete {
super.json();

if (isNil(this.query)) {
throw new ConfigurationError('You must provide a GraphQL query.');
}
Expand Down Expand Up @@ -122,7 +124,7 @@ export class GraphQLInteraction extends Interaction {
this.state.request
);

return this.state;
return this.state as InteractionStateComplete;
}

private queryOrMutation(query: string, type: string): this {
Expand Down
34 changes: 30 additions & 4 deletions src/dsl/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@ interface QueryObject {
}
export type Query = string | QueryObject;

export type Headers = {
[header: string]: string | Matcher<string>;
};

export interface RequestOptions {
method: HTTPMethods | HTTPMethod;
path: string | Matcher<string>;
query?: Query;
headers?: { [name: string]: string | Matcher<string> };
headers?: Headers;
body?: AnyTemplate;
}

export interface ResponseOptions {
status: number;
headers?: { [name: string]: string | Matcher<string> };
headers?: Headers;
body?: AnyTemplate;
}

Expand All @@ -41,6 +45,13 @@ export interface InteractionState {
response?: ResponseOptions;
}

export interface InteractionStateComplete {
providerState?: string;
description: string;
request: RequestOptions;
response: ResponseOptions;
}

/**
* Returns valid if object or matcher only contains string values
* @param query
Expand Down Expand Up @@ -156,12 +167,27 @@ export class Interaction {
* Returns the interaction object created.
* @returns {Object}
*/
public json(): InteractionState {
public json(): InteractionStateComplete {
if (isNil(this.state.description)) {
throw new ConfigurationError(
'You must provide a description for the Interaction'
);
}
return this.state;
if (
isNil(this.state.request) ||
isNil(this.state?.request?.method) ||
isNil(this.state?.request?.path)
) {
throw new ConfigurationError(
'You must provide a request with at least a method and path for the Interaction'
);
}
if (isNil(this.state.response) || isNil(this.state?.response?.status)) {
throw new ConfigurationError(
'You must provide a response with a status for the Interaction'
);
}

return this.state as InteractionStateComplete;
}
}
24 changes: 15 additions & 9 deletions src/dsl/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { isFunction, isNil, isEmpty, isUndefined } from 'lodash';
import { times } from 'ramda';
import { AnyJson, JsonMap } from '../common/jsonTypes';
import MatcherError from '../errors/matcherError';

Expand Down Expand Up @@ -35,10 +36,8 @@ export interface Matcher<T> {
getValue(): T;
}

export interface ArrayMatcher<T> {
value: [T];
export interface ArrayMatcher<T> extends Matcher<T> {
'pact:matcher:type': string;
getValue(): T[];
min?: number;
max?: number;
}
Expand Down Expand Up @@ -74,15 +73,15 @@ export function validateExample(example: string, matcher: string): boolean {

/**
* The eachLike matcher
* @param {any} content
* @param {any} template
* @param {Object} opts
* @param {Number} opts.min
*/
export function eachLike<T>(
content: T,
template: T,
opts?: { min: number }
): ArrayMatcher<T> {
if (isUndefined(content)) {
): ArrayMatcher<T[]> {
if (isUndefined(template)) {
throw new MatcherError(
'Error creating a Pact eachLike. Please provide a content argument'
);
Expand All @@ -97,8 +96,8 @@ export function eachLike<T>(
const min = !isEmpty(opts) && opts ? opts.min : 1;

return {
value: [content],
getValue: () => Array.from(new Array(min), () => content),
value: times(() => template, min),
getValue: () => times(() => template, min),
'pact:matcher:type': 'type',
min,
};
Expand Down Expand Up @@ -327,3 +326,10 @@ export function extractPayload(value: AnyTemplate): AnyJson {
}
return value;
}

// Gets a matcher as JSON or the string value if it's not a matcher
export function matcherValueOrString(obj: unknown): string {
if (typeof obj === 'string') return obj;

return JSON.stringify(obj);
}
153 changes: 0 additions & 153 deletions src/dsl/mockService.spec.ts

This file was deleted.

Loading

0 comments on commit 7b7d415

Please sign in to comment.