Skip to content

Commit 0648101

Browse files
committed
feat(core): Now contract verification is guaranteed to run contracts in order, instead of interleaving interactions
1 parent 2de452e commit 0648101

3 files changed

Lines changed: 42 additions & 30 deletions

File tree

packages/case-core/src/connectors/contract/ContractVerifierConnector.ts

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -181,32 +181,44 @@ export class ContractVerifierConnector {
181181

182182
if (contractsToVerify.length > 1) {
183183
this.context.logger.debug(
184-
'*** Multiple contracts are being verified ***',
185-
);
186-
this.context.logger.debug(
187-
'Note that the following debug log may contain interactions from any contract in any order',
184+
`*** There are ${contractsToVerify.length} contracts are being verified ***`,
188185
);
186+
this.context.logger.debug(`Take note of the contract number in the log`);
189187
}
190-
const results = contractsToVerify.map((contractLink, index) => {
191-
this.context.logger.debug(
192-
`Verifying contract from file '${contractLink.filePath}'`,
193-
);
194-
const contractVerifier = new ReadingCaseContract(
195-
contractLink.contents,
196-
this.dependencies,
197-
{
198-
...mergedConfig,
199-
coreLogContextPrefix:
200-
contractsToVerify.length > 1 ? `Contract[${index}]` : '',
201-
},
202-
this.parentVersions,
203-
this.mutex,
204-
);
205-
Object.entries(invokeableFns).forEach(([key, value]) => {
206-
contractVerifier.registerFunction(key, value);
207-
});
208-
return contractVerifier.verifyContract(invoker, this.callback);
209-
});
188+
const results = contractsToVerify.map((contractLink, index) =>
189+
this.mutex.runExclusive(() => {
190+
if (!contractLink?.contents?.description?.consumerName) {
191+
this.context.logger.error(
192+
`Contract in file '${contractLink.filePath}' appears to have no consumer name! It might not be a case contract`,
193+
);
194+
}
195+
196+
if (!contractLink?.contents?.description?.providerName) {
197+
this.context.logger.error(
198+
`Contract in file '${contractLink.filePath}' appears to have no provider name! It might not be a case contract`,
199+
);
200+
}
201+
202+
this.context.logger.debug(
203+
`*** Verifying contract: '${contractLink.contents.description.consumerName}' -> '${contractLink.contents.description.consumerName}'`,
204+
);
205+
this.context.logger.debug(`Contract File: ${contractLink.filePath}`);
206+
const contractVerifier = new ReadingCaseContract(
207+
contractLink.contents,
208+
this.dependencies,
209+
{
210+
...mergedConfig,
211+
coreLogContextPrefix:
212+
contractsToVerify.length > 1 ? `Contract[${index}]` : '',
213+
},
214+
this.parentVersions,
215+
);
216+
Object.entries(invokeableFns).forEach(([key, value]) => {
217+
contractVerifier.registerFunction(key, value);
218+
});
219+
return contractVerifier.verifyContract(invoker, this.callback);
220+
}),
221+
);
210222
if (mergedConfig.internals.asyncVerification) {
211223
this.context.logger.maintainerDebug(`Awaiting async verification`);
212224
return Promise.all(results).then(

packages/case-core/src/core/ReadingCaseContract.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export class ReadingCaseContract extends BaseCaseContract {
5151
}: ReaderDependencies,
5252
config: CaseConfig,
5353
parentVersions: string[],
54-
mutex: Mutex,
5554
) {
5655
super(
5756
contractFile.description,
@@ -65,7 +64,7 @@ export class ReadingCaseContract extends BaseCaseContract {
6564
this.makeBrokerService = makeBrokerService;
6665
this.links = contractFile;
6766
this.status = 'UNKNOWN';
68-
this.mutex = mutex;
67+
this.mutex = new Mutex();
6968
}
7069

7170
/**
@@ -83,15 +82,18 @@ export class ReadingCaseContract extends BaseCaseContract {
8382
runTestCb: RunTestCallback,
8483
): Promise<void> | undefined {
8584
this.initialContext.logger.maintainerDebug(
86-
`Verifying contract between '${this.currentContract.description.consumerName}' and '${this.currentContract.description.providerName}'. There are '${this.currentContract.examples.length}' examples`,
85+
`Verifying contract: '${this.currentContract.description.consumerName}' -> '${this.currentContract.description.providerName}'`,
86+
);
87+
this.initialContext.logger.maintainerDebug(
88+
`This contract has ${this.currentContract.examples.length} interactions`,
8789
);
8890
const interactionFinishedIndicators: Promise<unknown>[] = [];
8991
const interactionFinishers: Array<() => void> = [];
9092

9193
this.currentContract.examples.forEach((example, index) => {
9294
if (example.result !== 'VERIFIED') {
9395
throw new CaseCoreError(
94-
`Attempting to verify an interaction which was '${example.result}'. This should never happen in normal operation, and might be the result of a corrupted ContractCase file, a file that was not written by ContractCase, or a bug.`,
96+
`Attempting to verify an interaction which didn't pass the consumer test ('${example.result}'). This should never happen in normal operation, and might be the result of a corrupted ContractCase file, a file that was not written by ContractCase, or a bug.`,
9597
);
9698
}
9799

packages/case-core/src/index.http.request.verification.misconfigured.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type * as http from 'node:http';
22

33
import { CaseConfigurationError } from '@contract-case/case-plugin-base';
4-
import { Mutex } from 'async-mutex';
54
import type { RunTestCallback } from './core/executeExample/types';
65
import type { StateHandlers } from './entities/states/types';
76

@@ -46,7 +45,6 @@ describe('Server verification', () => {
4645
publish: false,
4746
},
4847
['tests'],
49-
new Mutex(),
5048
);
5149
beforeAll(async () => {
5250
server = await start(port, serverDependencies);

0 commit comments

Comments
 (0)