MCPcopy Index your code
hub / github.com/checkr/openmock

github.com/checkr/openmock @v0.3.7

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.3.7 ↗ · + Follow
642 symbols 1,425 edges 98 files 448 documented · 70%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

<a href="https://goreportcard.com/report/github.com/checkr/openmock" target="_blank">
    <img src="https://goreportcard.com/badge/github.com/checkr/openmock">
</a>
<a href="https://circleci.com/gh/checkr/openmock" target="_blank">
    <img src="https://circleci.com/gh/checkr/openmock.svg?style=shield">
</a>
<a href="https://godoc.org/github.com/checkr/openmock" target="_blank">
    <img src="https://img.shields.io/badge/godoc-reference-green.svg">
</a>

OpenMock

OpenMock is a Go service that can mock services in integration tests, staging environment, or anywhere. The goal is to simplify the process of writing mocks in various channels. Currently it supports the following channels:

  • HTTP
  • gRPC
  • Kafka
  • AMQP (e.g. RabbitMQ)

Usage

Use it with docker.

$ docker run -it -p 9999:9999 -v $(pwd)/demo_templates:/data/templates checkr/openmock 

More complete openmock instance (e.g. redis) with docker-compose.

$ docker-compose up

Test it.

$ curl localhost:9999/ping

Dependencies.

  • HTTP (native supported, thanks to https://echo.labstack.com/)
  • One can configure HTTP port, set env OPENMOCK_HTTP_PORT=80
  • GRPC (supported through through HTTP/2 interface)
  • One can configure GRPC port, set env OPENMOCK_GRPC_PORT=50051
  • Kafka (optional)
  • To enable mocking kafka, set env OPENMOCK_KAFKA_ENABLED=true.
  • One can also config the following kafka parameters, optionally with separate config for consumers and producers. For example OPENMOCK_KAFKA_SEED_BROKERS, OPENMOCK_KAFKA_PRODUCER_SEED_BROKERS, and OPENMOCK_KAFKA_CONSUMER_SEED_BROKERS
    • OPENMOCK_KAFKA_SEED_BROKERS
    • OPENMOCK_KAFKA_SASL_USERNAME
    • OPENMOCK_KAFKA_SASL_PASSWORD
    • OPENMOCK_KAFKA_TLS_ENABLED
  • AMQP (optional)
  • To enable mocking amqp, set env OPENMOCK_AMQP_ENABLED=true
  • One can also config OPENMOCK_AMQP_URL.
  • NPM (development only)
  • Used in Makefile during swagger admin API server generation

OpenMock Templates

Templates are YAML files that describe the behavior of OpenMock.

Templates Directory

You can put any number of .yaml or .yml files in a directory, and then point environment variable OPENMOCK_TEMPLATES_DIR to it. OpenMock will recursively (including subdirectories) load all the YAML files. For example:

# OPENMOCK_TEMPLATES_DIR=./demo_templates

./demo_templates
├── amqp.yaml
├── files
│   └── colors.json
├── http.yaml
├── jsonrpc.yaml
├── kafka.yaml
└── payload_from_file.yaml

Schema

OpenMock is configured a list of behaviors for it to follow. Each behavior is identified by a key, and a kind:

- key: respond-to-resource
  kind: Behavior

Expect

It represents the channel to listen on and condition for the actions of the behavior to be performed. Available channels are:

  • http
  • kafka
  • amqp
  • grpc

For example, under what condition and from what channel should we proceed with the actions.

- key: no-op
  kind: Behavior
  expect:
    # Condition checks if we need to do the actions or not
    # It only proceeds if it evaluates to "true"
    condition: '{{.HTTPHeader.Get "X-Token" | eq "t1234"}}'
    # Use one (and only one) of the following channels - [http, kafka, amqp]
    http:
      method: GET
      path: /ping
    kafka:
      topic: hello_kafka_in
    amqp:
      exchange: exchange_1
      routing_key: key_in
      queue: key_in

Actions

Actions are a series of functions to run. Availabe actions are:

  • publish_amqp
  • publish_kafka
  • redis
  • reply_http
  • send_http
  • reply_grpc
  • sleep
- key: every-op
  kind: Behavior
  expect:
    http:
      method: GET
      path: /ping
  actions:
    - publish_kafka:
        topic: hello_kafka_out
        payload: >
          {
            "kafka": "OK",
            "data": {}
          }
    - sleep:
        duration: 1s
    - reply_http:
        status_code: 200
        body: OK
        headers:
          Content-Type: text/html

The actions by default run in the order defined in the mock file; you can adjust this by adding an int 'order' value from lowest to highest number. The default value for 'order' is 0.

- key: every-op
  kind: Behavior
  expect:
    http:
      method: GET
      path: /ping
  actions:
    - publish_kafka:
        topic: hello_kafka_out
        payload: >
          {
            "kafka": "OK",
            "data": {}
          }
    - sleep:
        duration: 1s
      # sleep first
      order: -1000

Templates

Templates can be useful to assemble your payloads from parts

- key: dog
  kind: Template
  template: >
    <animal>dog</animal>

- key: cat
  kind: Template
  template: >
    <animal>cat</animal>

# $ curl 0:9999/fred
# <human>   <name>fred</name>   <pets>     <animal>dog</animal>      <animal>cat</animal>    </pets> </human>
- key: get-freds-pets
  kind: Behavior
  expect:
    http:
      method: GET
      path: /fred
  actions:
    - reply_http:
        status_code: 200
        body: >
          <human>
            <name>fred</name>
            <pets>
              {{template "dog"}}
              {{template "cat"}}
            </pets>
          </human>

Abstract Behaviors

Abstract Behaviors can be used to parameterize some data.

When an abstract behavior and a behavior extending it both have actions defined, all of them are run when the behavior matches. Actions will run from lowest to highest value of the 'order' field; if this is the same for two actions the action defined earlier in the abstract behavior runs first, followed by actions in the concrete behavior. Be aware that values with all digits will be interpreted into int type (YAML syntax), and it will fail the condition check given that some helper functions are returning string types. Pipe to toString before the comparison or alternatively put quotes around the values. See example in abstract_behaviors.yml.

- key: fruit-of-the-day
  kind: AbstractBehavior
  values:
    fruit: potato
  expect:
    condition: '{{.HTTPQueryString | contains .Values.day}}'
    http:
      method: GET
      path: /fruit-of-the-day
  actions:
    - reply_http:
        status_code: 200
        body: '{"fruit": "{{.Values.fruit}}"}'

# $ curl 0:9999/fruit-of-the-day?day=monday
# {"fruit": "apple"}
- key: monday-fruit
  kind: Behavior
  extend: fruit-of-the-day
  values:
    day: monday
    fruit: apple

# $ curl 0:9999/fruit-of-the-day?day=tuesday
# {"fruit": "potato"}
- key: tuesday-fruit
  kind: Behavior
  extend: fruit-of-the-day
  values:
    day: tuesday
  actions: 
    # sleep then reply_http
    - sleep:
         duration: 1s
      order: -1000

Dynamic templating

OpenMock leverages https://golang.org/pkg/text/template/ to write dynamic templates. Specifically, it supports a lot of Context and Helper Functions.

  • Usage of {{ expr }}. One can put {{ expr }} inside three types of places:

  • expect.condition

  • action.http.body, action.grpc.payload, action.kafka.payload, action.amqp.payload
  • action.http.body_from_file, action.http.body_from_binary_file, action.http.binary_file_name ,action.grpc.payload_from_file, action.kafka.payload_from_file, action.amqp.payload_from_file ({{ expr }} will be in the file)

  • Use Context inside {{ expr }}.

```bash .HTTPHeader # type: http.Header; example: {{.HTTPHeader.Get "X-Token"}} .HTTPBody # type: string; example: {{.HTTPBody}} .HTTPPath # type: string; example: {{.HTTPPath}} .HTTPQueryString # type: string; example: {{.HTTPQueryString}}

.GRPCHeader # type: string; example: {{.GRPCHeader}} .GRPCPayload # type: string; example: {{.GRPCPayload}} .GRPCService # type: string; example: {{.GRPCService}} .GRPCMethod # type: string; example: {{.GRPCMethod}}

.KafkaTopic # type: string; example: {{.KafkaTopic}} .KafkaPayload # type: string; example: {{.KafkaPayload}}

.AMQPExchange # type: string; example: {{.AMQPExchange}} .AMQPRoutingKey # type: string; example: {{.AMQPRoutingKey}} .AMQPQueue # type: string; example: {{.AMQPQueue}} .AMQPPayload # type: string; example: {{.AMQPPayload}} ```

  • Use helper functions inside {{ expr }}. We recommend pipeline format (|) of the functions.

```bash # Supported functions defined in ./template_helper.go

- 
- jsonPath    # doc: https://github.com/antchfx/xpath
- gJsonPath   # doc: https://github.com/tidwall/gjson
- xmlPath     # doc: https://github.com/antchfx/xpath
- uuidv5      # uuid v5 sha1 hash
- redisDo     # run redis commands. For example {{redisDo "RPUSH" "arr" "hi"}}
- ...

# Supported functions inherited from # https://github.com/Masterminds/sprig/blob/master/functions.go

- replace
- uuidv4
- regexMatch
- ...

# Examples {{.HTTPHeader.Get "X-Token" | eq "t1234"}} {{.HTTPBody | jsonPath "user/first_name" | replace "A" "a" | uuidv5 }} {{.HTTPBody | gJsonPath "users.0.first_name" }} {{.HTTPBody | xmlPath "node1/node2/node3"}} ```

Admin Interface

Openmock also by default provides an API on port 9998 to control the running instance. See api documentation. You can serve the api documentation by getting go-swagger and running:

./swagger serve --host 0.0.0.0 --port 9997 docs/api_docs/bundle.yaml"

Command Line Interface

Openmock has a command-line interface to help with certain tasks interacting with openmock instances. This is invoked with the omctl command. This uses the cobra library to provide a discoverable CLI; run omctl for a list of commands / flags.

CLI: Directory

Push

Pushes a local openmock model from the file system to a remote instance.

# Adds templates from the ./demo_templates directory to the instance running on localhost.
omctl push --directory ./demo_templates --url http://localhost:9998

Examples

Example: Mock HTTP

# demo_templates/http.yaml

# $ curl 0:9999/ping
# OK
- key: ping
  kind: Behavior
  expect:
    http:
      method: GET
      path: /ping
  actions:
    - reply_http:
        status_code: 200
        body: OK
        headers:
          Content-Type: text/html

# $ curl 0:9999/token -H X-Token:t1234 -H Y-Token:t1234
# OK
- key: header-token-200
  kind: Behavior
  expect:
    condition: '{{.HTTPHeader.Get "X-Token" | eq "t1234" | and (.HTTPHeader.Get "Y-Token" | eq "t1234")}}'
    http:
      method: GET
      path: /token
  actions:
    - reply_http:
        status_code: 200
        body: OK

# $ curl 0:9999/token
# Invalid X-Token
- key: header-token-401
  kind: Behavior
  expect:
    condition: '{{.HTTPHeader.Get "X-Token" | ne "t1234"}}'
    http:
      method: GET
      path: /token
  actions:
    - reply_http:
        status_code: 401
        body: Invalid X-Token

Example: Mock HTTP and reply with binary file

- key: get-pdf
  expect:
    http:
      method: GET
      path: /api/v1/:ClientID/pdf
    condition: '{{
      (.HTTPHeader.Get "Authorization" | contains "exp") | and
      (.HTTPHeader.Get "x-timestamp" | eq "" | not) 
    }}'
  actions:
    - reply_http:
        status_code: 200
        headers:
          Content-Type: application/pdf
        body_from_binary_file: ./data/example.pdf
        binary_file_name: example_pdf.pdf # optional file name

