diff --git a/package-lock.json b/package-lock.json index 5cc85b6e..2b19c9de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,6 @@ "workspaces": [ "packages/*" ], - "dependencies": { - "@nx/nx-linux-arm-gnueabihf": "20.1.3" - }, "devDependencies": { "@contract-case/eslint-config-case-maintainer": "0.1.1", "example-extractor": "^0.0.4", @@ -8988,6 +8985,13 @@ "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", "license": "MIT" }, + "node_modules/@types/git-rev-sync": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/git-rev-sync/-/git-rev-sync-2.0.2.tgz", + "integrity": "sha512-ygFM5I5q4VJjU+xrb2MSzgj4BpC6HUzMnmfWp4d8bgAw/XFkJTiKn1uaNpOOT1gw+IxELyfY97JA6sRBv7J9sA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/google-protobuf": { "version": "3.15.12", "dev": true, @@ -31291,6 +31295,7 @@ "chalk": "^4.1.2", "express": "^4.21.0", "filenamify": "^4.3.0", + "git-rev-sync": "^3.0.1", "is-ci": "^3.0.1", "mkdirp": "^3.0.1", "pretty-format": "^29.6.1", @@ -31303,6 +31308,7 @@ "@contract-case/eslint-config-case-maintainer": "0.1.1", "@types/current-git-branch": "^1.1.6", "@types/express": "^4.17.14", + "@types/git-rev-sync": "^2.0.2", "@types/is-ci": "^3.0.0", "@types/jest": "^29.5.12", "@types/qs": "^6.9.16", diff --git a/packages/case-connector-proto/proto/contract_case_stream.proto b/packages/case-connector-proto/proto/contract_case_stream.proto index 3d9bfe2b..56f70a5f 100644 --- a/packages/case-connector-proto/proto/contract_case_stream.proto +++ b/packages/case-connector-proto/proto/contract_case_stream.proto @@ -35,10 +35,13 @@ message ContractCaseConfig { 15; // Long term, this will be moved map mock_config = 16; + + google.protobuf.StringValue auto_version_from = 17; } // Indicates a successful response with no payload -message ResultSuccess {} +message ResultSuccess { +} message ResultSuccessHasMapPayload { // Always a map of Strings -> Strings google.protobuf.Struct map = 1; @@ -74,7 +77,9 @@ message StateHandlerHandle { } // A reference to a trigger function that can be invoked -message TriggerFunctionHandle { google.protobuf.StringValue handle = 1; } +message TriggerFunctionHandle { + google.protobuf.StringValue handle = 1; +} // Requests from client / host @@ -100,11 +105,14 @@ message RunRejectingExampleRequest { // From host to core, instructs the core to strip the matchers from a given // matcher / data object -message StripMatchersRequest { google.protobuf.Struct matcher_or_data = 2; } +message StripMatchersRequest { + google.protobuf.Struct matcher_or_data = 2; +} // From host to core, instructs the core to finish the current contract // definition -message EndDefinitionRequest {} +message EndDefinitionRequest { +} // From host to core, instructs the core to load a plugin message LoadPluginRequest { @@ -116,7 +124,9 @@ message LoadPluginRequest { // Responses from server // From Core to Host, instructs the host to run a given state handler -message RunStateHandlerRequest { StateHandlerHandle state_handler_handle = 1; } +message RunStateHandlerRequest { + StateHandlerHandle state_handler_handle = 1; +} // From Core to Host, requests the host invoke one of the user-provided // triggers. @@ -129,12 +139,14 @@ message TriggerFunctionRequest { SetupInfo setup = 3; } -message CoreFunctionHandle { google.protobuf.StringValue handle = 1; } +message CoreFunctionHandle { + google.protobuf.StringValue handle = 1; +} // Describes the setup of the currently executing Interaction message SetupInfo { // The resolved values for the state variables - map stateVariables = 1; + map state_variables = 1; // The values of any mock setup information map mock = 2; // Callbacks handles for functions @@ -276,7 +288,9 @@ message PrintTestTitleRequest { // // Most messages must be acknowledged with a response - this indicates the // return of the previous message. -message ResultResponse { BoundaryResult result = 1; } +message ResultResponse { + BoundaryResult result = 1; +} // From the Host to the Core, requests that a verification session begins message BeginVerificationRequest { @@ -287,7 +301,8 @@ message BeginVerificationRequest { } // From the Host to the Core, requests a list of the available contracts -message AvailableContractDefinitions {} +message AvailableContractDefinitions { +} // From the Host to the Core, instructs the core that the current verification // session is now configured, and asks it to execute the verification. diff --git a/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.d.ts b/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.d.ts index ba6376b7..a9ecb1dd 100644 --- a/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.d.ts +++ b/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.d.ts @@ -111,6 +111,13 @@ export class ContractCaseConfig extends jspb.Message { getMockConfigMap(): jspb.Map; clearMockConfigMap(): void; + hasAutoVersionFrom(): boolean; + clearAutoVersionFrom(): void; + getAutoVersionFrom(): google_protobuf_wrappers_pb.StringValue | undefined; + setAutoVersionFrom( + value?: google_protobuf_wrappers_pb.StringValue, + ): ContractCaseConfig; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ContractCaseConfig.AsObject; static toObject( @@ -152,6 +159,7 @@ export namespace ContractCaseConfig { baseUrlUnderTest?: google_protobuf_wrappers_pb.StringValue.AsObject; mockConfigMap: Array<[string, string]>; + autoVersionFrom?: google_protobuf_wrappers_pb.StringValue.AsObject; }; export class UsernamePassword extends jspb.Message { diff --git a/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.js b/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.js index 2a341f6d..7b54f00a 100644 --- a/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.js +++ b/packages/case-connector-proto/src/grpc/proto/contract_case_stream_pb.js @@ -833,7 +833,8 @@ proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.toObject = functi triggerAndTestsMap: (f = msg.getTriggerAndTestsMap()) ? f.toObject(includeInstance, proto.io.contract_testing.contractcase.grpc.TriggerFunctionHandle.toObject) : [], triggerAndTest: (f = msg.getTriggerAndTest()) && proto.io.contract_testing.contractcase.grpc.TriggerFunctionHandle.toObject(includeInstance, f), baseUrlUnderTest: (f = msg.getBaseUrlUnderTest()) && google_protobuf_wrappers_pb.StringValue.toObject(includeInstance, f), - mockConfigMap: (f = msg.getMockConfigMap()) ? f.toObject(includeInstance, undefined) : [] + mockConfigMap: (f = msg.getMockConfigMap()) ? f.toObject(includeInstance, undefined) : [], + autoVersionFrom: (f = msg.getAutoVersionFrom()) && google_protobuf_wrappers_pb.StringValue.toObject(includeInstance, f) }; if (includeInstance) { @@ -952,6 +953,11 @@ proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.deserializeBinary jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", ""); }); break; + case 17: + var value = new google_protobuf_wrappers_pb.StringValue; + reader.readMessage(value,google_protobuf_wrappers_pb.StringValue.deserializeBinaryFromReader); + msg.setAutoVersionFrom(value); + break; default: reader.skipField(); break; @@ -1101,6 +1107,14 @@ proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.serializeBinaryTo if (f && f.getLength() > 0) { f.serializeBinary(16, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString); } + f = message.getAutoVersionFrom(); + if (f != null) { + writer.writeMessage( + 17, + f, + google_protobuf_wrappers_pb.StringValue.serializeBinaryToWriter + ); + } }; @@ -1869,6 +1883,43 @@ proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.prototype.clearMo return this;}; +/** + * optional google.protobuf.StringValue auto_version_from = 17; + * @return {?proto.google.protobuf.StringValue} + */ +proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.prototype.getAutoVersionFrom = function() { + return /** @type{?proto.google.protobuf.StringValue} */ ( + jspb.Message.getWrapperField(this, google_protobuf_wrappers_pb.StringValue, 17)); +}; + + +/** + * @param {?proto.google.protobuf.StringValue|undefined} value + * @return {!proto.io.contract_testing.contractcase.grpc.ContractCaseConfig} returns this +*/ +proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.prototype.setAutoVersionFrom = function(value) { + return jspb.Message.setWrapperField(this, 17, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.io.contract_testing.contractcase.grpc.ContractCaseConfig} returns this + */ +proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.prototype.clearAutoVersionFrom = function() { + return this.setAutoVersionFrom(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.io.contract_testing.contractcase.grpc.ContractCaseConfig.prototype.hasAutoVersionFrom = function() { + return jspb.Message.getField(this, 17) != null; +}; + + diff --git a/packages/case-connector/src/connectors/case-boundary/internals/boundary/config.types.ts b/packages/case-connector/src/connectors/case-boundary/internals/boundary/config.types.ts index 51f81dca..da7ba7b1 100644 --- a/packages/case-connector/src/connectors/case-boundary/internals/boundary/config.types.ts +++ b/packages/case-connector/src/connectors/case-boundary/internals/boundary/config.types.ts @@ -20,6 +20,12 @@ export interface UserNamePassword { /** * Configure a ContractCase run. See the [configuration documentation](https://case.contract-testing.io/docs/reference/configuring) for more details. * + * Note that many of these types are more permissive than the reality - for + * example, the constrained string types are listed here as `string`, whereas + * the core will only accept a limited number of values (eg, log level accepts + * `'warn'` `'error'` etc, but not `'gibbons'`). Callers may rely on the + * boundary mappers to throw `CaseConfigurationErrors` in the case of invalid strings. + * * @public */ export interface ContractCaseBoundaryConfig { @@ -146,6 +152,16 @@ export interface ContractCaseBoundaryConfig { */ readonly mockConfig?: Record>; + /** + * How to generate version numbers for the system under test + * + * - `"TAG"`: Use {@link https://github.com/absolute-version/absolute-version-js | absolute version} to generate version numbers from the git tag + * - `"GIT_SHA"`: Use the full git sha + * + * @defaultValue `TAG` + */ + readonly autoVersionFrom?: string; + /** * The internals map allows configuration of low-level ContractCase features. * It contains no end-user configuration properties, and is intended to be used for customisation diff --git a/packages/case-connector/src/connectors/case-boundary/internals/mappers/config.ts b/packages/case-connector/src/connectors/case-boundary/internals/mappers/config.ts index 81d48247..4526d328 100644 --- a/packages/case-connector/src/connectors/case-boundary/internals/mappers/config.ts +++ b/packages/case-connector/src/connectors/case-boundary/internals/mappers/config.ts @@ -43,6 +43,25 @@ const mapPublish = ( } }; +const mapAutoVersionFrom = (autoVersionFrom: string): 'TAG' | 'GIT_SHA' => { + switch (autoVersionFrom.toUpperCase()) { + case 'TAG': { + return 'TAG'; + } + case 'GIT_SHA': + return 'GIT_SHA'; + default: + throw new CaseConfigurationError( + `The autoVersionFrom setting '${autoVersionFrom}' is not a valid auto version setting`, + ); + } +}; + +/** + * SeparateConfig only exists because at one point these two things were separate. + * At some point, we should refactor this so that the config shape doesn't need to + * be mapped into this intermediate type. + */ type SeparateConfig = { config: CaseConfig; partialInvoker: Partial>; @@ -54,10 +73,14 @@ export const convertConfig = ({ triggerAndTests, logLevel, publish, + autoVersionFrom, ...incoming }: ContractCaseBoundaryConfig): SeparateConfig => ({ config: { ...incoming, + ...(autoVersionFrom + ? { autoVersionFrom: mapAutoVersionFrom(autoVersionFrom) } + : {}), ...(logLevel ? { logLevel: mapLogLevel(logLevel) } : {}), ...(publish ? { publish: mapPublish(publish) } : {}), // baseUrlUnderTest: `http://localhost:${8084}`, diff --git a/packages/case-connector/src/connectors/grpc/requestMappers/config/config.ts b/packages/case-connector/src/connectors/grpc/requestMappers/config/config.ts index 1c6b2084..ddbfca14 100644 --- a/packages/case-connector/src/connectors/grpc/requestMappers/config/config.ts +++ b/packages/case-connector/src/connectors/grpc/requestMappers/config/config.ts @@ -158,6 +158,8 @@ const mapAllConfigFields = ( ), triggerAndTest: mapTriggerAndTest(config, executeCall, functionRegistry), mockConfig: mapMockConfig(config.getMockConfigMap()), + + autoVersionFrom: unboxOrUndefined(config.getAutoVersionFrom()), }); export const mapConfig = ( diff --git a/packages/case-connector/src/domain/types.ts b/packages/case-connector/src/domain/types.ts index 45dc49c4..c4bac274 100644 --- a/packages/case-connector/src/domain/types.ts +++ b/packages/case-connector/src/domain/types.ts @@ -27,6 +27,8 @@ export type ContractCaseConnectorConfig = { triggerAndTest: ConnectorTriggerFunction; mockConfig: Record>; + + autoVersionFrom: string; }; export type DefinitionId = string; diff --git a/packages/case-core/package.json b/packages/case-core/package.json index 50d0ff7c..2267be96 100644 --- a/packages/case-core/package.json +++ b/packages/case-core/package.json @@ -53,11 +53,12 @@ "url": "https://github.com/case-contract-testing/case/issues" }, "devDependencies": { + "@contract-case/case-definition-dsl": "^0.18.0", "@contract-case/case-maintainer-config": "0.1.0", "@contract-case/eslint-config-case-maintainer": "0.1.1", - "@contract-case/case-definition-dsl": "^0.18.0", "@types/current-git-branch": "^1.1.6", "@types/express": "^4.17.14", + "@types/git-rev-sync": "^2.0.2", "@types/is-ci": "^3.0.0", "@types/jest": "^29.5.12", "@types/qs": "^6.9.16", @@ -74,9 +75,9 @@ "typescript": "5.5.4" }, "dependencies": { - "@contract-case/case-core-plugin-http-dsl": "0.18.0", - "@contract-case/case-core-plugin-http": "0.18.0", "@contract-case/case-core-plugin-function": "0.18.0", + "@contract-case/case-core-plugin-http": "0.18.0", + "@contract-case/case-core-plugin-http-dsl": "0.18.0", "@contract-case/case-entities-internal": "0.18.0", "@contract-case/case-plugin-base": "0.18.0", "@contract-case/case-plugin-dsl-types": "0.18.0", @@ -86,6 +87,7 @@ "chalk": "^4.1.2", "express": "^4.21.0", "filenamify": "^4.3.0", + "git-rev-sync": "^3.0.1", "is-ci": "^3.0.1", "mkdirp": "^3.0.1", "pretty-format": "^29.6.1", diff --git a/packages/case-core/src/__tests__/testContext.ts b/packages/case-core/src/__tests__/testContext.ts index 42bfa664..9eeeb280 100644 --- a/packages/case-core/src/__tests__/testContext.ts +++ b/packages/case-core/src/__tests__/testContext.ts @@ -38,6 +38,7 @@ export const EMPTY_DATA_CONTEXT: DataContext = { '_case:currentRun:context:testName': '', '_case:currentRun:context:variables': {}, '_case:currentRun:context:defaultConfig': {}, + '_case:currentRun:context:autoVersionFrom': 'TAG', logger: EMPTY_LOGGER, resultPrinter: { printError(): void {}, diff --git a/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.spec.ts b/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.spec.ts new file mode 100644 index 00000000..f5746e8f --- /dev/null +++ b/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.spec.ts @@ -0,0 +1,27 @@ +import { EMPTY_DATA_CONTEXT } from '../../__tests__/testContext'; +import { makeEnvironment } from './BuildEnvironment'; + +describe('build environment', () => { + const buildEnvironment = makeEnvironment(); + + describe('version', () => { + describe('with defaults', () => { + it('starts with a semver tag (this repository always will)', () => { + expect(buildEnvironment.version(EMPTY_DATA_CONTEXT)).toMatch( + /^\d+\.\d+\.\d+/, + ); + }); + }); + + describe('with GIT_SHA', () => { + it('should be the right length for a full git sha', () => { + expect( + buildEnvironment.version({ + ...EMPTY_DATA_CONTEXT, + '_case:currentRun:context:autoVersionFrom': 'GIT_SHA', + }), + ).toHaveLength(40); + }); + }); + }); +}); diff --git a/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.ts b/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.ts index 58ebabe3..cffc3c27 100644 --- a/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.ts +++ b/packages/case-core/src/connectors/BuildEnvironment/BuildEnvironment.ts @@ -1,11 +1,27 @@ import isCi from 'is-ci'; import childProcess from 'child_process'; +import { long } from 'git-rev-sync'; import { versionFromGitTag } from 'absolute-version'; +import { CaseConfigurationError } from '@contract-case/case-plugin-base'; import { BuildEnvironment } from '../../core/types'; export const makeEnvironment = (): BuildEnvironment => ({ isCi: () => isCi, - version: () => versionFromGitTag(), + version: (context) => { + switch (context['_case:currentRun:context:autoVersionFrom']) { + case 'TAG': + return versionFromGitTag(); + case 'GIT_SHA': + return long(); + default: + throw new CaseConfigurationError( + `Unrecognised value for autoVersionFrom: '${context['_case:currentRun:context:autoVersionFrom']}'. + Understood values are: TAG, GIT_SHA`, + context, + ); + } + }, + branch: () => childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(), }); diff --git a/packages/case-core/src/connectors/broker/broker.case.spec.ts b/packages/case-core/src/connectors/broker/broker.case.spec.ts index 1f587a6e..aa3c435d 100644 --- a/packages/case-core/src/connectors/broker/broker.case.spec.ts +++ b/packages/case-core/src/connectors/broker/broker.case.spec.ts @@ -66,6 +66,7 @@ const emptyContext: DataContext = { '_case:currentRun:context:connectorClient': 'tests', '_case:currentRun:context:parentVersions': ['broker-test'], '_case:currentRun:context:defaultConfig': {}, + '_case:currentRun:context:autoVersionFrom': 'TAG', }; const contractFilename = 'case-contracts/case-pact-broker.case.json'; diff --git a/packages/case-core/src/connectors/dependencies.ts b/packages/case-core/src/connectors/dependencies.ts index fba8fc6f..2ff08173 100644 --- a/packages/case-core/src/connectors/dependencies.ts +++ b/packages/case-core/src/connectors/dependencies.ts @@ -18,6 +18,7 @@ const DEFAULT_CONFIG: Partial = { publish: 'ONLY_IN_CI', printResults: true, testRunId: DEFAULT_TEST_ID, + autoVersionFrom: 'TAG', }; export const writerDependencies: ( diff --git a/packages/case-core/src/core/BrokerService/BrokerService.ts b/packages/case-core/src/core/BrokerService/BrokerService.ts index 8ed0b734..86611e0b 100644 --- a/packages/case-core/src/core/BrokerService/BrokerService.ts +++ b/packages/case-core/src/core/BrokerService/BrokerService.ts @@ -57,7 +57,7 @@ export class BrokerService { .publishVerificationResults( contract, success, - this.environment.version(), + this.environment.version(context), this.environment.branch(), addLocation(':PublishingContractAdvanced', context), ) @@ -100,7 +100,7 @@ export class BrokerService { return this.broker .publishContractAdvanced( contract, - this.environment.version(), + this.environment.version(context), this.environment.branch(), addLocation(':PublishingContractAdvanced', context), ) diff --git a/packages/case-core/src/core/config/types.ts b/packages/case-core/src/core/config/types.ts index 50628790..5282105c 100644 --- a/packages/case-core/src/core/config/types.ts +++ b/packages/case-core/src/core/config/types.ts @@ -20,6 +20,7 @@ export const stringConfigArgs: Array> = [ 'publish', 'brokerCiAccessToken', 'brokerBaseUrl', + 'autoVersionFrom', ]; export interface BaseCaseConfig { @@ -111,6 +112,18 @@ export interface BaseCaseConfig { password: string; }; + /** + * Where to automatically get the version for the service under test. + * + * - `TAG` get the version from the git tag using absolute-version + * - `GIT_SHA` get the version from the full git sha + * + * If there is no git repository, then versioning will fail. + * + * @defaultValue `'TAG'` + */ + autoVersionFrom?: 'TAG' | 'GIT_SHA'; + /** * The internals map allows configuration of low-level ContractCase features. * It contains no end-user configuration properties, and is intended to be used for customisation diff --git a/packages/case-core/src/core/types.environment.ts b/packages/case-core/src/core/types.environment.ts index 1070367e..c48b6ef5 100644 --- a/packages/case-core/src/core/types.environment.ts +++ b/packages/case-core/src/core/types.environment.ts @@ -1,3 +1,5 @@ +import { DataContext } from '@contract-case/case-plugin-base'; + export interface BuildEnvironment { /** * What branch of the git repository is under test in this run @@ -15,8 +17,10 @@ export interface BuildEnvironment { /** * Get the version for the service under test + * + * @param context - the {@link DataContext} for this run */ - version(): string; + version(context: DataContext): string; } export type MakeEnvironment = () => BuildEnvironment; diff --git a/packages/case-core/src/index.http.client.define.spec.ts b/packages/case-core/src/index.http.client.define.spec.ts index 64b6d42c..d0074389 100644 --- a/packages/case-core/src/index.http.client.define.spec.ts +++ b/packages/case-core/src/index.http.client.define.spec.ts @@ -99,25 +99,22 @@ describe('e2e http consumer driven', () => { const state = inState('Server is down'); describe('No body server response', () => { it('calls server health', () => - contract.runRejectingExample( - { - states: [state], - definition: willSendHttpRequest({ - request: { - method: 'GET', - path: '/health', - }, - response: { - status: httpStatus(['4XX', '5XX']), - }, - }), - trigger: sendHealthRequest, - testErrorResponse: (e) => { - expect(e).toBeInstanceOf(ApiError); + contract.runRejectingExample({ + states: [state], + definition: willSendHttpRequest({ + request: { + method: 'GET', + path: '/health', + }, + response: { + status: httpStatus(['4XX', '5XX']), }, + }), + trigger: sendHealthRequest, + testErrorResponse: (e) => { + expect(e).toBeInstanceOf(ApiError); }, - { logLevel: 'deepMaintainerDebug' }, - )); + })); }); describe('specific server response', () => { diff --git a/packages/case-plugin-base/api-extractor/case-plugin-base.api.json b/packages/case-plugin-base/api-extractor/case-plugin-base.api.json index d72d656a..3f0fbbff 100644 --- a/packages/case-plugin-base/api-extractor/case-plugin-base.api.json +++ b/packages/case-plugin-base/api-extractor/case-plugin-base.api.json @@ -1664,7 +1664,7 @@ { "kind": "TypeAlias", "canonicalReference": "@contract-case/case-plugin-base!DataContext:type", - "docComment": "/**\n * Just the parts of the context that have data\n *\n * @public\n */\n", + "docComment": "/**\n * Convenience type for just the parts of the context that have data - everything in this type will be serialisable.\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -1754,7 +1754,7 @@ { "kind": "TypeAlias", "canonicalReference": "@contract-case/case-plugin-base!DefaultContext:type", - "docComment": "/**\n * The default context for a run\n *\n * @public\n */\n", + "docComment": "/**\n * The settings that are set as default context for a run\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -1794,7 +1794,7 @@ }, { "kind": "Content", - "text": ";\n '_case:currentRun:context:contractMode': 'write' | 'read';\n '_case:currentRun:context:printResults': boolean;\n '_case:currentRun:context:connectorClient': string;\n}" + "text": ";\n '_case:currentRun:context:contractMode': 'write' | 'read';\n '_case:currentRun:context:printResults': boolean;\n '_case:currentRun:context:connectorClient': string;\n '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA';\n}" }, { "kind": "Content", @@ -2334,7 +2334,7 @@ { "kind": "Interface", "canonicalReference": "@contract-case/case-plugin-base!LogContext:interface", - "docComment": "/**\n * Part of the context with the logger attached.\n *\n * @public\n */\n", + "docComment": "/**\n * Part of the context with the logger attached. Useful if you just want to pass the logging functions to something. This is used in a few places where the whole context isn't available (eg, before the context has been constructed).\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -2791,7 +2791,7 @@ { "kind": "TypeAlias", "canonicalReference": "@contract-case/case-plugin-base!MatchContext:type", - "docComment": "/**\n * The part of the context used during a matching run - contains traversal functions\n *\n * @public\n */\n", + "docComment": "/**\n * The part of the context used during a matching run - contains traversal functions and any lookup functions that arbitrary matchers or mocks might need.\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -2845,7 +2845,7 @@ { "kind": "Interface", "canonicalReference": "@contract-case/case-plugin-base!MatchContextByExact:interface", - "docComment": "/**\n * Indicates that the matchers should default to exact matching\n *\n * @public\n */\n", + "docComment": "/**\n * If the context is assignable to this type, it indicates that the matchers should default to exact matching\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", @@ -2890,7 +2890,7 @@ { "kind": "Interface", "canonicalReference": "@contract-case/case-plugin-base!MatchContextByType:interface", - "docComment": "/**\n * Indicates that the matchers should default to shape matching\n *\n * @public\n */\n", + "docComment": "/**\n * If the context is assignable to this type, it indicates that the matchers should default to shape (ie, type) matching\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", diff --git a/packages/case-plugin-base/api-extractor/case-plugin-base.api.md b/packages/case-plugin-base/api-extractor/case-plugin-base.api.md index 964ba9e8..3e16759a 100644 --- a/packages/case-plugin-base/api-extractor/case-plugin-base.api.md +++ b/packages/case-plugin-base/api-extractor/case-plugin-base.api.md @@ -144,6 +144,7 @@ export type DefaultContext = LogLevelContext & { '_case:currentRun:context:contractMode': 'write' | 'read'; '_case:currentRun:context:printResults': boolean; '_case:currentRun:context:connectorClient': string; + '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA'; }; // @public @@ -396,6 +397,8 @@ export type ResultFormatter = { // // @internal export interface RunContext extends Partial { + // (undocumented) + '_case:currentRun:context:autoVersionFrom'?: 'TAG' | 'GIT_SHA'; // (undocumented) '_case:currentRun:context:brokerBaseUrl'?: string; // (undocumented) @@ -491,7 +494,7 @@ export class VerifyTriggerReturnObjectError extends Error { // Warnings were encountered during analysis: // -// src/context/types.ts:470:3 - (ae-forgotten-export) The symbol "CaseExample" needs to be exported by the entry point index.d.ts +// src/context/types.ts:490:3 - (ae-forgotten-export) The symbol "CaseExample" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/case-plugin-base/docs/case-plugin-base.datacontext.md b/packages/case-plugin-base/docs/case-plugin-base.datacontext.md index 4bd66603..3c932c0d 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.datacontext.md +++ b/packages/case-plugin-base/docs/case-plugin-base.datacontext.md @@ -4,7 +4,7 @@ ## DataContext type -Just the parts of the context that have data +Convenience type for just the parts of the context that have data - everything in this type will be serialisable. **Signature:** diff --git a/packages/case-plugin-base/docs/case-plugin-base.defaultcontext.md b/packages/case-plugin-base/docs/case-plugin-base.defaultcontext.md index 1f93717a..9c88372c 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.defaultcontext.md +++ b/packages/case-plugin-base/docs/case-plugin-base.defaultcontext.md @@ -4,7 +4,7 @@ ## DefaultContext type -The default context for a run +The settings that are set as default context for a run **Signature:** @@ -15,6 +15,7 @@ export type DefaultContext = LogLevelContext & { '_case:currentRun:context:contractMode': 'write' | 'read'; '_case:currentRun:context:printResults': boolean; '_case:currentRun:context:connectorClient': string; + '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA'; }; ``` **References:** [MATCH\_BY\_TYPE](./case-plugin-base.match_by_type.md), [MATCH\_BY\_EXACT](./case-plugin-base.match_by_exact.md), [SERIALISABLE\_TO\_JSON](./case-plugin-base.serialisable_to_json.md) diff --git a/packages/case-plugin-base/docs/case-plugin-base.logcontext.md b/packages/case-plugin-base/docs/case-plugin-base.logcontext.md index f6600ac3..30ed7b28 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.logcontext.md +++ b/packages/case-plugin-base/docs/case-plugin-base.logcontext.md @@ -4,7 +4,7 @@ ## LogContext interface -Part of the context with the logger attached. +Part of the context with the logger attached. Useful if you just want to pass the logging functions to something. This is used in a few places where the whole context isn't available (eg, before the context has been constructed). **Signature:** diff --git a/packages/case-plugin-base/docs/case-plugin-base.matchcontext.md b/packages/case-plugin-base/docs/case-plugin-base.matchcontext.md index 7e10f060..d87f9ca5 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.matchcontext.md +++ b/packages/case-plugin-base/docs/case-plugin-base.matchcontext.md @@ -4,7 +4,7 @@ ## MatchContext type -The part of the context used during a matching run - contains traversal functions +The part of the context used during a matching run - contains traversal functions and any lookup functions that arbitrary matchers or mocks might need. **Signature:** diff --git a/packages/case-plugin-base/docs/case-plugin-base.matchcontextbyexact.md b/packages/case-plugin-base/docs/case-plugin-base.matchcontextbyexact.md index e3409bcd..4cd4ed12 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.matchcontextbyexact.md +++ b/packages/case-plugin-base/docs/case-plugin-base.matchcontextbyexact.md @@ -4,7 +4,7 @@ ## MatchContextByExact interface -Indicates that the matchers should default to exact matching +If the context is assignable to this type, it indicates that the matchers should default to exact matching **Signature:** diff --git a/packages/case-plugin-base/docs/case-plugin-base.matchcontextbytype.md b/packages/case-plugin-base/docs/case-plugin-base.matchcontextbytype.md index 023df1df..959bdfac 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.matchcontextbytype.md +++ b/packages/case-plugin-base/docs/case-plugin-base.matchcontextbytype.md @@ -4,7 +4,7 @@ ## MatchContextByType interface -Indicates that the matchers should default to shape matching +If the context is assignable to this type, it indicates that the matchers should default to shape (ie, type) matching **Signature:** diff --git a/packages/case-plugin-base/docs/case-plugin-base.md b/packages/case-plugin-base/docs/case-plugin-base.md index 581fb2fe..f75720ef 100644 --- a/packages/case-plugin-base/docs/case-plugin-base.md +++ b/packages/case-plugin-base/docs/case-plugin-base.md @@ -350,7 +350,7 @@ The base type for a case matcher descriptor that has this string constant -Part of the context with the logger attached. +Part of the context with the logger attached. Useful if you just want to pass the logging functions to something. This is used in a few places where the whole context isn't available (eg, before the context has been constructed). @@ -372,7 +372,7 @@ Describes the logger object -Indicates that the matchers should default to exact matching +If the context is assignable to this type, it indicates that the matchers should default to exact matching @@ -383,7 +383,7 @@ Indicates that the matchers should default to exact matching -Indicates that the matchers should default to shape matching +If the context is assignable to this type, it indicates that the matchers should default to shape (ie, type) matching @@ -645,7 +645,7 @@ Represents a plugin for the ContractCase contract testing framework. A plugin ca -Just the parts of the context that have data +Convenience type for just the parts of the context that have data - everything in this type will be serialisable. @@ -656,7 +656,7 @@ Just the parts of the context that have data -The default context for a run +The settings that are set as default context for a run @@ -689,7 +689,7 @@ The log levels available -The part of the context used during a matching run - contains traversal functions +The part of the context used during a matching run - contains traversal functions and any lookup functions that arbitrary matchers or mocks might need. diff --git a/packages/case-plugin-base/src/context/context.ts b/packages/case-plugin-base/src/context/context.ts index 95667c66..a04f969f 100644 --- a/packages/case-plugin-base/src/context/context.ts +++ b/packages/case-plugin-base/src/context/context.ts @@ -32,6 +32,7 @@ const DEFAULT_CONTEXT: DefaultContext = { '_case:currentRun:context:location': [], '_case:currentRun:context:contractMode': 'write', '_case:currentRun:context:logLevel': 'warn', + '_case:currentRun:context:autoVersionFrom': 'TAG', '_case:currentRun:context:printResults': true, '_case:context:matchBy': 'exact', '_case:context:serialisableTo': 'json', diff --git a/packages/case-plugin-base/src/context/types.ts b/packages/case-plugin-base/src/context/types.ts index 0026ccf9..eab5f905 100644 --- a/packages/case-plugin-base/src/context/types.ts +++ b/packages/case-plugin-base/src/context/types.ts @@ -303,7 +303,7 @@ export interface ContractFileConfig { export type HasContractFileConfig = DataContext & ContractFileConfig; /** - * The default context for a run + * The settings that are set as default context for a run * * @public */ @@ -327,11 +327,22 @@ export type DefaultContext = LogLevelContext & { * What's the connector client (ie, host language) for this run? */ '_case:currentRun:context:connectorClient': string; + + /** + * How to generate the version for the system under test + */ + '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA'; }; +/** + * These elements of context are injectable by the user + * + * @internal + */ interface InjectableContext { '_case:currentRun:context:baseUrlUnderTest'?: string; '_case:currentRun:context:contractMode': 'write' | 'read'; + '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA'; } /** @@ -381,6 +392,7 @@ export interface RunContext // TODO: These are from CaseConfig and should be auto generated '_case:currentRun:context:throwOnFail'?: boolean; '_case:currentRun:context:brokerCiAccessToken'?: string; + '_case:currentRun:context:autoVersionFrom'?: 'TAG' | 'GIT_SHA'; '_case:currentRun:context:publish'?: | false | true @@ -398,7 +410,8 @@ export interface RunContext } /** - * Indicates that the matchers should default to shape matching + * If the context is assignable to this type, it indicates that + * the matchers should default to shape (ie, type) matching * * @public */ @@ -407,7 +420,8 @@ export interface MatchContextByType { } /** - * Indicates that the matchers should default to exact matching + * If the context is assignable to this type, it indicates + * that the matchers should default to exact matching * * @public */ @@ -426,7 +440,9 @@ export interface HttpTestContext { } /** - * Part of the context with the logger attached. + * Part of the context with the logger attached. Useful if you just want to pass + * the logging functions to something. This is used in a few places where the whole + * context isn't available (eg, before the context has been constructed). * * @public */ @@ -440,7 +456,8 @@ export interface LogContext { } /** - * Just the parts of the context that have data + * Convenience type for just the parts of the context that have data - everything in this type + * will be serialisable. * * @public */ @@ -453,6 +470,7 @@ export type DataContext = DefaultContext & /** * The part of the context used during a matching run - contains traversal functions + * and any lookup functions that arbitrary matchers or mocks might need. * * @public */ @@ -462,7 +480,9 @@ export type MatchContext = DataContext & HasMakeLookupFn; /** - * The result formatter object + * The result formatter is used for printing results. The core doesn't know how these are + * formatted and printed - this is up to the host DSL wrappers. + * * @internal */ export type ResultFormatter = { diff --git a/packages/case-plugin-base/temp/case-plugin-base.api.md b/packages/case-plugin-base/temp/case-plugin-base.api.md index 964ba9e8..3e16759a 100644 --- a/packages/case-plugin-base/temp/case-plugin-base.api.md +++ b/packages/case-plugin-base/temp/case-plugin-base.api.md @@ -144,6 +144,7 @@ export type DefaultContext = LogLevelContext & { '_case:currentRun:context:contractMode': 'write' | 'read'; '_case:currentRun:context:printResults': boolean; '_case:currentRun:context:connectorClient': string; + '_case:currentRun:context:autoVersionFrom': 'TAG' | 'GIT_SHA'; }; // @public @@ -396,6 +397,8 @@ export type ResultFormatter = { // // @internal export interface RunContext extends Partial { + // (undocumented) + '_case:currentRun:context:autoVersionFrom'?: 'TAG' | 'GIT_SHA'; // (undocumented) '_case:currentRun:context:brokerBaseUrl'?: string; // (undocumented) @@ -491,7 +494,7 @@ export class VerifyTriggerReturnObjectError extends Error { // Warnings were encountered during analysis: // -// src/context/types.ts:470:3 - (ae-forgotten-export) The symbol "CaseExample" needs to be exported by the entry point index.d.ts +// src/context/types.ts:490:3 - (ae-forgotten-export) The symbol "CaseExample" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/contract-case-jest/src/entities/config.ts b/packages/contract-case-jest/src/entities/config.ts index 712c72f9..cc4c46d3 100644 --- a/packages/contract-case-jest/src/entities/config.ts +++ b/packages/contract-case-jest/src/entities/config.ts @@ -46,6 +46,8 @@ export interface ContractCaseConfig { * `"debug"` - Information to help users find out what is happening during their tests * * `"maintainerDebug" | "deepMaintainerDebug"` - debugging information for ContractCase maintainers + * + * @defaultValue `'warn'` */ readonly logLevel?: LogLevel; @@ -53,22 +55,31 @@ export interface ContractCaseConfig { * The directory where the contract will be written. If you provide this, ContractCase * will generate the filename for you (unless `contractFilename` is specified, * in which case this setting is ignored) + * + * @defaultValue The current working directory */ readonly contractDir?: string; /** * The filename where the contract will be written. If you * provide this, `contractDir` is ignored + * + * @defaultValue Generated automatically */ readonly contractFilename?: string; /** + * The test run ID, used in part to generate filenames and distinguish separate runs. + * This generally shouldn't need to be set by users. + * * @internal */ readonly testRunId?: string; /** * Whether or not the results should be printed + * + * @defaultValue true */ readonly printResults?: boolean; @@ -79,7 +90,7 @@ export interface ContractCaseConfig { * this setting. This includes exceptions thrown during trigger functions, but * does not include exceptions thrown by testResponse functions. * - * Default: `true` in contract definition, `false` in contract verification + * @defaultValue `true` in contract definition, `false` in contract verification */ readonly throwOnFail?: boolean; @@ -90,19 +101,23 @@ export interface ContractCaseConfig { * `"NEVER"` or `false` - never publish * `"ALWAYS"` or `true` - always publish (not recommended) * - * Default: `"ONLY_IN_CI"` + * @defaultValue `"ONLY_IN_CI"` */ readonly publish?: 'ONLY_IN_CI' | 'NEVER' | 'ALWAYS' | false | true; /** - * The access token to use for the contract broker. Must have CI scope. + * The access token to use for the contract broker. For a pact broker, this must have CI scope. * * If this is specified along with brokerBasicAuth, the basic auth is ignored. + * + * @defaultValue not set */ readonly brokerCiAccessToken?: string; /** * The base URL for the contract broker + * + * @defaultValue not set */ readonly brokerBaseUrl?: string; @@ -110,6 +125,8 @@ export interface ContractCaseConfig { * The basic authentication username and password to access the contract broker. * * If this is specified along with brokerCiAccessToken, the basic auth is ignored. + * + * @defaultValue not set */ readonly brokerBasicAuth?: UserNamePassword; @@ -117,15 +134,19 @@ export interface ContractCaseConfig { * State setup and teardown handlers for any states this test requires (see * [writing state handlers](https://case.contract-testing.io/docs/reference/state-handlers/)) * for more details + * + * @defaultValue No setup or teardown handlers */ readonly stateHandlers?: StateHandlers; /** - * Define the trigger and test function (if any) for this interaction pair. + * Define the trigger and test function (if any) for multiple examples under test. * * Keyed by `${requestName}::${responseName}` * - * Use a `TriggerGroupMap` to construct this + * Don't populate this directly, use a `TriggerGroupMap` to construct this. + * + * @defaultValue no triggers */ readonly triggers?: TriggerGroups; @@ -138,8 +159,20 @@ export interface ContractCaseConfig { /** * Configuration for any plugins or specific mock types. Keyed by the plugin / mock shortName + * + * @defaultValue empty config */ readonly mockConfig?: Record>; + + /** + * How to generate version numbers for the system under test + * + * - `"TAG"`: Use {@link https://github.com/absolute-version/absolute-version-js | absolute version} to generate version numbers from the git tag + * - `"GIT_SHA"`: Use the full git sha + * + * @defaultValue `TAG` + */ + readonly autoVersionFrom?: 'TAG' | 'GIT_SHA'; } export type ContractCaseVerifierConfig = Omit< diff --git a/packages/documentation/docs/reference/configuring.md b/packages/documentation/docs/reference/configuring.md index bf80148e..b41f4f50 100644 --- a/packages/documentation/docs/reference/configuring.md +++ b/packages/documentation/docs/reference/configuring.md @@ -12,9 +12,7 @@ optional partial config object as the last parameter to `runExample`, `runRejectingExample`, or `runVerification`. This is useful for overriding specifics for different tests (eg, for increasing the log level on one specific test). -You can view the options as a filter - if an option is not specified in the test -run, it will look to the next layer. Here are the layers, from most to least -important: +There are multiple places you can supply configuration. In general, the closer an option is to the `runExample` interaction, the higher precedence it has. Here are the places configuration can be specified, where the top of the list has the most precedence. 1. Options specified in the test run (`runExample`, `runRejectingExample`, or `runVerification`). 1. Options specified at contract creation time (`defineContract` or `verifyContract`) @@ -148,10 +146,41 @@ Whether to publish contracts or verification results to the broker. When set to: If `publish` is set, you must provide credentials and `brokerBaseUrl`. See above. -## Contract Testing configuration options +## General configuration -These options modify the behaviour of the contract testing engine. In general, -you should not need to change these defaults unless your use case is unusual. +### `autoVersionFrom` \["TAG" | "GIT_SHA"] + +Default: `"TAG"` + +How to determine the version for the service under test. It's important that each change to your service has a unique version, so ContractCase uses your git repository to determine the version. + +- `TAG` (default) This setting will generate a human-friendly version from using [absolute-version](https://github.com/absolute-version/absolute-version-js?). Most useful if use +- `GIT_SHA` get the version from the full git sha + +If there is no git repository, versioning will fail. Support for non-git repositories will be added in the future. If this is something you need prioritised, please open an issue. + +### `stateHandlers` \[various, depending on language] + +State setup and teardown handlers for any states this test requires. How these are specified depends on the language you're using, see +[writing state handlers](./state-handlers) for more details. + +### `triggers` / `trigger` / `testResponse` / `testErrorResponse` \[various, depending on language] + +Defines the trigger and test functions for multiple examples under test. + +- `triggers`: Used to define multiple trigger and test functions, useful during verification that needs triggers. +- `trigger`: Defines a single trigger. This is only valid on individual examples +- `testResponse` / `testErrorResponse`: Used to verify the response object from the trigger + +See [triggers](./triggers.md) for language specific details. + +## Internal configuration options + +These options modify the behaviour of the contract testing engine. In general, you should not need to change these from the default, unless your use case is unusual. + +Most users probably won't need these, but they're documented here in case you are extending ContractCase or building tools on top of it. + +If you're using these, it might be worth opening an issue to discuss your use-case and whether it should be better supported. ### `throwOnFail` \[boolean]; @@ -174,10 +203,6 @@ this setting. This includes exceptions thrown during [trigger functions](./trigg does not include exceptions thrown by [testResponse functions](./triggers). ::: -## Internal options - -Most users probably won't need these, but they're documented here in case you are extending ContractCase or building tools on top of it. - ### `testRunId` \[string] Default: None (or provided by your test wrapper, eg Jest) diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/AutoVersionFrom.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/AutoVersionFrom.java new file mode 100644 index 00000000..fe7a70a6 --- /dev/null +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/AutoVersionFrom.java @@ -0,0 +1,28 @@ +package io.contract_testing.contractcase; + +/** + * Enum to configure where ContractCase gets the version for the system under test + */ +public enum AutoVersionFrom { + + /** + * Uses absolute-version to determine + * the version, ideally from the git tag + */ + TAG("TAG"), + + /** + * Uses the full git sha as the version for the system under test + */ + GIT_SHA("GIT_SHA"); + + private final String autoVersionFrom; + + AutoVersionFrom(String autoVersionFrom) { + this.autoVersionFrom = autoVersionFrom; + } + + public String toString() { + return this.autoVersionFrom; + } +} diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ConnectorConfigMapper.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ConnectorConfigMapper.java index 917290b9..4cab4c01 100644 --- a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ConnectorConfigMapper.java +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ConnectorConfigMapper.java @@ -115,6 +115,10 @@ private static ContractCaseConnectorConfig.Builder makeBuilder(ContractCaseConfi builder.stateHandlers(ConnectorStateHandlerMapper.map(config.stateHandlers)); } + if(config.autoVersionFrom != null) { + builder.autoVersionFrom(config.autoVersionFrom); + } + config.mockConfig.forEach(builder::mockConfig); return builder; diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ContractCaseConfig.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ContractCaseConfig.java index 0bcdd5f3..36ecc638 100644 --- a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ContractCaseConfig.java +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/ContractCaseConfig.java @@ -116,6 +116,11 @@ public class ContractCaseConfig { */ public final Map stateHandlers; + /** + * Controls how ContractCase will determine the version for the system under test. + */ + public final AutoVersionFrom autoVersionFrom; + /** * Don't construct this directly, use a {@link ContractCaseConfigBuilder} instead, obtained via * {@link ContractCaseConfigBuilder#aContractCaseConfig()} @@ -125,7 +130,8 @@ protected ContractCaseConfig(String providerName, String consumerName, LogLevel String contractDir, String contractFilename, Boolean printResults, Boolean throwOnFail, PublishType publish, String brokerBaseUrl, String brokerCiAccessToken, BrokerBasicAuthCredentials brokerBasicAuth, String baseUrlUnderTest, TriggerGroups triggers, - Map stateHandlers, Map> mockConfig) { + Map stateHandlers, Map> mockConfig, + AutoVersionFrom autoVersionFrom) { this.providerName = providerName; this.consumerName = consumerName; this.logLevel = logLevel; @@ -141,6 +147,7 @@ protected ContractCaseConfig(String providerName, String consumerName, LogLevel this.triggers = triggers; this.stateHandlers = stateHandlers; this.mockConfig = mockConfig; + this.autoVersionFrom = autoVersionFrom; } public String getProviderName() { @@ -199,6 +206,10 @@ public Map getStateHandlers() { return stateHandlers; } + public AutoVersionFrom getAutoVersionFrom() { + return this.autoVersionFrom; + } + /** * Builder for {@link ContractCaseConfig} objects. *

@@ -224,6 +235,8 @@ public static final class ContractCaseConfigBuilder { private TriggerGroups triggers; private Map stateHandlers; + private AutoVersionFrom autoVersionFrom; + private final Map> mockConfig = new HashMap<>(); private ContractCaseConfigBuilder() { @@ -311,6 +324,11 @@ public ContractCaseConfigBuilder mockConfig(String mockShortName, Map extends ContractCaseConfig { public final Trigger trigger; public final TestErrorResponseFunction testErrorResponse; - private IndividualFailedTestConfig(String providerName, String consumerName, LogLevel logLevel, - String contractDir, String contractFilename, Boolean printResults, Boolean throwOnFail, - PublishType publish, String brokerBaseUrl, String brokerCiAccessToken, - BrokerBasicAuthCredentials brokerBasicAuth, String baseUrlUnderTest, TriggerGroups triggers, - Map stateHandlers, Trigger trigger, - TestErrorResponseFunction testErrorResponse, Map> mockConfig) { + private IndividualFailedTestConfig(String providerName, + String consumerName, + LogLevel logLevel, + String contractDir, + String contractFilename, + Boolean printResults, + Boolean throwOnFail, + PublishType publish, + String brokerBaseUrl, + String brokerCiAccessToken, + BrokerBasicAuthCredentials brokerBasicAuth, + String baseUrlUnderTest, + TriggerGroups triggers, + Map stateHandlers, + Trigger trigger, + TestErrorResponseFunction testErrorResponse, + Map> mockConfig, + AutoVersionFrom autoVersionFrom) { super(providerName, consumerName, logLevel, contractDir, contractFilename, printResults, throwOnFail, publish, brokerBaseUrl, brokerCiAccessToken, brokerBasicAuth, baseUrlUnderTest, - triggers, stateHandlers, mockConfig + triggers, stateHandlers, mockConfig, + autoVersionFrom ); this.trigger = trigger; this.testErrorResponse = testErrorResponse; @@ -41,6 +54,7 @@ public static final class IndividualFailedTestConfigBuilder { private Trigger trigger; private TestErrorResponseFunction testErrorResponse; private final Map> mockConfig = new HashMap<>(); + private AutoVersionFrom autoVersionFrom; private IndividualFailedTestConfigBuilder() { } @@ -140,6 +154,10 @@ public IndividualFailedTestConfigBuilder mockConfig(String mockShortName, return this; } + public IndividualFailedTestConfigBuilder autoVersionFrom(AutoVersionFrom autoVersionFrom) { + this.autoVersionFrom = autoVersionFrom; + return this; + } public IndividualFailedTestConfig build() { return new IndividualFailedTestConfig<>( @@ -159,7 +177,8 @@ public IndividualFailedTestConfig build() { stateHandlers, trigger, testErrorResponse, - mockConfig + mockConfig, + autoVersionFrom ); } } diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/IndividualSuccessTestConfig.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/IndividualSuccessTestConfig.java index ca8c8e4e..331f5e04 100644 --- a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/IndividualSuccessTestConfig.java +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/IndividualSuccessTestConfig.java @@ -8,15 +8,28 @@ public class IndividualSuccessTestConfig extends ContractCaseConfig { public final Trigger trigger; public final TestResponseFunction testResponse; - private IndividualSuccessTestConfig(String providerName, String consumerName, LogLevel logLevel, - String contractDir, String contractFilename, Boolean printResults, Boolean throwOnFail, - PublishType publish, String brokerBaseUrl, String brokerCiAccessToken, - BrokerBasicAuthCredentials brokerBasicAuth, String baseUrlUnderTest, TriggerGroups triggers, - Map stateHandlers, Trigger trigger, - TestResponseFunction testResponse, Map> mockConfig) { + private IndividualSuccessTestConfig(String providerName, + String consumerName, + LogLevel logLevel, + String contractDir, + String contractFilename, + Boolean printResults, + Boolean throwOnFail, + PublishType publish, + String brokerBaseUrl, + String brokerCiAccessToken, + BrokerBasicAuthCredentials brokerBasicAuth, + String baseUrlUnderTest, + TriggerGroups triggers, + Map stateHandlers, + Trigger trigger, + TestResponseFunction testResponse, + Map> mockConfig, + AutoVersionFrom autoVersionFrom) { super(providerName, consumerName, logLevel, contractDir, contractFilename, printResults, throwOnFail, publish, brokerBaseUrl, brokerCiAccessToken, brokerBasicAuth, baseUrlUnderTest, - triggers, stateHandlers, mockConfig + triggers, stateHandlers, mockConfig, + autoVersionFrom ); this.trigger = trigger; this.testResponse = testResponse; @@ -41,6 +54,8 @@ public static final class IndividualSuccessTestConfigBuilder { private Trigger trigger; private TestResponseFunction testResponse; + private AutoVersionFrom autoVersionFrom; + private final Map> mockConfig = new HashMap<>(); private IndividualSuccessTestConfigBuilder() { @@ -140,6 +155,11 @@ public IndividualSuccessTestConfigBuilder mockConfig(String mockShortName, return this; } + public IndividualSuccessTestConfigBuilder autoVersionFrom(AutoVersionFrom autoVersionFrom) { + this.autoVersionFrom = autoVersionFrom; + return this; + } + public IndividualSuccessTestConfig build() { return new IndividualSuccessTestConfig<>( providerName, @@ -158,7 +178,8 @@ public IndividualSuccessTestConfig build() { stateHandlers, trigger, testResponse, - mockConfig + mockConfig, + autoVersionFrom ); } } diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/client/ConnectorOutgoingMapper.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/client/ConnectorOutgoingMapper.java index e5eae562..5ef56600 100644 --- a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/client/ConnectorOutgoingMapper.java +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/client/ConnectorOutgoingMapper.java @@ -133,6 +133,11 @@ static ContractCaseConfig mapConfig(final @NotNull ContractCaseConnectorConfig c builder.setPublish(ConnectorOutgoingMapper.map(config.getPublish().toString())); } + if (config.getAutoVersionFrom() != null) { + builder.setAutoVersionFrom(ConnectorOutgoingMapper.map(config.getAutoVersionFrom() + .toString())); + } + if (config.getMockConfig() != null) { config.getMockConfig().forEach((String k, Map v) -> { try { diff --git a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/edge/ContractCaseConnectorConfig.java b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/edge/ContractCaseConnectorConfig.java index fe57b33d..8b28bcae 100644 --- a/packages/dsl-java/src/main/java/io/contract_testing/contractcase/edge/ContractCaseConnectorConfig.java +++ b/packages/dsl-java/src/main/java/io/contract_testing/contractcase/edge/ContractCaseConnectorConfig.java @@ -1,5 +1,6 @@ package io.contract_testing.contractcase.edge; +import io.contract_testing.contractcase.AutoVersionFrom; import io.contract_testing.contractcase.BrokerBasicAuthCredentials; import io.contract_testing.contractcase.ContractCaseConfig; import io.contract_testing.contractcase.LogLevel; @@ -47,7 +48,8 @@ protected ContractCaseConnectorConfig( String testRunId, Map triggerAndTests, ITriggerFunction triggerAndTest, - Map> mockConfig) { + Map> mockConfig, + AutoVersionFrom autoVersionFrom) { super( providerName, consumerName, @@ -63,7 +65,8 @@ protected ContractCaseConnectorConfig( baseUrlUnderTest, null, null, - mockConfig + mockConfig, + autoVersionFrom ); this.testRunId = testRunId; this.triggerAndTests = triggerAndTests; @@ -126,6 +129,7 @@ public static final class Builder { private ITriggerFunction triggerAndTest; private final Map> mockConfig = new HashMap<>(); + private AutoVersionFrom autoVersionFrom; private Builder() { } @@ -219,6 +223,12 @@ public Builder mockConfig(String mockShortName, Map config) { return this; } + public Builder autoVersionFrom(AutoVersionFrom autoVersionFrom) { + this.autoVersionFrom = autoVersionFrom; + return this; + } + + public ContractCaseConnectorConfig build() { return new ContractCaseConnectorConfig( providerName, @@ -237,7 +247,8 @@ public ContractCaseConnectorConfig build() { testRunId, triggerAndTests, triggerAndTest, - mockConfig + mockConfig, + autoVersionFrom ); } diff --git a/packages/dsl-java/src/main/proto/contract_case_stream.proto b/packages/dsl-java/src/main/proto/contract_case_stream.proto index 3d9bfe2b..56f70a5f 100644 --- a/packages/dsl-java/src/main/proto/contract_case_stream.proto +++ b/packages/dsl-java/src/main/proto/contract_case_stream.proto @@ -35,10 +35,13 @@ message ContractCaseConfig { 15; // Long term, this will be moved map mock_config = 16; + + google.protobuf.StringValue auto_version_from = 17; } // Indicates a successful response with no payload -message ResultSuccess {} +message ResultSuccess { +} message ResultSuccessHasMapPayload { // Always a map of Strings -> Strings google.protobuf.Struct map = 1; @@ -74,7 +77,9 @@ message StateHandlerHandle { } // A reference to a trigger function that can be invoked -message TriggerFunctionHandle { google.protobuf.StringValue handle = 1; } +message TriggerFunctionHandle { + google.protobuf.StringValue handle = 1; +} // Requests from client / host @@ -100,11 +105,14 @@ message RunRejectingExampleRequest { // From host to core, instructs the core to strip the matchers from a given // matcher / data object -message StripMatchersRequest { google.protobuf.Struct matcher_or_data = 2; } +message StripMatchersRequest { + google.protobuf.Struct matcher_or_data = 2; +} // From host to core, instructs the core to finish the current contract // definition -message EndDefinitionRequest {} +message EndDefinitionRequest { +} // From host to core, instructs the core to load a plugin message LoadPluginRequest { @@ -116,7 +124,9 @@ message LoadPluginRequest { // Responses from server // From Core to Host, instructs the host to run a given state handler -message RunStateHandlerRequest { StateHandlerHandle state_handler_handle = 1; } +message RunStateHandlerRequest { + StateHandlerHandle state_handler_handle = 1; +} // From Core to Host, requests the host invoke one of the user-provided // triggers. @@ -129,12 +139,14 @@ message TriggerFunctionRequest { SetupInfo setup = 3; } -message CoreFunctionHandle { google.protobuf.StringValue handle = 1; } +message CoreFunctionHandle { + google.protobuf.StringValue handle = 1; +} // Describes the setup of the currently executing Interaction message SetupInfo { // The resolved values for the state variables - map stateVariables = 1; + map state_variables = 1; // The values of any mock setup information map mock = 2; // Callbacks handles for functions @@ -276,7 +288,9 @@ message PrintTestTitleRequest { // // Most messages must be acknowledged with a response - this indicates the // return of the previous message. -message ResultResponse { BoundaryResult result = 1; } +message ResultResponse { + BoundaryResult result = 1; +} // From the Host to the Core, requests that a verification session begins message BeginVerificationRequest { @@ -287,7 +301,8 @@ message BeginVerificationRequest { } // From the Host to the Core, requests a list of the available contracts -message AvailableContractDefinitions {} +message AvailableContractDefinitions { +} // From the Host to the Core, instructs the core that the current verification // session is now configured, and asks it to execute the verification.