diff --git a/.gitignore b/.gitignore index 7428b3bb..496c168c 100644 --- a/.gitignore +++ b/.gitignore @@ -65,9 +65,6 @@ reports tmp .tmp -# Files created during testing -foo-consumer-bar-provider.json - # jest config !jest.config.js diff --git a/.npmignore b/.npmignore index 5e77b40e..b65f4840 100644 --- a/.npmignore +++ b/.npmignore @@ -13,7 +13,6 @@ script # Tests test *.spec.ts -foo-consumer-bar-provider.json # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/package-lock.json b/package-lock.json index c2bc46be..dee0a1c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3358,15 +3358,6 @@ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dev": true, - "requires": { - "follow-redirects": "^1.14.0" - } - }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -6309,12 +6300,6 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, - "follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", - "dev": true - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", diff --git a/package.json b/package.json index afc7f09a..c2687b65 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,6 @@ "@types/url-join": "^4.0.0", "@typescript-eslint/eslint-plugin": "^4.28.0", "@typescript-eslint/parser": "^4.28.0", - "axios": "^0.21.4", "basic-auth": "2.0.0", "body-parser": "1.18.2", "chai": "4.1.2", diff --git a/src/consumer/checkErrors.ts b/src/consumer/checkErrors.ts deleted file mode 100644 index cf1b22e8..00000000 --- a/src/consumer/checkErrors.ts +++ /dev/null @@ -1,33 +0,0 @@ -import logger from '../logger'; - -export const wrapWithCheck = - boolean>( - f: BooleanFunction, - contextMessage: string - ) => - (...args: Parameters): boolean => { - const result = f(...args); - if (!result) { - logger.pactCrash( - `The pact consumer core returned false at '${contextMessage}'. This\nshould only happen if the core methods were invoked out of order` - ); - } - return result; - }; - -type BooleanFunction = T extends (...args: infer A) => boolean - ? (...args: A) => boolean - : never; - -type BooleanFunctions = { - [key in keyof T]: BooleanFunction; -}; - -export const wrapAllWithCheck = >( - o: T -): BooleanFunctions => - Object.keys(o) - .map((key: string) => ({ - [key]: wrapWithCheck(o[key], key), - })) - .reduce((acc, curr) => ({ ...acc, ...curr }), {}) as T; diff --git a/src/consumer/index.spec.ts b/src/consumer/index.spec.ts deleted file mode 100644 index 0d0fb986..00000000 --- a/src/consumer/index.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import chai = require('chai'); -import chaiAsPromised = require('chai-as-promised'); - -import { makeConsumerPact } from '.'; -import { FfiSpecificationVersion } from '../ffi/types'; -import axios from 'axios'; -import path = require('path'); -import { setLogLevel } from '../logger'; -import { ConsumerPact } from './types'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -const HOST = '127.0.0.1'; - -describe('Integration like test for the consumer API', () => { - setLogLevel('trace'); - let port: number; - let pact: ConsumerPact; - - beforeEach(() => { - const like = (value: unknown) => { - return { - 'pact:matcher:type': 'type', - value, - }; - }; - - pact = makeConsumerPact( - 'foo-consumer', - 'bar-provider', - FfiSpecificationVersion.SPECIFICATION_VERSION_V3 - ); - - const interaction = pact.newInteraction('some description'); - - interaction.uponReceiving('a request to get a dog'); - interaction.given('fido exists'); - interaction.withRequest('GET', '/dogs/1234'); - interaction.withRequestHeader('x-special-header', 0, 'header'); - interaction.withQuery('someParam', 0, 'someValue'); - interaction.withResponseBody( - JSON.stringify({ - name: like('fido'), - age: like(23), - alive: like(true), - }), - 'application/json' - ); - interaction.withResponseHeader('x-special-header', 0, 'header'); - interaction.withStatus(200); - - port = pact.createMockServer(HOST); - }); - - it('Generates a pact with success', () => { - return axios - .request({ - baseURL: `http://${HOST}:${port}`, - headers: { Accept: 'application/json', 'x-special-header': 'header' }, - params: { - someParam: 'someValue', - }, - method: 'GET', - url: '/dogs/1234', - }) - .then((res) => { - expect(res.data).to.deep.equal({ - name: 'fido', - age: 23, - alive: true, - }); - }) - .then(() => { - expect(pact.mockServerMatchedSuccessfully(port)).to.be.true; - }) - .then(() => { - // You don't have to call this, it's just here to check it works - const mismatches = pact.mockServerMismatches(port); - expect(mismatches).to.have.length(0); - }) - .then(() => { - pact.writePactFile(port, path.join(__dirname, '__testoutput__')); - }) - .then(() => { - pact.cleanupMockServer(port); - }); - }); - it('Generates a pact with failure', () => { - return axios - .request({ - baseURL: `http://${HOST}:${port}`, - headers: { - Accept: 'application/json', - 'x-special-header': 'WrongHeader', - }, - params: { - someParam: 'wrongValue', - }, - method: 'GET', - url: '/dogs/1234', - }) - .then( - () => { - throw new Error( - 'This call is not supposed to succeed during testing' - ); - }, - (err) => { - expect(err.message).to.equal('Request failed with status code 500'); - } - ) - .then(() => { - const mismatches = pact.mockServerMismatches(port); - expect(mismatches[0]).to.deep.equal({ - method: 'GET', - mismatches: [ - { - actual: 'wrongValue', - expected: 'someValue', - mismatch: "Expected 'someValue' to be equal to 'wrongValue'", - parameter: 'someParam', - type: 'QueryMismatch', - }, - { - actual: 'WrongHeader', - expected: 'header', - key: 'x-special-header', - mismatch: - "Mismatch with header 'x-special-header': Expected 'header' to be equal to 'WrongHeader'", - type: 'HeaderMismatch', - }, - ], - path: '/dogs/1234', - type: 'request-mismatch', - }); - }) - .then(() => { - // Yes, this writes the pact file. - // Yes, even though the tests have failed - pact.writePactFile(port, path.join(__dirname, '__testoutput__')); - }) - .then(() => { - pact.cleanupMockServer(port); - }); - }); -}); diff --git a/src/consumer/index.ts b/src/consumer/index.ts deleted file mode 100644 index 8097a073..00000000 --- a/src/consumer/index.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { getFfiLib } from '../ffi'; -import { - CREATE_MOCK_SERVER_ERRORS, - FfiSpecificationVersion, - FfiWritePactResponse, - INTERACTION_PART_REQUEST, - INTERACTION_PART_RESPONSE, -} from '../ffi/types'; -import { getLogLevel, logCrashAndThrow, logErrorAndThrow } from '../logger'; -import { wrapAllWithCheck, wrapWithCheck } from './checkErrors'; - -import { - ConsumerInteraction, - ConsumerPact, - MatchingResult, - Mismatch, -} from './types'; - -type AnyJson = boolean | number | string | null | JsonArray | JsonMap; -interface JsonMap { - [key: string]: AnyJson; -} -type JsonArray = Array; - -export const makeConsumerPact = ( - consumer: string, - provider: string, - version: FfiSpecificationVersion, - logLevel = getLogLevel() -): ConsumerPact => { - const lib = getFfiLib(logLevel); - - const pactPtr = lib.pactffi_new_pact(consumer, provider); - if (!lib.pactffi_with_specification(pactPtr, version)) { - throw new Error( - `Unable to set core spec version. The pact FfiSpecificationVersion '${version}' may be invalid (note this is not the same as the pact spec version)` - ); - } - - return { - createMockServer: ( - address: string, - requestedPort?: number, - tls = false - ) => { - const port = lib.pactffi_create_mock_server_for_pact( - pactPtr, - `${address}:${requestedPort ? requestedPort : 0}`, - tls - ); - const error: keyof typeof CREATE_MOCK_SERVER_ERRORS | undefined = - Object.keys(CREATE_MOCK_SERVER_ERRORS).find( - (key) => CREATE_MOCK_SERVER_ERRORS[key] === port - ) as keyof typeof CREATE_MOCK_SERVER_ERRORS; - if (error) { - if (error === 'ADDRESS_NOT_VALID') { - logErrorAndThrow( - `Unable to start mock server at '${address}'. Is the address and port valid?` - ); - } - if (error === 'TLS_CONFIG') { - logErrorAndThrow( - `Unable to create TLS configuration with self-signed certificate` - ); - } - logCrashAndThrow( - `The pact core couldn\'t create the mock server because of an error described by '${error}'` - ); - } - if (port <= 0) { - logCrashAndThrow( - `The pact core returned an unhandled error code '${port}'` - ); - } - return port; - }, - - mockServerMatchedSuccessfully: (port: number) => { - return lib.pactffi_mock_server_matched(port); - }, - mockServerMismatches: (port: number): MatchingResult[] => { - const results: MatchingResult[] = JSON.parse( - lib.pactffi_mock_server_mismatches(port) - ); - return results.map((result: MatchingResult) => ({ - ...result, - ...('mismatches' in result - ? { - mismatches: result.mismatches.map((m: string | Mismatch) => - typeof m === 'string' ? JSON.parse(m) : m - ), - } - : {}), - })); - }, - cleanupMockServer: (port: number): boolean => { - return wrapWithCheck<(port: number) => boolean>( - (port: number): boolean => lib.pactffi_cleanup_mock_server(port), - 'cleanupMockServer' - )(port); - }, - writePactFile: (port: number, dir: string, merge = true) => { - const result = lib.pactffi_write_pact_file(port, dir, !merge); - switch (result) { - case FfiWritePactResponse.SUCCESS: - return; - case FfiWritePactResponse.UNABLE_TO_WRITE_PACT_FILE: - logErrorAndThrow('The pact core was unable to write the pact file'); - case FfiWritePactResponse.GENERAL_PANIC: - logCrashAndThrow( - 'The pact core panicked while writing the pact file' - ); - case FfiWritePactResponse.MOCK_SERVER_NOT_FOUND: - logCrashAndThrow( - 'The pact core was asked to write a pact file from a mock server that appears not to exist' - ); - default: - logCrashAndThrow( - `The pact core returned an unknown error code (${result}) instead of writing the pact` - ); - } - }, - newInteraction: (description: string): ConsumerInteraction => { - const interactionPtr = lib.pactffi_new_interaction(pactPtr, description); - return wrapAllWithCheck({ - uponReceiving: (description: string) => { - return lib.pactffi_upon_receiving(interactionPtr, description); - }, - given: (state: string) => { - return lib.pactffi_given(interactionPtr, state); - }, - withRequest: (method: string, path: string) => { - return lib.pactffi_with_request(interactionPtr, method, path); - }, - withQuery: (name: string, index: number, value: string) => { - return lib.pactffi_with_query_parameter( - interactionPtr, - name, - index, - value - ); - }, - withRequestHeader: (name: string, index: number, value: string) => { - return lib.pactffi_with_header( - interactionPtr, - INTERACTION_PART_REQUEST, - name, - index, - value - ); - }, - withRequestBody: (body: string, contentType: string) => { - return lib.pactffi_with_body( - interactionPtr, - INTERACTION_PART_REQUEST, - contentType, - body - ); - }, - withResponseHeader: (name: string, index: number, value: string) => { - return lib.pactffi_with_header( - interactionPtr, - INTERACTION_PART_RESPONSE, - name, - index, - value - ); - }, - withResponseBody: (body: string, contentType: string) => { - return lib.pactffi_with_body( - interactionPtr, - INTERACTION_PART_RESPONSE, - contentType, - body - ); - }, - withStatus: (status: number) => { - return lib.pactffi_response_status(interactionPtr, status); - }, - }); - }, - }; -}; diff --git a/src/consumer/types.ts b/src/consumer/types.ts deleted file mode 100644 index 4e2d752c..00000000 --- a/src/consumer/types.ts +++ /dev/null @@ -1,140 +0,0 @@ -export type MatchingResult = - | MatchingResultSuccess - | MatchingResultRequestMismatch - | MatchingResultRequestNotFound - | MatchingResultMissingRequest; - -// As far as I can tell, MatchingResultSuccess is actually -// never produced by the FFI lib -export type MatchingResultSuccess = { - type: 'request-match'; -}; - -export type MatchingResultRequestMismatch = { - type: 'request-mismatch'; - method: string; - path: string; - mismatches: Mismatch[]; -}; - -export type MatchingResultRequestNotFound = { - type: 'request-not-found'; - method: string; - path: string; - request: unknown; -}; - -export type MatchingResultMissingRequest = { - type: 'missing-request'; - method: string; - path: string; - request: unknown; -}; - -export type Mismatch = - | MethodMismatch - | PathMismatch - | StatusMismatch - | QueryMismatch - | HeaderMismatch - | BodyTypeMismatch - | BodyMismatch - | MetadataMismatch; - -export type MethodMismatch = { - type: 'MethodMismatch'; - expected: string; - actual: string; -}; - -export type PathMismatch = { - type: 'PathMismatch'; - expected: string; - actual: string; - mismatch: string; -}; - -export type StatusMismatch = { - type: 'StatusMismatch'; - expected: string; - actual: string; - mismatch: string; -}; - -export type QueryMismatch = { - type: 'QueryMismatch'; - parameter: string; - expected: string; - actual: string; - mismatch: string; -}; - -export type HeaderMismatch = { - type: 'HeaderMismatch'; - key: string; - expected: string; - actual: string; - mismatch: string; -}; - -export type BodyTypeMismatch = { - type: 'BodyTypeMismatch'; - expected: string; - actual: string; - mismatch: string; - expectedBody?: string; - actualBody?: string; -}; - -export type BodyMismatch = { - type: 'BodyMismatch'; - path: string; - expected?: string; - actual?: string; - mismatch: string; -}; - -export type MetadataMismatch = { - type: 'MetadataMismatch'; - key: string; - expected: string; - actual: string; - mismatch: string; -}; - -export type ConsumerInteraction = { - uponReceiving: (description: string) => boolean; - given: (state: string) => boolean; - withRequest: (method: string, path: string) => boolean; - withQuery: (name: string, index: number, value: string) => boolean; - withStatus: (status: number) => boolean; - withRequestHeader: (name: string, index: number, value: string) => boolean; - withRequestBody: (body: string, contentType: string) => boolean; - withResponseHeader: (name: string, index: number, value: string) => boolean; - withResponseBody: (body: string, contentType: string) => boolean; -}; - -export type ConsumerPact = { - newInteraction: (description: string) => ConsumerInteraction; - createMockServer: (address: string, port?: number, tls?: boolean) => number; - mockServerMismatches: (port: number) => MatchingResult[]; - cleanupMockServer: (port: number) => boolean; - /** - * This function writes the pact file, regardless of whether or not the test was successful. - * Do not call it without checking that the tests were successful, unless you want to write the wrong pact contents. - * - * @param port the port number the mock server is running on. - * @param dir the directory to write the pact file to - * @param merge whether or not to merge the pact file contents (default true) - */ - writePactFile: (port: number, dir: string, merge?: boolean) => void; - /** - * Check if a mock server has matched all its requests. - * - * @param port the port number the mock server is running on. - * @returns {boolean} true if all requests have been matched. False if there - * is no mock server on the given port, or if any request has not been successfully matched, or - * the method panics. - */ - mockServerMatchedSuccessfully: (port: number) => boolean; -}; diff --git a/src/ffi/declarations.ts b/src/ffi/declarations.ts index 506cc899..05b7f3a8 100644 --- a/src/ffi/declarations.ts +++ b/src/ffi/declarations.ts @@ -1,7 +1,5 @@ import ref = require('ref-napi'); import refStructDi = require('ref-struct-di'); -import { FfiEnum } from './internals/types'; -import { FfiInteractionPart, FfiSpecificationVersion } from './types'; const struct = refStructDi(ref); @@ -19,11 +17,6 @@ const InteractionHandle = struct({ interaction: InteractionPtr, }); -const StringResultStruct = struct({ - tag: ref.types.int, - ok: ref.types.CString, -}); - // We have to declare this twice because typescript can't figure it out // There's a workaround here we could employ: // https://gist.github.com/jcalz/381562d282ebaa9b41217d1b31e2c211 @@ -31,230 +24,104 @@ export type FfiDeclarations = { pactffi_init: ['string', ['string']]; pactffi_version: ['string', []]; pactffi_free_string: ['void', ['string']]; - pactffi_verify: ['int32', ['string']]; - /** - * External interface to create a mock server. A pointer to the pact JSON as a C string is passed in, - * as well as the port for the mock server to run on. A value of 0 for the port will result in a - * port being allocated by the operating system. The port of the mock server is returned. - * - * * `pact_str` - Pact JSON - * * `addr_str` - Address to bind to in the form name:port (i.e. 127.0.0.1:0) - * * `tls` - boolean flag to indicate of the mock server should use TLS (using a self-signed certificate) - * - * # Errors - * - * Errors are returned as negative values. - * - * | Error | Description | - * |-------|-------------| - * | -1 | A null pointer was received | - * | -2 | The pact JSON could not be parsed | - * | -3 | The mock server could not be started | - * | -4 | The method panicked | - * | -5 | The address is not valid | - * | -6 | Could not create the TLS configuration with the self-signed certificate | - * - * int32_t pactffi_create_mock_server(const char *pact_str, - * const char *addr_str, - * bool tls); - */ + pactffi_verify: ['int', ['string']]; pactffi_create_mock_server_for_pact: [ - 'int32', + 'int', [typeof PactHandle, 'string', 'bool'] ]; pactffi_new_pact: [typeof PactHandle, ['string', 'string']]; - /** - * Sets the specification version for a given Pact model. Returns false if the interaction or Pact can't be - * modified (i.e. the mock server for it has already started) or the version is invalid - * - * * `pact` - Handle to a Pact model - * * `version` - the spec version to use - * - * bool pactffi_with_specification(struct PactHandle pact, - * enum PactSpecification version); - */ - pactffi_with_specification: [ - 'bool', - [typeof PactHandle, FfiEnum] - ]; - /** - * External interface to check if a mock server has matched all its requests. The port number is - * passed in, and if all requests have been matched, true is returned. False is returned if there - * is no mock server on the given port, or if any request has not been successfully matched, or - * the method panics. - * - * bool pactffi_mock_server_matched(int32_t mock_server_port); - */ - pactffi_mock_server_matched: ['bool', ['int32']]; - /** - * Creates a new Interaction and returns a handle to it. - * - * * `description` - The interaction description. It needs to be unique for each interaction. - * - * Returns a new `InteractionHandle`. - * - * struct InteractionHandle pactffi_new_interaction(struct PactHandle pact, const char *description); - */ + pactffi_with_specification: ['void', [typeof PactHandle, 'int']]; pactffi_new_interaction: [ typeof InteractionHandle, [typeof PactHandle, 'string'] ]; - /** - * Sets the description for the Interaction. Returns false if the interaction or Pact can't be - * modified (i.e. the mock server for it has already started) - * - * `description` - The interaction description. It needs to be unique for each interaction. - * - * bool pactffi_upon_receiving(struct InteractionHandle interaction, const char *description); - */ - pactffi_upon_receiving: ['bool', [typeof InteractionHandle, 'string']]; - /** bool pactffi_given(struct InteractionHandle interaction, const char *description); */ - pactffi_given: ['bool', [typeof InteractionHandle, 'string']]; + pactffi_upon_receiving: ['void', [typeof InteractionHandle, 'string']]; + pactffi_given: ['void', [typeof InteractionHandle, 'string']]; pactffi_given_with_param: [ - 'bool', + 'void', [typeof InteractionHandle, 'string', 'string', 'string'] ]; pactffi_with_request: [ - 'bool', + 'void', [typeof InteractionHandle, 'string', 'string'] ]; pactffi_with_query_parameter: [ - 'bool', - [typeof InteractionHandle, 'string', 'int32', 'string'] + 'void', + [typeof InteractionHandle, 'string', 'int', 'string'] ]; pactffi_with_header: [ - 'bool', - [ - typeof InteractionHandle, - FfiEnum, - 'string', - 'int32', - 'string' - ] + 'void', + [typeof InteractionHandle, 'int', 'string', 'int', 'string'] ]; pactffi_with_body: [ - 'bool', - [typeof InteractionHandle, FfiEnum, 'string', 'string'] + 'void', + [typeof InteractionHandle, 'int', 'string', 'string'] ]; pactffi_with_binary_file: [ - 'bool', - [ - typeof InteractionHandle, - FfiEnum, - 'string', - 'string', - 'int32' - ] + 'void', + [typeof InteractionHandle, 'int', 'string', 'string', 'int'] ]; pactffi_with_multipart_file: [ - typeof StringResultStruct, - [typeof InteractionHandle, 'int32', 'string', 'string', 'string'] + 'void', + [typeof InteractionHandle, 'int', 'string', 'string', 'string'] ]; - pactffi_response_status: ['bool', [typeof InteractionHandle, 'int32']]; - /** - * External interface to trigger a mock server to write out its pact file. This function should - * be called if all the consumer tests have passed. The directory to write the file to is passed - * as the second parameter. If a NULL pointer is passed, the current working directory is used. - * - * If overwrite is true, the file will be overwritten with the contents of the current pact. - * Otherwise, it will be merged with any existing pact file. - * - * Returns 0 if the pact file was successfully written. Returns a positive code if the file can - * not be written, or there is no mock server running on that port or the function panics. - * - * # Errors - * - * Errors are returned as positive values. - * - * | Error | Description | - * |-------|-------------| - * | 1 | A general panic was caught | - * | 2 | The pact file was not able to be written | - * | 3 | A mock server with the provided port was not found | - * - * int32_t pactffi_write_pact_file(int32_t mock_server_port, const char *directory, bool overwrite); - */ - pactffi_write_pact_file: ['int32', ['int32', 'string', 'bool']]; - /** - * External interface to cleanup a mock server. This function will try terminate the mock server - * with the given port number and cleanup any memory allocated for it. Returns true, unless a - * mock server with the given port number does not exist, or the function panics. - */ - pactffi_cleanup_mock_server: ['bool', ['int32']]; - /** - * External interface to get all the mismatches from a mock server. The port number of the mock - * server is passed in, and a pointer to a C string with the mismatches in JSON format is - * returned. - * - * **NOTE:** The JSON string for the result is allocated on the heap, and will have to be freed - * once the code using the mock server is complete. The [`cleanup_mock_server`](fn.cleanup_mock_server.html) function is - * provided for this purpose. - * - * # Errors - * - * If there is no mock server with the provided port number, or the function panics, a NULL - * pointer will be returned. Don't try to dereference it, it will not end well for you. - * - * char *pactffi_mock_server_mismatches(int32_t mock_server_port); - */ - pactffi_mock_server_mismatches: ['string', ['int32']]; + pactffi_response_status: ['void', [typeof InteractionHandle, 'int']]; + pactffi_write_pact_file: ['int', ['int', 'string']]; + pactffi_cleanup_mock_server: ['bool', ['int']]; + pactffi_mock_server_mismatches: ['string', ['int']]; pactffi_get_tls_ca_certificate: ['string', []]; pactffi_log_message: ['void', ['string', 'string', 'string']]; - pactffi_log_to_buffer: ['int32', ['int32']]; - pactffi_init_with_log_level: ['void', ['int32']]; - pactffi_log_to_stdout: ['int32', ['int32']]; - pactffi_log_to_file: ['int32', ['string', 'int32', 'int32']]; - pactffi_fetch_log_buffer: ['string', ['int32']]; + pactffi_log_to_buffer: ['int', ['int']]; + pactffi_init_with_log_level: ['void', ['int']]; + pactffi_log_to_stdout: ['int', ['int']]; + pactffi_log_to_file: ['int', ['string', 'int', 'int']]; + pactffi_fetch_log_buffer: ['string', ['int']]; }; export const declarations: FfiDeclarations = { pactffi_init: ['string', ['string']], pactffi_version: ['string', []], pactffi_free_string: ['void', ['string']], - pactffi_verify: ['int32', ['string']], - pactffi_create_mock_server_for_pact: [ - 'int32', - [PactHandle, 'string', 'bool'], - ], + pactffi_verify: ['int', ['string']], + pactffi_create_mock_server_for_pact: ['int', [PactHandle, 'string', 'bool']], pactffi_new_pact: [PactHandle, ['string', 'string']], - pactffi_with_specification: ['bool', [PactHandle, 'int32']], - pactffi_mock_server_matched: ['bool', ['int32']], + pactffi_with_specification: ['void', [PactHandle, 'int']], pactffi_new_interaction: [InteractionHandle, [PactHandle, 'string']], - pactffi_upon_receiving: ['bool', [InteractionHandle, 'string']], - pactffi_given: ['bool', [InteractionHandle, 'string']], + pactffi_upon_receiving: ['void', [InteractionHandle, 'string']], + pactffi_given: ['void', [InteractionHandle, 'string']], pactffi_given_with_param: [ - 'bool', + 'void', [InteractionHandle, 'string', 'string', 'string'], ], - pactffi_with_request: ['bool', [InteractionHandle, 'string', 'string']], + pactffi_with_request: ['void', [InteractionHandle, 'string', 'string']], pactffi_with_query_parameter: [ - 'bool', - [InteractionHandle, 'string', 'int32', 'string'], + 'void', + [InteractionHandle, 'string', 'int', 'string'], ], pactffi_with_header: [ - 'bool', - [InteractionHandle, 'int32', 'string', 'int32', 'string'], + 'void', + [InteractionHandle, 'int', 'string', 'int', 'string'], ], - pactffi_with_body: ['bool', [InteractionHandle, 'int32', 'string', 'string']], + pactffi_with_body: ['void', [InteractionHandle, 'int', 'string', 'string']], pactffi_with_binary_file: [ - 'bool', - [InteractionHandle, 'int32', 'string', 'string', 'int32'], + 'void', + [InteractionHandle, 'int', 'string', 'string', 'int'], ], pactffi_with_multipart_file: [ - StringResultStruct, - [InteractionHandle, 'int32', 'string', 'string', 'string'], + 'void', + [InteractionHandle, 'int', 'string', 'string', 'string'], ], - pactffi_response_status: ['bool', [InteractionHandle, 'int32']], - pactffi_write_pact_file: ['int32', ['int32', 'string', 'bool']], - pactffi_cleanup_mock_server: ['bool', ['int32']], - pactffi_mock_server_mismatches: ['string', ['int32']], + pactffi_response_status: ['void', [InteractionHandle, 'int']], + pactffi_write_pact_file: ['int', ['int', 'string']], + pactffi_cleanup_mock_server: ['bool', ['int']], + pactffi_mock_server_mismatches: ['string', ['int']], pactffi_get_tls_ca_certificate: ['string', []], pactffi_log_message: ['void', ['string', 'string', 'string']], - pactffi_log_to_buffer: ['int32', ['int32']], - pactffi_init_with_log_level: ['void', ['int32']], - pactffi_fetch_log_buffer: ['string', ['int32']], - pactffi_log_to_stdout: ['int32', ['int32']], - pactffi_log_to_file: ['int32', ['string', 'int32', 'int32']], + pactffi_log_to_buffer: ['int', ['int']], + pactffi_init_with_log_level: ['void', ['int']], + pactffi_fetch_log_buffer: ['string', ['int']], + pactffi_log_to_stdout: ['int', ['int']], + pactffi_log_to_file: ['int', ['string', 'int', 'int']], }; export enum FfiFunctionResult { @@ -262,6 +129,20 @@ export enum FfiFunctionResult { RESULT_FAILED, } +export enum FfiSpecificationVersion { + SPECIFICATION_VERSION_UNKNOWN = 0, + SPECIFICATION_VERSION_V1, + SPECIFICATION_VERSION_V1_1, + SPECIFICATION_VERSION_V2, + SPECIFICATION_VERSION_V3, + SPECIFICATION_VERSION_V4, +} + +export enum FfiInteractionPart { + INTERACTION_PART_REQUEST = 0, + INTERACTION_PART_RESPONSE, +} + export enum FfiLogLevelFilter { LOG_LEVEL_OFF = 0, LOG_LEVEL_ERROR, diff --git a/src/ffi/index.spec.ts b/src/ffi/index.spec.ts deleted file mode 100644 index 2b414599..00000000 --- a/src/ffi/index.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import chai = require('chai'); -import chaiAsPromised = require('chai-as-promised'); - -import { getFfiLib } from '.'; -import { declarations } from './declarations'; - -chai.use(chaiAsPromised); -const expect = chai.expect; - -describe('ffi Lib', () => { - let lib: ReturnType; - beforeEach(() => { - lib = getFfiLib(); - }); - - describe('functions', () => { - Object.keys(declarations).forEach((key) => { - it(`'${key}' was loaded`, () => { - expect(lib[key]).not.to.be.undefined; - }); - }); - }); -}); diff --git a/src/ffi/internals/types.ts b/src/ffi/internals/types.ts index a3524d65..718b28ec 100644 --- a/src/ffi/internals/types.ts +++ b/src/ffi/internals/types.ts @@ -14,43 +14,16 @@ type FfiFunction = T extends (...a: infer Args) => infer ReturnType // We allow any here, because typescript won't accept the `StructType`from // ref-struct-di for some reason. Using any lets us pass through arbitrary // parameter types, so we just have to be careful to get them right. -type VariableType = - | 'string' - | 'void' - | 'int' - | 'int32' - | 'double' - | 'float' - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | any; - -// This allows us to have enum types report as 'int', but -// be typed correctly on the way back -type UnpackEnum = [T] extends [unknown] - ? T extends number - ? T - : never - : never; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type VariableType = 'string' | 'void' | 'int' | 'double' | 'float' | any; type ActualType = [T] extends ['string'] ? string : [T] extends ['void'] ? void - : [number] extends [T] - ? UnpackEnum - : ['int32'] extends [T] - ? [T] extends ['int32'] - ? number - : UnpackEnum - : ['int'] extends [T] - ? [T] extends ['int'] - ? number - : UnpackEnum - : [T] extends ['double' | 'float'] + : [T] extends ['int' | 'double' | 'float'] ? number - : [T] extends ['bool'] - ? boolean - : T; // For now, structs are just passed through but we should do them properly ideally + : T; type ArrayActualType]> = { [Index in keyof Tuple]: ActualType; @@ -69,16 +42,8 @@ type LibDescription = { [k in Functions]: [VariableType, Array]; }; -/** - * `FfiBinding` generates the correct library return - * types from a structure that is - * typed for `ffi.Library` from ffi-napi - */ export type FfiBinding = T extends LibDescription ? { [Key in keyof T]: FfiFunction>; } : never; - -/** Use this type within an `FfiBinding` to tell the FFI that it's an enum type that it should respect */ -export type FfiEnum = T | 'int32'; diff --git a/src/ffi/types.ts b/src/ffi/types.ts deleted file mode 100644 index 21bf71ce..00000000 --- a/src/ffi/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -export type FfiSpecificationVersion = 0 | 1 | 2 | 3 | 4 | 5; - -export const FfiSpecificationVersion: Record = - { - SPECIFICATION_VERSION_UNKNOWN: 0, - SPECIFICATION_VERSION_V1: 1, - SPECIFICATION_VERSION_V1_1: 2, - SPECIFICATION_VERSION_V2: 3, - SPECIFICATION_VERSION_V3: 4, - SPECIFICATION_VERSION_V4: 5, - }; - -export type FfiWritePactResponse = 0 | 1 | 2 | 3; - -export const FfiWritePactResponse: Record = { - SUCCESS: 0, - GENERAL_PANIC: 1, - UNABLE_TO_WRITE_PACT_FILE: 2, - MOCK_SERVER_NOT_FOUND: 3, -}; - -export type FfiInteractionPart = 0 | 1; - -export const INTERACTION_PART_REQUEST: FfiInteractionPart = 0; -export const INTERACTION_PART_RESPONSE: FfiInteractionPart = 1; - -export const CREATE_MOCK_SERVER_ERRORS = { - NULL_POINTER: -1, - JSON_PARSE_ERROR: -2, - MOCK_SERVER_START_FAIL: -3, - CORE_PANIC: -4, - ADDRESS_NOT_VALID: -5, - TLS_CONFIG: -6, -}; -/* --1 A null pointer was received --2 The pact JSON could not be parsed --3 The mock server could not be started --4 The method panicked --5 The address is not valid --6 Could not create the TLS configuration with the self-signed certificate -*/ diff --git a/src/logger/index.ts b/src/logger/index.ts index 66f7727c..938a60e7 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -16,15 +16,13 @@ export const setLogLevel = (level: LogLevel = 'info'): void => { logger = createLogger(currentLogLevel); }; -export const getLogLevel = (): LogLevel => currentLogLevel; - export const verboseIsImplied = (): boolean => currentLogLevel === 'trace' || currentLogLevel === 'debug'; const addContext = (context: string, message: string) => `${context}: ${message}`; -const logFunctions = { +export default { pactCrash: (message: string, context: string = logContext): void => logger.error(addContext(context, pactCrashMessage(message))), error: (message: string, context: string = logContext): void => @@ -38,15 +36,3 @@ const logFunctions = { trace: (message: string, context: string = logContext): void => logger.trace(addContext(context, message)), }; - -export const logErrorAndThrow = (message: string, context?: string): never => { - logger.error(message, context); - throw new Error(message); -}; - -export const logCrashAndThrow = (message: string, context?: string): never => { - logger.pactCrash(message, context); - throw new Error(message); -}; - -export default logFunctions;