Example: Mock HTTP and reply with body from file

- key: get-json
  expect:
    http:
      method: GET
      path: /api/v1/:ClientID/json
    condition: '{{
      (.HTTPHeader.Get "Authorization" | contains "exp") | and
      (.HTTPHeader.Get "x-timestamp" | eq "" | not) 
    }}'
  actions:
    - reply_http:
        status_code: 200
        headers:
          Content-Type: application/json
        body_from_file: ./data/example.json # only text files supported

Example: Mock GRPC

# demo_templates/grpc.yaml

- key: example_grpc
  expect:
    grpc:
      service: demo_protobuf.ExampleService
      method: ExampleMethod
  actions:
    - reply_grpc:
        payload_from_file: './files/example_grpc_response.json'

Example: Mock Kafka

# demo_templates/kafka.yaml

- key: test_kafka_1
  kind: Behavior
  expect:
    kafka:
      topic: hello_kafka_in
  actions:
    - publish_kafka:
        topic: hello_kafka_out
        payload: >
          {
            "kafka": "OK",
            "data": {}
          }

- key: test_kafka_2
  kind: Behavior
  expect:
    kafka:
      topic: hello_kafka_in_2
  actions:
    - publish_kafka:
        topic: hello_kafka_out
        payload_from_file: './files/colors.json' # the path is relative to OPENMOCK_TEMPLATES_DIR

