From d5b728bc7c25c95b73bd9f4036f1eb397fb69cdf Mon Sep 17 00:00:00 2001 From: mefellows Date: Sat, 8 Jul 2023 21:50:51 +1000 Subject: [PATCH] wip: status, eachKey and eachValue matchers --- src/v3/ffi.ts | 2 +- src/v3/matchers.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++- src/v3/types.ts | 27 ++++++++++++++++++++- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/v3/ffi.ts b/src/v3/ffi.ts index fa617d737..56b94f834 100644 --- a/src/v3/ffi.ts +++ b/src/v3/ffi.ts @@ -42,7 +42,7 @@ export const setResponseDetails = ( interaction: ConsumerInteraction, res: V3Response ): void => { - interaction.withStatus(res.status); + interaction.withStatus(MatchersV3.reify(res.status) as number); forEachObjIndexed((v, k) => { if (Array.isArray(v)) { diff --git a/src/v3/matchers.ts b/src/v3/matchers.ts index a3a4a9312..59cdf8a36 100644 --- a/src/v3/matchers.ts +++ b/src/v3/matchers.ts @@ -8,6 +8,9 @@ import { MaxLikeMatcher, MinLikeMatcher, ProviderStateInjectedValue, + RulesMatcher, + HTTPResponseStatusClass, + StatusCodeMatcher, V3RegexMatcher, } from './types'; @@ -33,8 +36,9 @@ export const like = (template: T): Matcher => ({ }); /** - * Object where the key itself is ignored, but the value template must match. + * Object where the values are ignored, but the key template must match. * + * @deprecated use eachKeyMatches or eachValueMatches * @param keyTemplate Example key to use * @param template Example value template to base the comparison on */ @@ -48,6 +52,59 @@ export const eachKeyLike = ( }, }); +/** + * Object where the _keys_ must match the supplied matchers. + * The values for each key are ignored. That is, there can be 0 or more keys + * with any valid JSON identifier, so long as the names of the keys match the constraints. + * + * @param example Example object with key/values e.g. `{ foo: 'bar', baz: 'qux'}` + * @param matchers Matchers to apply to each key + */ +export const eachKeyMatches = ( + example: Record, + matchers: Matcher | Matcher[] = like('key') +): RulesMatcher => ({ + 'pact:matcher:type': 'eachKey', + rules: Array.isArray(matchers) ? matchers : [matchers], + value: example, +}); + +/** + * Object where the _values_ must match the supplied matchers. + * The names of the keys are ignored. That is, there can be 0 or more keys + * with any valid JSON identifier, so long as the values match the constraints. + * + * @param example Example object with key/values e.g. `{ foo: 'bar', baz: 'qux'}` + * @param matchers Matchers to apply to each value + */ +export const eachValueMatches = ( + example: Record, + matchers: Matcher | Matcher[] +): RulesMatcher => ({ + 'pact:matcher:type': 'eachValue', + rules: Array.isArray(matchers) ? matchers : [matchers], + value: example, + // Unsure if the full object is provided, or just a template k/v pair + // value: { + // [keyTemplate]: template, + // }, +}); + +/** + * Matches HTTP status codes by their range description, or by a list of specific codes. + * + * @param example Example status code to use + * @param range Allowed status codes + */ +export const matchStatus = ( + example: number, + range: HTTPResponseStatusClass | number[] +): StatusCodeMatcher => ({ + value: example, + 'pact:matcher:type': 'statusCode', + status: range, +}); + /** * Array where each element must match the given template * @param template Template to base the comparison on diff --git a/src/v3/types.ts b/src/v3/types.ts index f9bb03795..3c5501391 100644 --- a/src/v3/types.ts +++ b/src/v3/types.ts @@ -68,6 +68,14 @@ export interface ProviderStateInjectedValue extends Matcher { expression: string; } +export interface StatusCodeMatcher extends Matcher { + status: string | number[]; +} + +export interface RulesMatcher extends Matcher { + rules: Matcher[]; +} + /** * Options for the mock server */ @@ -143,7 +151,7 @@ export interface V3Request { } export interface V3Response { - status: number; + status: number | Matcher; headers?: TemplateHeaders; body?: unknown; contentType?: string; @@ -154,3 +162,20 @@ export interface V3MockServer { url: string; id: string; } + +export enum HTTPResponseStatusClass { + // Informational responses (100–199) + Information = 'information', + // Successful responses (200–299) + Success = 'success', + // Redirects (300–399) + Redirect = 'redirect', + // Client errors (400–499) + ClientError = 'clientError', + // Server errors (500–599) + ServerError = 'serverError', + // Non-error response(< 400) + NonError = 'nonError', + // Any error response (>= 400) + Error = 'error', +}