Cornell record & replay mock server

Related tags

Testing cornell
Overview

Cornell: record & replay mock server

Build Status

Cornell makes it dead simple, via its record and replay features to perform end-to-end testing in a fast and isolated testing environment.

Cornell Logo

When your application integrates with multiple web based services, end-to-end testing is crucial before deploying to production. Mocking is often a tedious task, it becomes even more tiresome when working with multiple APIs from multiple vendors.

vcrpy is an awesome library that records and replays HTTP interactions for unit tests. Its output is saved to reusable "cassette" files.

By wrapping vcrpy with Flask, Cornell provides a lightweight record and replay server that can be easily used during distributed system testing and simulate all HTTP traffic needed for your tests.

Basic Use Case

When you're working with distributed systems, the test client entry point triggers a cascade of events that eventually send HTTP requests to an external server

System in test

With Cornell server started, it will act as a proxy (record mode) between the outgoing HTTP requests and the external server and will record all relevant interactions. Once interactions are recorded, Cornell can be work in replay mode and replace the external server entirely, short circuting the calls and instead, replying back instantly with the previously recorded response.

System in test

Installation

To install from PyPI, all you need to do is this:

  pip install cornell

Usage

Usage: cornell_server.py [OPTIONS]

  Usage Examples: Record mode: `cornell --forward_uri="https://remote_server/api" --record -cd custom_cassette_dir`
  Replay mode: `cornell -cd custom_cassette_dir

Options:
  -p, --port INTEGER
  -ff, --forward_uri TEXT         Must be provided in case of recording mode
  -, --record-once / --record-all
                                  Record each scenario only once, ignore the
                                  rest

  -r, --record                    Start server in record mode
  -fp, --fixed-path               Fixed cassettes path. If enabled, Cornell
                                  will support only one server for recording

  -cd, --cassettes-dir TEXT       Cassettes parent directory, If not
                                  specified, Cornell parent dir will be used

  --help                          Show this message and exit.

Demo - Full Example

Staring Cornell in record mode:

cornell -ff https://api.github.com/ --record -cd cassettes

This will start the server in record-proxy mode on port 9000, and will forward all requests to https://api.github.com/

asciicast

When cornell is in record mode, it will forward all request to the specified forwarding url, for example:

requests.get("http://127.0.0.1:9000/github/repos/kevin1024/vcrpy/license").json()

or

requests.get("http://127.0.0.1:9000/github/repos/kevin1024/vcrpy/license").json()

or you can browse to the url using your browser

Browser

Cornell will forward the request to the specified url and will record both the request and the response.

The yaml cassettes will be recorded in dedicated dictory (cassettes in the root dir, by default)

For example:

Cassette dir

Note

By default, `cassettes` directory will be created in cornell's root dir and will contain the cassette by destination hierarchy.
Use `-cd` to specify custom directory for your cassettes.
Mind that `-cd  should match for both record and replay modes

Once all the necessary interactions were recorded, stop cornell server using ctrl+c. Once stopped, all interactions will be mapped via an auto-generated index.yaml file.

Note

In case the `index.yaml` was already present, it will be updated with new interactions, otherwise new file will be created.

In this specific example, we can see that the 2 requests are mapped to the saved cassettes:

Index file

Features

Request Matchers

In addition to the vcrpy matchers, cornell provides the following custom request matchers:

  • OData request query matcher
  • SOAP request body matcher

Environment Variables

Since Cornell is a testing server it's executed by default with FLASK_ENV=local. You can modify this as described in flask configuration

Advanced Features

Can be found in the documentation

Contributing

Yes please! contributions are more than welcome!

Please follow PEP8 and the Python Naming Conventions

Add tests when you're adding new functionality and make sure all the existing tests are happy and green :)

To set up development environment:

  python -m venv venv
  source venv/bin/activate
  make configure

Running Tests

To run tests, run the following command

  python -m venv venv
  source venv/bin/activate
  make test