If you started the example from docker-compose, you can test the above kafka mocks by using a kt docker container.

# Exec into the container
docker-compose exec kt bash

# Run some kt commands inside the container
# Notice that the container is within the docker-compose network, and it connects to "kafka:9092"

$ kt topic
$ echo '{"123":"hi"}' | kt produce -topic hello_kafka_in -literal
$ kt consume -topic hello_kafka_out -offsets all=newest:newest

Example: Mock AMQP (e.g. RabbitMQ)

```yaml

demo_templates/amqp.yaml

  • key: test_amqp_1 kind: Behavior expect: amqp: exchange: exchange_1 routing_key: key_in queue: key_in actions:

    • publish_amqp: exchange: exchange_1 routing_key: key_out payload: > { "amqp": "OK", "data": {} }
  • key: test_amqp_2 kind: Behavior expect: amqp: exchange: exchange_1

Extension points exported contracts — how you extend this code

Action (Interface)
(no doc) [7 implementers]
model.go
RedisDoer (Interface)
RedisDoer can run redis commands [1 implementers]
redis.go
KafkaPipelineFunc (FuncType)
KafkaPipelineFunc defines pipeline functions For example, decode/encode messages
kafka.go
EvaluateHandlerFunc (FuncType)
EvaluateHandlerFunc turns a function with the right signature into a evaluate handler
swagger_gen/restapi/operations/evaluate/evaluate.go
CRUD (Interface)
(no doc) [1 implementers]
pkg/admin/crud.go
EvaluateHandler (Interface)
EvaluateHandler interface for that can handle valid evaluate params
swagger_gen/restapi/operations/evaluate/evaluate.go
DeleteTemplatesHandlerFunc (FuncType)
DeleteTemplatesHandlerFunc turns a function with the right signature into a delete templates handler
swagger_gen/restapi/operations/template/delete_templates.go
DeleteTemplatesHandler (Interface)
DeleteTemplatesHandler interface for that can handle valid delete templates params
swagger_gen/restapi/operations/template/delete_templates.go

Core symbols most depended-on inside this repo

Render
called by 42
template.go
Do
called by 24
redis.go
SetupRepo
called by 12
openmock.go
Fatalf
called by 12
swagger_gen/restapi/server.go
gJsonPath
called by 11
template_helper.go
loadFile
called by 10
load.go
Logf
called by 10
swagger_gen/restapi/server.go
Validate
called by 9
model.go

Shape

Method 379
Function 136
Struct 96
Interface 13
FuncType 9
TypeAlias 9

Languages

Go100%

Modules by API surface

demo_protobuf/example.pb.go32 symbols
model.go25 symbols
swagger_gen/restapi/operations/open_mock_api.go23 symbols
pkg/admin/crud.go22 symbols
swagger_gen/restapi/server.go20 symbols
swagger_gen/restapi/operations/template/delete_template_responses.go20 symbols
swagger_gen/restapi/operations/template_set/post_template_set_responses.go17 symbols
swagger_gen/restapi/operations/template/post_templates_responses.go17 symbols
swagger_gen/restapi/operations/template/get_templates_responses.go12 symbols
swagger_gen/restapi/operations/health/get_health_responses.go12 symbols
swagger_gen/restapi/operations/evaluate/evaluate_responses.go12 symbols
swagger_gen/models/action_dispatcher.go11 symbols

For agents

$ claude mcp add openmock \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact