Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tavern GRPC support #438

Merged
merged 86 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
d546e83
Tavern GRPC support
Sep 5, 2019
5da3fac
fix pylint issue
Sep 5, 2019
66dc3a7
Merge branch 'master' into grpc-support
michaelboulton Sep 7, 2019
62f8abe
Merge branch 'master' of ssh://github.com/taverntesting/tavern into g…
Sep 11, 2019
eafcaac
Fix import module for GRPC and make errors more informative
Sep 11, 2019
e1fad44
Merge branch 'grpc-support' of ssh://github.com/kiril-me/tavern into …
Sep 11, 2019
afe880e
Merge branch 'master' into grpc-support
kiril-me Jan 2, 2020
adee817
Merge branch 'master' into kr-grpc
michaelboulton Apr 5, 2020
2d0c83f
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Dec 19, 2020
703dca6
Fix python2/3 things which now no longer apply
michaelboulton Dec 19, 2020
b9a0112
Fix format
michaelboulton Dec 19, 2020
3f8cba8
disable duplicate code warning
michaelboulton Dec 24, 2020
552d6a0
Fix type errors
michaelboulton Dec 24, 2020
2281aa8
Fix some issues causing tests to fail
michaelboulton Dec 24, 2020
e9ce410
Disable some useless checks in pylint
michaelboulton Dec 24, 2020
12469cf
try to fix CI
michaelboulton Dec 24, 2020
e98bb05
Add more extenral packages
michaelboulton Dec 24, 2020
01241be
Use other client
michaelboulton Dec 24, 2020
0213946
Merge branch 'master' into grpc-support
michaelboulton Mar 27, 2021
b2ce791
Lint
michaelboulton Mar 27, 2021
285d48c
Merge master
michaelboulton Feb 21, 2023
5bc389c
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Feb 21, 2023
21a4a05
Fix imports
michaelboulton Feb 21, 2023
ee0c30c
Put backends in one file
michaelboulton Apr 2, 2023
924f009
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Apr 2, 2023
9ee78d7
Update version in grpc docker file
michaelboulton Apr 2, 2023
d537dba
Regenerate grpc definitions appropriately
michaelboulton Apr 2, 2023
4cdd863
Fix grpc and add debug flag
michaelboulton Apr 2, 2023
35916ef
Fix validation
michaelboulton Apr 2, 2023
b2f2ad5
Clean up some code and add more exceptions and tests
michaelboulton Apr 8, 2023
7c3faee
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Apr 15, 2023
7436302
Start writing docs and fix some warnings/errors/cleanup
michaelboulton Apr 15, 2023
0c20543
Fix some more issues with compiling protos and reflection
michaelboulton Apr 15, 2023
b2f72cd
Check for instance where there's no proto files
michaelboulton Apr 15, 2023
ebf542a
More docs
michaelboulton Apr 15, 2023
d72c425
Fix lint
michaelboulton Apr 15, 2023
7412530
Fix more lint
michaelboulton Apr 15, 2023
d69c733
Run grpc in CI
michaelboulton Apr 15, 2023
80471d0
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Oct 22, 2023
12eddf2
Clarify importing grpc modules
michaelboulton Oct 29, 2023
5beded5
Cleanu
michaelboulton Oct 29, 2023
bc37b31
fix mypy
michaelboulton Oct 29, 2023
d1496b7
grpc as optional
michaelboulton Oct 29, 2023
abee27a
Add warning
michaelboulton Oct 29, 2023
f8c811b
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Oct 29, 2023
987b628
Fix tests
michaelboulton Oct 29, 2023
8747c12
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Jan 6, 2024
cfdd1c3
Fix missing
michaelboulton Jan 6, 2024
5a56368
f-string
michaelboulton Jan 6, 2024
17a83d3
fix backend check
michaelboulton Jan 6, 2024
522b182
Annotate
michaelboulton Jan 6, 2024
c21a552
Add a couple more precommit hooks
michaelboulton Jan 6, 2024
5d8cf6e
Fix new ruff warnings
michaelboulton Jan 6, 2024
d61dc7f
Setup protoc in grpc tests
michaelboulton Jan 6, 2024
6d8dbe1
Try updating dependencies
michaelboulton Jan 6, 2024
d33f2e4
Annotations
michaelboulton Jan 6, 2024
d44a043
fix port for tests
michaelboulton Jan 6, 2024
ce99cc5
Missing host spec
michaelboulton Jan 8, 2024
1b2dd97
Format with ruff
michaelboulton Jan 16, 2024
cb037a8
Merge remote-tracking branch 'origin/master' into grpc-support
michaelboulton Jan 18, 2024
1beb3c9
pb2 files generated
michaelboulton Jan 18, 2024
1740152
document files
michaelboulton Jan 18, 2024
3c0c2b7
docs
michaelboulton Jan 18, 2024
21cdacb
fix import err
michaelboulton Jan 18, 2024
dbc2a25
dont include whole errror
michaelboulton Jan 18, 2024
48b684a
Add unit test
michaelboulton Jan 18, 2024
da53616
Clean up tavernhook
michaelboulton Jan 18, 2024
2b89dff
More docs
michaelboulton Jan 18, 2024
1d47425
More docs
michaelboulton Jan 18, 2024
9d3d28c
Add more limitations/plans
michaelboulton Jan 18, 2024
e85a3ee
Add initial unit test code for grpc
michaelboulton Jan 18, 2024
c2b8e1e
Initial 'empty' test
michaelboulton Jan 18, 2024
f57f5af
Parametrize tests
michaelboulton Jan 18, 2024
7428215
cleanup + annotate
michaelboulton Jan 18, 2024
ac0f60e
Clean up values from grpc call method
michaelboulton Jan 18, 2024
3316675
Fix method name servic elookup
michaelboulton Jan 18, 2024
1146947
Fix serialisation, tests, check type, etc
michaelboulton Jan 18, 2024
5cdb541
More test
michaelboulton Jan 18, 2024
acbd452
fix some type annotations
michaelboulton Jan 18, 2024
1031649
Fix bad body error
michaelboulton Jan 18, 2024
641a3e0
Extra test
michaelboulton Jan 18, 2024
a7c1d1d
Move file
michaelboulton Jan 18, 2024
fdb3614
More docs
michaelboulton Jan 18, 2024
0bd7f28
Fix response checking
michaelboulton Jan 19, 2024
efc30e2
Update deps
michaelboulton Jan 20, 2024
ff23c76
Add warning
michaelboulton Jan 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/*_pb2.py linguist-generated
**/*_pb2_grpc.py linguist-generated
**/*_pb2.pyi linguist-generated
8 changes: 8 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ jobs:
TOXCFG: tox-integration.ini
- TOXENV: py3-advanced
TOXCFG: tox-integration.ini
- TOXENV: py3-grpc
TOXCFG: tox-integration.ini

services:
docker:
Expand Down Expand Up @@ -123,6 +125,12 @@ jobs:
with:
python-version: "3.11"

- name: Install Protoc
if: ${{ contains(matrix.TOXENV, 'grpc') }}
uses: arduino/setup-protoc@v2
with:
version: "23.x"

- name: install deps
run: |
pip install tox -c constraints.txt
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,4 @@ bazel-out
bazel-tavern
bazel-testlogs

example/grpc/proto
6 changes: 4 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ If on Windows, you should be able to just run the 'tox' commands in that file.

1. Update requirements files (BOTH of them)

pip-compile --all-extras --resolver=backtracking pyproject.toml --output-file requirements.txt --reuse-hashes --generate-hashes
pip-compile --all-extras --resolver=backtracking pyproject.toml --output-file constraints.txt --strip-extras
```shell
pip-compile --all-extras --resolver=backtracking pyproject.toml --output-file requirements.txt --reuse-hashes --generate-hashes -U
pip-compile --all-extras --resolver=backtracking pyproject.toml --output-file constraints.txt --strip-extras -U
```

1. Run tests as above

Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
include tavern/_core/schema/tests.jsonschema.yaml
include tavern/_plugins/mqtt/jsonschema.yaml
include tavern/_plugins/grpc/schema.yaml
include LICENSE
74 changes: 67 additions & 7 deletions constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --all-extras --output-file=constraints.txt --resolver=backtracking --strip-extras pyproject.toml
# pip-compile --all-extras --output-file=constraints.txt --strip-extras pyproject.toml
#
alabaster==0.7.16
# via sphinx
Expand All @@ -25,7 +25,9 @@ build==1.0.3
bump2version==1.0.1
# via tavern (pyproject.toml)
cachetools==5.3.2
# via tox
# via
# google-auth
# tox
certifi==2023.11.17
# via requests
cffi==1.16.0
Expand Down Expand Up @@ -68,20 +70,54 @@ docutils==0.20.1
# tavern (pyproject.toml)
execnet==2.0.2
# via pytest-xdist
faker==22.2.0
faker==22.4.0
# via tavern (pyproject.toml)
filelock==3.13.1
# via
# tox
# virtualenv
flask==3.0.0
flask==3.0.1
# via tavern (pyproject.toml)
flit==3.9.0
# via tavern (pyproject.toml)
flit-core==3.9.0
# via flit
fluent-logger==0.10.0
# via tavern (pyproject.toml)
google-api-core==2.15.0
# via google-api-python-client
google-api-python-client==2.114.0
# via tavern (pyproject.toml)
google-auth==2.26.2
# via
# google-api-core
# google-api-python-client
# google-auth-httplib2
google-auth-httplib2==0.2.0
# via google-api-python-client
googleapis-common-protos==1.62.0
# via
# google-api-core
# grpcio-status
grpc-interceptor==0.15.4
# via tavern (pyproject.toml)
grpcio==1.60.0
# via
# grpc-interceptor
# grpcio-reflection
# grpcio-status
# grpcio-tools
# tavern (pyproject.toml)
grpcio-reflection==1.60.0
# via tavern (pyproject.toml)
grpcio-status==1.60.0
# via tavern (pyproject.toml)
grpcio-tools==1.60.0
# via tavern (pyproject.toml)
httplib2==0.22.0
# via
# google-api-python-client
# google-auth-httplib2
identify==2.5.33
# via pre-commit
idna==3.6
Expand Down Expand Up @@ -110,7 +146,7 @@ jinja2==3.1.3
# sphinx
jmespath==1.0.1
# via tavern (pyproject.toml)
jsonschema==4.21.0
jsonschema==4.21.1
# via tavern (pyproject.toml)
jsonschema-specifications==2023.12.1
# via jsonschema
Expand All @@ -120,7 +156,7 @@ markdown==3.5.2
# via sphinx-markdown-tables
markdown-it-py==3.0.0
# via rich
markupsafe==2.1.3
markupsafe==2.1.4
# via
# jinja2
# werkzeug
Expand Down Expand Up @@ -166,8 +202,25 @@ pluggy==1.3.0
# tox
pre-commit==3.6.0
# via tavern (pyproject.toml)
proto-plus==1.23.0
# via tavern (pyproject.toml)
protobuf==4.25.2
# via
# google-api-core
# googleapis-common-protos
# grpcio-reflection
# grpcio-status
# grpcio-tools
# proto-plus
# tavern (pyproject.toml)
py==1.11.0
# via tavern (pyproject.toml)
pyasn1==0.5.1
# via
# pyasn1-modules
# rsa
pyasn1-modules==0.3.0
# via google-auth
pycparser==2.21
# via cffi
pygments==2.17.2
Expand All @@ -180,6 +233,8 @@ pyjwt==2.8.0
# via tavern (pyproject.toml)
pykwalify==1.8.0
# via tavern (pyproject.toml)
pyparsing==3.1.1
# via httplib2
pyproject-api==1.6.1
# via tox
pyproject-hooks==1.0.0
Expand Down Expand Up @@ -215,6 +270,7 @@ referencing==0.32.1
requests==2.31.0
# via
# flit
# google-api-core
# requests-toolbelt
# sphinx
# tavern (pyproject.toml)
Expand All @@ -229,11 +285,13 @@ rpds-py==0.17.1
# via
# jsonschema
# referencing
rsa==4.9
# via google-auth
ruamel-yaml==0.18.5
# via pykwalify
ruamel-yaml-clib==0.2.8
# via ruamel-yaml
ruff==0.1.13
ruff==0.1.14
# via tavern (pyproject.toml)
secretstorage==3.3.3
# via keyring
Expand Down Expand Up @@ -281,6 +339,8 @@ types-setuptools==69.0.0.20240115
# via tavern (pyproject.toml)
typing-extensions==4.9.0
# via mypy
uritemplate==4.1.1
# via google-api-python-client
urllib3==2.1.0
# via
# requests
Expand Down
1 change: 0 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
# needs_sphinx = '1.0'

import sphinx_rtd_theme
import recommonmark
from recommonmark.transform import AutoStructify

# Add any Sphinx extension module names here, as strings. They can be
Expand Down
168 changes: 168 additions & 0 deletions docs/source/grpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# gRPC integration testing

## Current limitations / future plans

- Should be able to specify channel credentials.
- Currently there is no way of doing custom TLS options (like with rest/mqtt)
- Better syntax around importing modules
- Some way of representing streaming RPCs? This is pretty niche and Tavern is built around a core of only making 1
request which doesn't work well with streaming request RPCs, but streaming response RPCs could be handled like
multiple MQTT responses.
- Much like the tavern-flask plugin it wouldn't be too difficult to write a plugin which started a Python gRPC server
in-process and ran tests against that instead of having to use a remote server
- Fix comparing results - currently it serialises with

including_default_value_fields=True,
preserving_proto_field_name=True,

Which formats a field like `my_field_name` as `my_field_name` and not `myFieldName` which is what protojson in Go
converts it to for example, need to provide a way to allow people to write tests using either one
- protos are compiled into a folder based on `tempfile.gettempdir()`, this could be configurable

## Connection

There are 2 ways of specifying the grpc connection, in the `grpc` block at the top of the test similarly to an mqtt
connection block, or in the test stage itself.

In the `grpc.connect` block:

```yaml
grpc:
connect:
host: localhost
port: 50052
```

In the test stage itself:

```yaml
stages:
- name: Do a thing
grpc_request:
host: "localhost: 50052"
service: my.cool.service/Waoh
body:
...
```

The connection will be established at the beginning of the test and dropped when it finishes.

### SSL connection

Tavern currently _defaults to an insecure connection_ when connecting to grpc, to enable SSL connections add
the `secure` key in the `connect` block:

```yaml
grpc:
connect:
secure: true
```

### Metadata

Generic metadata can be passed on every message using the `metadata` key:

```yaml
grpc:
metadata:
my-extra-info: something
```

### Advanced: connection options

Generic connection options can be passed as key:value pairs under the `options` block:

```yaml
grpc:
connect:
options:
grpc.max_send_message_length: 10000000
```

See [the gRPC documentation](https://grpc.github.io/grpc/core/group__grpc__arg__keys.html) for a list of possible
options, note that some of these may not be implemented in Python.

## Requests

The `grpc_request` block requires, at minimum, the name of the service to send the request to

```yaml
stages:
- name: Say hello
grpc_request:
service: helloworld.v3.Greeter/SayHello
body:
name: "John"
```

The 'body' block will be reflected into the protobuf message type expected for the service, if the schema is invalid
then an exception will be raised.

## Responses

If no response is specified, Tavern will assume that _any_ response with an `OK` status code to be successful.

Other status codes are specified using the `status` key. The gRPC status code should be a string matching
a [gRPC status code](https://grpc.github.io/grpc/core/md_doc_statuscodes.html), for
example `OK`, `NOT_FOUND`, etc. or the numerical value of the code. It can also be a list of codes.

```yaml
stages:
- name: Echo text
grpc_request:
service: helloworld.v1.Greeter/SayHello
body:
name: "John"
grpc_response:
status: "OK" # Also the default
```

## Loading protobuf definitions

There are 3 different ways Tavern will try to load the appropriate proto definitions:

#### Specifying the proto module to use

If you already have all the Python gRPC stubs in your repository. Example:

```yaml
grpc:
proto:
module: server/helloworld_pb2_grpc
```

This will attempt to import the given module (it should not be a Python file, but the path to the module containing the
existing stubs) and register all the protos in it.

#### Specifying a folder with some protos in

Example:

```yaml
grpc:
proto:
source: path/to/protos
```

This will attempt to find all files ending in `.proto` in the given folder and compile them using
the protoc compiler. It first checks the value of the environment variable `PROTOC` and use that,
and if not defined it will then look for a binary called `protoc` in the path. proto files are
compiled into a folder called `proto` under the same folder that the Tavern yaml is in.

This has a few drawbacks, especially that if it can't find the protoc compiler at runtime it will
fail, but it might be useful if you're talking to a Java/Go/other server and you don't want to keep
some compiled Python gRPC stubs in your repository.

#### Server reflection

This is obviously the least useful method. If you don't specify a proto source or module, the client
can attempt to
use [gRPC reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) to
determine what is the appropriate message type for the message you're trying to send. This is not
reliable as the server you're trying to talk to might not have reflection turned on. This needs to be specified in
the `grpc` block:

```yaml
grpc:
attempt_reflection: true
```
1 change: 1 addition & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Tavern is still in active development and is used by 100s of companies.
* [Basic Concepts](basics.md)
* [HTTP Integration testing](http.md)
* [MQTT Integration testing](mqtt.md)
* [gRPC Integration testing](grpc.md)
* [Plugins](plugins.md)
* [Debugging Tests](debugging.md)
* [Examples](examples.md)
Expand Down
Loading