Comments
  • TypeError: get_custom_vcr() got multiple values for argument 'base_uri'

    TypeError: get_custom_vcr() got multiple values for argument 'base_uri'

    Hello, it seems that running this example in the docs throws a TypeError: get_custom_vcr() got multiple values for argument 'base_uri

    The problematic code is directly from the doc:

    #!/usr/bin/env python
    
    import click
    import json
    from vcr.util import read_body
    from cornell.cornell_server import CornellCmdOptions, start_cornell
    from cornell.cornell_helpers import json_in_headers
    from cornell.custom_matchers import requests_match_conditions
    
    
    # Custom Matcher
    @requests_match_conditions(json_in_headers, lambda request: request.body)
    def vcr_json_custom_body_matcher(received_request, cassette_request):
        received_request_dict = json.loads(read_body(received_request))
        cassette_request_dict = json.loads(read_body(cassette_request))
        if received_request_dict == cassette_request_dict or "special_params" not in received_request_dict:
            return True
        return is_specially_matched(received_request_dict, cassette_request_dict)
    
    
    @click.command(cls=CornellCmdOptions)
    def start_mock_service(**kwargs):
        start_cornell(additional_vcr_matchers=[vcr_json_custom_body_matcher], **kwargs)
    
    
    if __name__ == "__main__":
        start_mock_service()
    

    Dockerfile to reproduce:

    FROM python:3.9-alpine
    
    RUN pip install cornell
    COPY cornell/main.py .
    EXPOSE 9000
    
    ENTRYPOINT ["python", "main.py"]
    

    It seems that changing the line 189 in cornell_server.py to app.config.vcr = get_custom_vcr(app.config.base_uri, DUMMY_URL, record_errors, *additional_vcr_matchers) fixes this, but it seems hacky. Overall I haven't been able to make the custom matchers work, anybody has a working example ? I'm looking to have a sidecar container that will record every request with same data parameters to an API, this seems like something cornell should do

    opened by Wats0ns 4
  • build a docker image for cornell

    build a docker image for cornell

    In our development, we got stuck using cornell because one of our libraries didn't support Flask 2.0.0.

    One solution would be to run cornell on an independent execution space. A docker image on dockerhub would solve this problem. I am preparing a PR to package a Dockerfile at the root of the repo.

    I publish a repository in my own namespace. I have configured a rebuild every 24 hours. I will remove it as soon as you publish your own dockerhub repository.

    docker run hiredscorelabs/cornell
    
    opened by FabienArcellier 3
  • Support for --host argument to replace default listening IP address of 127.0.0.1

    Support for --host argument to replace default listening IP address of 127.0.0.1

    Use Case

    Some developers may be running their IDE from a different computer and remotely connecting to Cornell. Rather than defaulting to listening on 127.0.0.1, is it possible to support a --host argument, e.g.

    --host=0.0.0.0

    This will result in replacing the Flask running code:

    app.run(port=port, threaded=False)

    with:

    app.run(host=0.0.0.0, port=port, threaded=False)

    Happy to create a PR if you are agreeable.

    opened by lcshiung 3
  • keep the headers on classic requests and remove it on redirection

    keep the headers on classic requests and remove it on redirection

    The header Content-Type is dropped when it's not a redirection. It seems tobe the opposite behavior of the commented out issue. I suppose on this one it's an error of code.

    fix #14

    opened by FabienArcellier 3
  • I can't record and replay a route that use the method DELETE

    I can't record and replay a route that use the method DELETE

    Hello,

    I am trying to record the result of an API call that requires DELETE method. The script get a 405 error (method not allowed) Have you blocked this behavior on purpose ? Can I propose a PR to add the support of this method ?

    In cornell/cornell_server.py :

    SUPPORTED_METHODS = ['GET', 'POST', 'PUT']
    
    opened by FabienArcellier 3
  • Index.yaml file is empty when cornell is stopped by SIGTERM

    Index.yaml file is empty when cornell is stopped by SIGTERM

    I run cornell as a background process in automatic testing to record the context of some test. In that case, the file index.yaml is not dumped properly at the end of the process and is empty. It makes replay not usable without removing the index files. (I am not sure of its real usage)

    @contextmanager
    def record_cassette(api: str, cassette_path: str, port=9000):
        cornell = local['cornell']
        log = os.path.join(cassette_path, 'cassette.log')
        if os.path.isfile(log):
            os.remove(log)
    
        with io.open(log, 'a') as fp:
            run = (cornell['-ff', api, '--record', '-fp', '-cd', cassette_path, '-p', port] & BG(stdout=fp, stderr=fp))
            try:
                time.sleep(2)
                yield f'http://localhost:{port}'
    
            finally:
                run.proc.terminate()
    

    I will open a PR to fix this issue

    app.logger.info("Starting Cornell", app_name=app.name, port=port, record=record, record_once=record_once,
                        fixed_path=fixed_path, forward_uri=forward_uri, cassettes_dir=str(cassettes_dir))
    atexit.register(on_cornell_exit, app=app)
    signal.signal(signal.SIGTERM, lambda: on_cornell_exit(app))
    app.run(port=port, threaded=False)
    
    opened by FabienArcellier 2
  • small change in update_nested_dict_value using copy

    small change in update_nested_dict_value using copy

    @yael-malwa Think it is better to use copy() rather than creating a tmp vars in update_nested_dict_value method. Also I runed black formatter on the file (advising you to use it robustly). Also advising to use flake8.

    To add you as a reviewer I need you to add me as a collaborator

    opened by DavidMeu 2
  • Index.yaml file is empty when cornell is stopped by SIGTERM

    Index.yaml file is empty when cornell is stopped by SIGTERM

    I run cornell as a background process in automatic testing to record the context of some test. In that case, the file index.yaml is not dumped properly at the end of the process and is empty. It makes replay not usable without removing the index files.

    I fix this behavior using signal.signal handler

    fix #10

    opened by FabienArcellier 1
  • [CI] Add docker image build and push pipeline

    [CI] Add docker image build and push pipeline

    Built and pushed successfully when I used a test branch: https://app.travis-ci.com/github/hiredscorelabs/cornell/jobs/546283438 https://hub.docker.com/repository/docker/hiredscorelabs/cornell

    opened by look4regev 0
  • Added support for --host (-h) argument

    Added support for --host (-h) argument

    Added support for --host (or -h) argument. This is defaulted to "127.0.0.1" if not specified.

    # cornell  
    2021-10-23 00:13.18 [info     ] Starting Cornell               app_name=CornellMock cassettes_dir=None fixed_path=False forward_uri=None host=127.t0.0.1 port=9000 record=False record_errors=False record_once=True  
    * Serving Flask app 'CornellMock' (lazy loading)
    * Environment: local
    * Debug mode: off
    * Running on http://127.0.0.1:9000/ (Press CTRL+C to quit)
    
    # cornell -h 0.0.0.0
    2021-10-23 00:13.26 [info     ] Starting Cornell               app_name=CornellMock cassettes_dir=None fixed_path=False forward_uri=None host=0.0.0.0 port=9000 record=False record_errors=False record_once=True
     * Serving Flask app 'CornellMock' (lazy loading)
     * Environment: local
     * Debug mode: off
     * Running on all addresses.
       WARNING: This is a development server. Do not use it in a production deployment.
     * Running on http://192.168.1.85:9000/ (Press CTRL+C to quit)
    

    Fixes #19

    opened by lcshiung 0
  • Keep the Content-type when the query is not a redirection

    Keep the Content-type when the query is not a redirection

    The header Content-Type is dropped when it's not a redirection. It seems tobe the opposite behavior of the commented out issue. I suppose on this one it's an error of code.

    As there is no content-type, Postman is reading it as full text.

    image

    instead of

    image

    On the code, it check if resp.status_code is not a Temporary Redirect and Permanent Redirect. I think the condition would be to check if resp.status_code is a Temporary Redirect.

    def _processes_headers(resp):
        if resp.status_code not in (HTTPStatus.TEMPORARY_REDIRECT, HTTPStatus.PERMANENT_REDIRECT):
            # https://github.com/psf/requests/issues/3490
            for header in ('Content-Length', 'Content-Type', 'Transfer-Encoding'):
                resp.headers.pop(header, None)
    
    opened by FabienArcellier 3
