UNPKG

@contract-case/case-core

Version:

Core functionality for the ContractCase contract testing suite

136 lines 7.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WritingCaseContract = void 0; const async_mutex_1 = require("async-mutex"); const case_plugin_base_1 = require("@contract-case/case-plugin-base"); const BaseCaseContract_1 = require("./BaseCaseContract"); const structure_1 = require("./structure"); const config_1 = require("./config"); const executeExample_1 = require("./executeExample"); const types_1 = require("../entities/types"); const entities_1 = require("../entities"); class WritingCaseContract extends BaseCaseContract_1.BaseCaseContract { testIndex = 0; mutex; dependencies; /** * Indicates that the contract has been closed by endRecord. * After this, no new interactions can be written to the contract */ contractClosed = false; constructor(description, dependencies, config, parentVersions) { super(description, config, dependencies.defaultConfig, dependencies.resultFormatter, dependencies.makeLogger, parentVersions); this.mutex = new async_mutex_1.Mutex(); this.dependencies = dependencies; } executeTest({ states = [], mockDescription, trigger, testResponse, triggers, testErrorResponse, triggerAndTest, triggerAndTests, stateHandlers = {}, }, runConfig) { const thisIndex = this.testIndex; this.testIndex += 1; if (this.contractClosed) { throw new case_plugin_base_1.CaseConfigurationError('Unable to write more interactions to the contract after endRecord() has been called', this.initialContext, 'UNDOCUMENTED'); } const runContext = (0, case_plugin_base_1.applyNodeToContext)(mockDescription, this.initialContext, { '_case:currentRun:context:throwOnFail': true, '_case:currentRun:context:contractMode': 'write', '_case:currentRun:context:testName': `${thisIndex}`, ...(0, config_1.configToRunContext)(runConfig), }); runContext.logger.deepMaintainerDebug('The full context object:', runContext); // TODO: Tidy up the testinvokers so we don't have to pass around individual things runContext.logger.deepMaintainerDebug('TestInvoker: ', { states, mockDescription, trigger, testResponse, triggers, testErrorResponse, triggerAndTest, triggerAndTests, stateHandlers, }); if (runContext['_case:currentRun:context:contractMode'] !== 'write') { runContext.logger.warn(`The contractMode is expected to be 'write', but it was '${runContext['_case:currentRun:context:contractMode']}'. If you are not expecting this message, this is almost certainly a misconfiguration`); } return this.mutex .runExclusive(() => { states.forEach((state) => { if (state['_case:state:type'] === types_1.SETUP_VARIABLE_STATE) { Object.entries(state.variables).forEach(([key, value]) => runContext.addDefaultVariable(key, state.stateName, value)); } }); // So that stripMatcher ect have access to the context this.runningContext = runContext; const example = { states, mock: mockDescription, result: 'PENDING', }; return (0, executeExample_1.executeExample)(example, { stateHandlers, trigger, triggers, testResponse, triggerAndTest, triggerAndTests, testErrorResponse, names: (0, entities_1.exampleToNames)(example, `${this.testIndex}`), }, this, runContext); }) .then((r) => { if (r != null) { runContext.logger.maintainerDebug('executeTest completed with:', r); } else { runContext.logger.maintainerDebug('executeTest completed with void return, as expected'); } return r; }) .catch((e) => { runContext.logger.maintainerDebug('executeTest threw:', e, e.stack); throw e; }); } recordExample(example, currentContext) { if (!this.currentContract) { currentContext.logger.error('recordSuccess was called without initialising the contract file. This should not be possible.'); throw new case_plugin_base_1.CaseCoreError('Contract was not initialised at the time that recordSuccess was called'); } if (example.result === 'PENDING') { throw new case_plugin_base_1.CaseCoreError('Trying to record a pending example. This should never happen.'); } this.currentContract = (0, structure_1.addExample)(this.currentContract, example, currentContext); return example; } async endRecord() { const writingContext = (0, case_plugin_base_1.addLocation)('WritingContract', this.initialContext); this.contractClosed = true; if ((0, structure_1.hasFailure)(this.currentContract)) { const failures = (0, structure_1.getFailures)(this.currentContract); const successCount = (0, structure_1.getSuccessCount)(this.currentContract); const pendingCount = (0, structure_1.getPendingCount)(this.currentContract); const totalCount = failures.length + successCount + pendingCount; const message = `Unable to write contract: ${failures.length}/${totalCount} interactions failed${pendingCount !== 0 ? ` (${pendingCount} were pending)` : ''}\nSee the other failing tests for details`; throw new entities_1.CaseFailedAssertionError(message, (0, case_plugin_base_1.makeResults)({ type: case_plugin_base_1.ERROR_TYPE_CONFIGURATION, message, code: 'FAIL', location: ['Writing Contract'], toString: () => `There were contract definition failures in ${failures.length}/${totalCount} interactions`, }, ...failures)); } if ((0, structure_1.isEmpty)(this.currentContract)) { throw new case_plugin_base_1.CaseConfigurationError('The contract was empty when endRecord was called, but it must have interactions in it before it can be written.\nEnsure that calls to define the contract are made before endRecord is called.', writingContext); } // - if success, write contract const contractDetails = this.dependencies.writeContract(this.currentContract, writingContext); contractDetails.contractPaths.forEach((fileName) => { writingContext.logger.debug(`Wrote contract file: ${fileName}`); }); await this.dependencies .makeBrokerService(writingContext) .publishContract(this.currentContract, (0, case_plugin_base_1.addLocation)(`PublishingContract(${this.currentContract.description.consumerName} -> ${this.currentContract.description.providerName})`, this.initialContext)); return contractDetails; } } exports.WritingCaseContract = WritingCaseContract; //# sourceMappingURL=WritingCaseContract.js.map