Natively Pluggable Module

    @cepharum/mockup-service

    0.1.5 • Public • Published

    mockup-service

    License

    MIT

    About

    This module is designed to help with mocking network services some code to be tested relies on. It supports an extensible domain-specific language describing an ordered list of rules with each rule containing a test applied on incoming requests and a response to be sent back in case of test succeeds.

    Install

    npm install -D @cepharum/mockup-service
    

    Usage

    Consider the following definition of rules for an HTTP service:

    headers.content-type = text/plain ==>
    	Hello tester!
    EOT
    
    headers.content-type = text/html AND method = POST ==>
    	Hello posting HTML-tester!
    EOT
    
    headers.content-type ~ /image\/jpe?g/ ==>
    	Hello JPEG-tester!
    EOT
    
    /just/some/path ==>
    	Hello path-tester!
    EOT
    
    /some/interpolated/response ==>
    	Hello {{headers.x-value}}-tester!
    EOT
    
    /api/:prefix/test ==>
    	Hello {{rule.params.prefix}}-tester!
    EOT
    
    /some/actual/message AND query.type === "actual" ==> MSG
    	set-cookie: mycookie=value:set
    	x-folded-value: so
    		me
    	  folded value
    
    	Hello {{query.type}} message-tester!
    EOM
    

    In a unit test this definition could be used like this:

    "use strict";
    
    const { describe, before, after } = require( "mocha" );
    const { HttpService } = require( "@cepharum/mockup-service" );
    
    const Mock = new HttpService( `
    headers.content-type = text/plain ==>
    	Hello tester!
    EOT
    
    headers.content-type = text/html AND method = POST ==>
    	Hello posting HTML-tester!
    EOT
    
    headers.content-type ~ /image\/jpe?g/ ==>
    	Hello JPEG-tester!
    EOT
    
    /just/some/path ==>
    	Hello path-tester!
    EOT
    
    /some/interpolated/response ==>
    	Hello {{headers.x-value}}-tester!
    EOT
    
    /api/:prefix/test ==>
    	Hello {{rule.params.prefix}}-tester!
    EOT
    
    /some/actual/message AND query.type === "actual" ==> MSG
    	set-cookie: mycookie=value:set
    	x-folded-value: so
    		me
    	  folded value
    
    	Hello {{query.type}} message-tester!
    EOM
    ` );
    
    describe( "Mock-up service for HTTP", () => {
    	before( () => Mock.start() );
    	after( () => Mock.stop() );
    
    	it( "responds ...", () => {
    		// return require( "node-fetch" )( Mock.url ).then( response => {} );
    	} );
    
    	// TODO: add more tests here
    } );

    Using Mock.url in actual tests to be added the mocked service's URL is available for use with your particular client library to be tested. Instead of using Mock.url you might also use Mock.address to fetch the IP address service is listening on and Mock.port to get the related port service is listening on.

    The service mock-up also includes a client Mock.query() for querying the service but this mostly for testing the implementation of mock-up service itself.

    API

    The API basically consists of services to be instantiated in combination with a script describing how the resulting service is meant to respond to either incoming request.

    As demonstrated before, in case of HTTP service this looks like this:

    const { HttpService } = require( "@cepharum/mockup-service" );
    const Mock = new HttpService( scriptDefiningRules );

    Methods

    Every such instance of a service provides the following methods:

    Mock.start() : Promise<{address, port}>

    Starts service for processing defined rules on every incoming request. The method promises information on then fully started service. This information is promised as object containing running service's IP address in property address and its port in property port.

    Mock.stop() : Promise

    Shuts down previously started service. Promises service shut down eventually.

    Mock.query() : Promise

    This method is provided to query the started service e.g. for testing whether some defined response is actually delivered. This is used a lot in testing this library. The method's signature stringly depends on type of service:

    HttpService: Mock.query( method, url [, headers [, body] ] ) : Promise

    This version accepts desired HTTP method and URL for request. In addition, an object listing request headers and some request payload might be given.

    Properties

    Mock.url

    This property is exposing the service's URL. Its content strongly depends on type of service. This information is reliable after having started service, only.

    Mock.address

    This property is exposing the address the service is listening on. This information is reliable after having started service, only.

    Mock.remoteAddress

    This property is exposing the address for accessing the service from a client's point of view. This information is reliable after having started service, only.

    Mock.port

    This property is exposing the port the service is listening on. This information is reliable after having started service, only.

    Events

    Either service instance is deriving from EventEmitter as provided by Node and thus it is possible to manage listeners for selected events to be emitted by either service.

    request

    The request-event is emitted prior to searching defined rules for the earliest one matching incoming request. Any listener is invoked with these arguments:

    1. descriptor of incoming request
    2. manager for controlling response

    response

    The response-event is emitted after having sent response to client. Any listener is invoked with these arguments:

    1. descriptor of incoming request
    2. manager for controlling response
    3. an array of Buffer instances sent in response
    4. descriptor of matched rule which delivered the response

    error

    The error-event is emitted when handling any request resulted in error. This might happen once per received request. Any listener is invoked with a description of error as instance of Error as sole argument.

    Syntax

    The script for mocking-up a service is a sequence of rules processed from top to bottom. Every rule defines a test to be performed on any incoming request and a response to send back in case of a matching test.

    <test> ==> <response>
    

    Several kinds of tests are supported:

    Tests

    Custom Comparison Tests

    Custom comparison tests consist of a source's name exposing some actual value, a comparing operator and a literal value to compare read source with.

    <source> <operator> <value>
    

    The source may consist of several segments separated by full stop with each segment selecting another level of previously addressed information while descending into a hierarchy of information. The initial source is description of current request. This description may be include different additional information depending on selected type of mock-up service.

    The operator is one out of these options:

    Operator Comparison
    = value equals source
    == value equals source
    != value is different from source
    <> value is different from source
    <> value is different from source
    === value equals source and is of same type
    eq value equals source and is of same type
    !== value is different from source or differs from source by type
    neq value is different from source or differs from source by type
    ne value is different from source or differs from source by type
    < value is less than source
    lt value is less than source
    <= value is less than or equal source
    lte value is less than or equal source
    > value is greater than source
    gt value is greater than source
    >= value is greater than or equal source
    gte value is greater than or equal source
    ~ value is a regular expression that matches source
    !~ value is a regular expression that doesn't match source

    :::tip Example path == /some/path is testing path name of URL in context of an HTTP service. In context of same service query.arg = 1 tests whether URL query parameter named arg has value 1 or not. :::

    See the following table for a list of probable sources:

    Name Description
    method verb of HTTP request selecting request method
    httpVersion HTTP version selected by request
    headers.* HTTP request headers - Replace * with name of header to read. Headers are provided with all letters converted to lowercase variants.
    url full request URL consisting of path and query
    path leading part of request URL up to first ?
    query.* query parameter extracted from part of request URL following first contained ? - Replace * with name of parameter to read.
    params.* parameter extracted from pathname of request URL (in opposition to parameters found in its query) - Works after using convenient path test as described below, only. Replace * with name of parameter to read
    body.* parameter extracted from transmitted body - Works if body is encoded as JSON and header "content-type" is set. Replace * with name of parameter to read.

    Conveniently Test Path

    Tests starting with a forward slash are considered to provide a path name pattern to be matched by requests. The provided pattern is passed through path-to-regexp to generate a pattern that's eventually used to test an incoming request. See description of supported syntax there.

    The pattern may include parameters to be exposed in request.params. This enables use of those parameters when defining interpolated responses.

    :::tip Example /some/:myname* is testing path name of URL in context of an HTTP service. Matching path names are /some, /some/sub, /some/deep/arbitrary etc. with exposing part following /some/ as params.myname e.g. for response interpolation. :::

    Combining Tests

    Multiple tests may be combined per rule using either keyword AND or OR between two adjacent test definitions. Using either keyword result in an intersection or union of combined tests. Multiple tests may be combined this way, but only one of the keywords may be used throughout the combination of tests in a single rule.

    :::tip Examples Intersection: <test-a> AND <test-b> or <test-a> AND <test-b> AND <test-c> Union: <test-a> OR <test-b> or <test-a> OR <test-b> OR <test-c> :::

    Responses

    In a rule's definition response is separated from preceding tests by ==>. This arrow must be written literally. Additional meta information on response might be given after that arrow, but in same line.

    Actual response starts after arrow and some optional meta information. Either kind of response may use custom marker for end of response, but currently all supported response types use EOT written in a separate line at least.

    Delaying Responses

    Responses may be delayed by intention. A delay is defined by writing number of milliseconds followed by another arrow ==> right after the arrow marking end of tests as described before.

    <test> ==> 1500 ==> <response>
    

    This results in response being sent back to client in about 1500ms.

    :::warning Due to Javascript lacking realtime capabilities either time may be considered estimate value of delay, only. :::

    By defining a range using two positive integer values separated by a single dash the actual delay per response is picked randomly from this defined range.

    <test> ==> 1500-5000 ==> <response>
    

    This results in response being sent back to client with an arbitrary delay between 1500ms and 5000ms.

    /just/some/path ==> 5000 ==>
    	Hello path-tester!
    EOT
    

    Selecting Type of Response

    The type of response is selected using special type name right after ==>. In case of declaring a delay this applies to the second ==>. Following type names are supported:

    Type Name Resulting Type of Response
    TEXT Simple Text
    MSG RFC-2822-Like Message
    MESSAGE RFC-2822-Like Message

    When omitting this information TEXT is used by default.

    :::warning Note: This information selects one out of several supported response handlers. It doesn't define the kind of actual response but the way it is defined. Using TEXT might be sufficient to sent arbitrary data, but MSG is required to be actually able to define response headers. So, sending plain text responses with custom headers still requires use of MSG response type. :::

    Simple Text Responses

    Starting with line succeeding the one containing ==> with optionally given meta information and type selector the actual response is provided as regular text. EOT must be written in a separate line to mark end of text.

    The text might be indented. Shallowest indentation common to all lines of response is ignored.

    /api/:prefix/test ==>
    	Hello {{rule.params.prefix}}-tester!
    EOT
    

    RFC-2822-Like Messages

    This type of response doesn't implicitly affect content of resulting response. However it enables support to separately define response headers and response payload in a format similar to RFC-2822. The meaning of response headers strongly depends on actually used service. In context of HTTP service the response headers are actual headers of a RFC-2822-compliant message sent back to client.

    Starting with line succeeding the one containing ==> with optionally given meta information and type selector the actual response is provided. Initial lines of response are defining response headers. Starting after first empty line of response the actual response payload is given. EOT must be written in a separate line to mark end of text.

    The response payload might be indented. Shallowest indentation common to all lines of response is ignored. Due to that this response type isn't suitable for sending binary data without proper transfer encoding.

    /some/actual/message AND query.type === "actual" ==> MSG
    	set-cookie: mycookie=value:set
    	x-folded-value: so
    		me
    	  folded value
    
    	Hello {{query.type}} message-tester!
    EOM
    

    Script Processing

    For every incoming request the whole sequence of rules is processed. Iterating over defined rules top to bottom the first rule in sequence with matching test is picked to provide a response. Any succeeding rules are ignored for the rest of either request.

    Keywords

    none

    Install

    npm i @cepharum/mockup-service

    DownloadsWeekly Downloads

    0

    Version

    0.1.5

    License

    MIT

    Unpacked Size

    74.6 kB

    Total Files

    18

    Last publish

    Collaborators

    • simon.friedo
    • soletan