Owner
HiredScoreLabs
Open Source from HiredScore Engineering
HiredScoreLabs
a socket mock framework - for all kinds of socket animals, web-clients included

mocket /mɔˈkɛt/ A socket mock framework for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support ...and then MicroPytho

Giorgio Salluzzo 249 Dec 14, 2022
Automatically mock your HTTP interactions to simplify and speed up testing

VCR.py ?? This is a Python version of Ruby's VCR library. Source code https://github.com/kevin1024/vcrpy Documentation https://vcrpy.readthedocs.io/ R

Kevin McCarthy 2.3k Jan 1, 2023
Automatically mock your HTTP interactions to simplify and speed up testing

VCR.py ?? This is a Python version of Ruby's VCR library. Source code https://github.com/kevin1024/vcrpy Documentation https://vcrpy.readthedocs.io/ R

Kevin McCarthy 1.8k Feb 7, 2021
Aioresponses is a helper for mock/fake web requests in python aiohttp package.

aioresponses Aioresponses is a helper to mock/fake web requests in python aiohttp package. For requests module there are a lot of packages that help u

null 402 Jan 6, 2023
a socket mock framework - for all kinds of socket animals, web-clients included

mocket /mɔˈkɛt/ A socket mock framework for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support ...and then MicroPytho

Giorgio Salluzzo 208 Jan 31, 2021
Mockoon is the easiest and quickest way to run mock APIs locally. No remote deployment, no account required, open source.

Mockoon Mockoon is the easiest and quickest way to run mock APIs locally. No remote deployment, no account required, open source. It has been built wi

mockoon 4.4k Dec 30, 2022
Thin-wrapper around the mock package for easier use with pytest

pytest-mock This plugin provides a mocker fixture which is a thin-wrapper around the patching API provided by the mock package: import os class UnixF

pytest-dev 1.5k Jan 5, 2023
Mock smart contracts for writing Ethereum test suites

Mock smart contracts for writing Ethereum test suites This package contains comm

Trading Strategy 222 Jan 4, 2023
Turn any OpenAPI2/3 and Postman Collection file into an API server with mocking, transformations and validations.

Prism is a set of packages for API mocking and contract testing with OpenAPI v2 (formerly known as Swagger) and OpenAPI v3.x. Mock Servers: Life-like

Stoplight 3.3k Jan 5, 2023
Wraps any WSGI application and makes it easy to send test requests to that application, without starting up an HTTP server.

WebTest This wraps any WSGI application and makes it easy to send test requests to that application, without starting up an HTTP server. This provides

Pylons Project 325 Dec 30, 2022
Django-google-optimize is a Django application designed to make running server side Google Optimize A/B tests easy.

Django-google-optimize Django-google-optimize is a Django application designed to make running Google Optimize A/B tests easy. Here is a tutorial on t

Adin Hodovic 39 Oct 25, 2022
Kent - Fake Sentry server for local development, debugging, and integration testing

Kent is a service for debugging and integration testing Sentry.

Will Kahn-Greene 100 Dec 15, 2022
Ripurei is a free-to-use osu! replay downloader, that can be configured to download from any osu! server.

Ripurei Ripurei is a fully functional osu! replay downloader, fully capable of downloading from almost any osu! server. Functionality Timeline ✔️ Able

Thomas 0 Feb 11, 2022
CBKH: The Cornell Biomedical Knowledge Hub

Cornell Biomedical Knowledge Hub (CBKH) CBKG integrates data from 18 publicly available biomedical databases. The current version of CBKG contains a t

null 44 Dec 21, 2022
SAS output to EXCEL converter for Cornell/MIT Language and acquisition lab

CORNELLSASLAB SAS output to EXCEL converter for Cornell/MIT Language and acquisition lab Instructions: This python code can be used to convert SAS out

null 2 Jan 26, 2022
User-interest mock backend server implemnted using flask restful, and SQLAlchemy ORM confiugred with sqlite

Flask_Restful_SQLAlchemy_server User-interest mock backend server implemnted using flask restful, and SQLAlchemy ORM confiugred with sqlite. Backend b

Austin Weigel 1 Nov 17, 2022
Pokemon sword replay capture

pokemon-sword-replay-capture This is an old version (March 2020) pokemon-sword-replay-capture-mar-2020-version of my Pokemon Replay Capture software.

null 11 May 15, 2022
Replay Felica Exchange For Python

FelicaReplay Replay Felica Exchange Description Standalone Replay Module Usage Save FelicaRelay (>=2.0) output to file, then python replay.py [FILE].

null 3 Jul 14, 2022
a socket mock framework - for all kinds of socket animals, web-clients included

mocket /mɔˈkɛt/ A socket mock framework for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support ...and then MicroPytho

Giorgio Salluzzo 249 Dec 14, 2022
Automatically mock your HTTP interactions to simplify and speed up testing

VCR.py ?? This is a Python version of Ruby's VCR library. Source code https://github.com/kevin1024/vcrpy Documentation https://vcrpy.readthedocs.io/ R

Kevin McCarthy 2.3k Jan 1, 2023