Skip to content

Commit 36791b2

Browse files
authored
Merge pull request #704 from MetaCell/feature/700
Fix and improve api tests
2 parents c35f8c3 + 042189b commit 36791b2

5 files changed

Lines changed: 109 additions & 27 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import os
2+
from pprint import pprint
3+
import schemathesis as st
4+
from schemathesis.checks import response_schema_conformance, not_a_server_error
5+
6+
from cloudharness_test import apitest_init # include to perform default authorization
7+
8+
app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")
9+
10+
try:
11+
schema = st.from_uri(app_url + "/openapi.json")
12+
except:
13+
# support alternative schema location
14+
schema = st.from_uri(app_url.replace("/api", "") + "/openapi.json")
15+
16+
17+
@schema.parametrize(endpoint="/ping")
18+
def test_ping(case):
19+
response = case.call()
20+
pprint(response.__dict__)
21+
assert response.status_code == 200, "this api errors on purpose"
22+
23+
def test_state_machine():
24+
schema.as_state_machine().run()
25+
# APIWorkflow = schema.as_state_machine()
26+
# APIWorkflow.run()
27+
# TestAPI = APIWorkflow.TestCase

deployment-configuration/value-template.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ harness:
2222
- administrator
2323
- uri: /api/openapi.json
2424
white-listed: true
25+
- uri: /openapi.json
26+
white-listed: true
2527
# -- Defines reference deployment parameters. Values maps to k8s spec
2628
deployment:
2729
# -- When true, enables automatic deployment

docs/testing.md

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,23 +108,30 @@ The test can use environmental variables:
108108
Examples:
109109
- [Sample api test](../applications/samples/test/api/test_st.py)
110110

111-
### Common smoke tests
112-
113-
Once a test is created for your application, generic smoke tests are also
114-
executed, checking for:
115111

116-
- Main page is reachable
117-
- No errors in the console
118-
- No error responses from network resources and fetch requests (code < 400)
119-
120-
The smoke tests is defined [in this file](../test/test-e2e/__tests__/common.spec.ts).
121112

122113

123114

124115
## End to end (E2E) tests
125116

126117
End to end tests run in a headless browser ([Puppeteer](https://github.com/puppeteer/puppeteer)) against the full deployment on Kubernetes.
127118

119+
Custom configuration:
120+
121+
```yaml
122+
harness:
123+
...
124+
test:
125+
e2e:
126+
# -- enable/disable e2e tests
127+
enabled: true
128+
# -- ignore errors on console by default
129+
ignoreConsoleErrors: false
130+
# -- ignore fetched resources errors by default
131+
ignoreRequestErrors: false
132+
# -- enable common smoke tests
133+
smoketest: true
134+
```
128135

129136
### Write tests with Jest and Puppeteer
130137

@@ -159,7 +166,7 @@ executed, checking for:
159166
- No errors in the console
160167
- No error responses from network resources and fetch requests (code < 400)
161168

162-
The smoke tests is defined [in this file](../test/jest-puppeteer/__tests__/common.spec.ts).
169+
The smoke tests are defined [in this file](../test/jest-puppeteer/__tests__/common.spec.ts).
163170

164171

165172
## Run API and E2E tests in the CI/CD pipeline
@@ -182,7 +189,7 @@ deployment.
182189
In order to use `harness-test` install the library with
183190

184191
```
185-
pip install -r requirements-test.txt
192+
pip install -e tools/cloudharness-test
186193
```
187194
188195
In order to run tests against an existing deployment based on a domain (say, my.domain), run:
@@ -198,6 +205,25 @@ If you want to run the deployment locally and then run the tests, can use skaffo
198205
1. Wait the deployment to settle
199206
1. Run `harness-test PATHS`
200207
208+
### Tests development
209+
The `harness-test` client is useful while developing and tweaking the tests.
210+
In that case it's better to target the application under development and
211+
the kind of tests we are working on.
212+
213+
214+
To target a specific application for end-to-end tests, use:
215+
```
216+
harness-test . -i [APPNAME] -e
217+
```
218+
219+
To target a specific application for api tests, use:
220+
```
221+
harness-test . -i [APPNAME] -a
222+
```
223+
224+
Note that the local version of the openapi.yaml file located at applications/[APPNAME]/api/openapi.yaml is used if available. That's useful to tweak examples and responses
225+
used by schemathesis to generate the test hypotheses.
226+
201227
## Create test users for your application
202228
203229
To create test users:

tools/cloudharness-test/cloudharness_test/api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ def run_api_tests(root_paths, helm_values: HarnessMainConfig, base_domain, inclu
6060

6161
app_env = get_app_environment(app_config, app_domain)
6262

63+
schema_file = f"applications/{app_config.name}/api/openapi.yaml"
64+
65+
for path in root_paths:
66+
# use local schema if available to simplify test development
67+
if os.path.exists(os.path.join(path, schema_file)):
68+
app_env["APP_SCHEMA_FILE"] = schema_file
69+
6370
if api_config.autotest:
6471
logging.info("Running auto api tests")
6572
cmd = get_schemathesis_command(api_filename, app_config, app_domain)
Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,64 @@
11
import os
22
import logging
3+
import requests
34

45
import schemathesis as st
56

67
from cloudharness.auth import get_token
78

8-
if "APP_URL" in os.environ:
9+
if "APP_URL" or "APP_SCHEMA_FILE" in os.environ:
10+
app_schema = os.environ.get("APP_SCHEMA_FILE", None)
11+
app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")
12+
logging.info("Start schemathesis tests on %s", app_url)
13+
if app_schema:
14+
# Test locally with harness-test -- use local schema for convenience during test development
15+
openapi_uri = app_schema
16+
schema = st.from_file(openapi_uri)
17+
else:
18+
# remote testing: might be /api/openapi.json or /openapi.json
19+
try:
20+
openapi_uri = openapi_uri = app_url + "/openapi.json"
21+
schema = st.from_uri(openapi_uri)
22+
except st.exceptions.SchemaLoadingError as e:
23+
# Use alternative configuration
24+
try:
25+
openapi_uri = app_url.replace("/api", "") + "/openapi.json"
26+
print(requests.get(openapi_uri))
27+
schema = st.from_uri(openapi_uri)
28+
except st.exceptions.SchemaLoadingError as e:
29+
raise Exception(
30+
f"Cannot setup api tests: {openapi_uri} not valid. Check your deployment is up and configuration") from e
931

10-
app_url = os.environ["APP_URL"]
32+
except Exception as e:
33+
raise Exception(
34+
f"Cannot setup api tests: {openapi_uri}: {e}") from e
1135

12-
try:
13-
openapi_uri = app_url + "/openapi.json"
14-
logging.info("Using openapi spec at %s", openapi_uri)
15-
schema = st.from_uri(openapi_uri)
16-
except Exception as e:
17-
raise Exception(f"Cannot setup api tests: {openapi_uri} not reachable. Check your deployment is up and configuration") from e
36+
logging.info("Using openapi spec at %s", openapi_uri)
1837

1938
if "USERNAME" in os.environ and "PASSWORD" in os.environ:
2039
logging.info("Setting token from username and password")
40+
2141
@st.auth.register()
2242
class TokenAuth:
2343
def get(self, context):
24-
44+
2545
username = os.environ["USERNAME"]
26-
password = os.environ["PASSWORD"]
27-
46+
password = os.environ["PASSWORD"]
47+
2848
return get_token(username, password)
2949

3050
def set(self, case, data, context):
3151
case.headers = case.headers or {}
32-
case.headers["Authorization"] = f"Bearer {data}"
33-
case.headers["Cookie"] = f"kc-access={data}"
52+
case.headers["Authorization"] = f"Bearer {data}"
53+
case.headers["Cookie"] = f"kc-access={data}"
3454
else:
3555
@st.auth.register()
3656
class TokenAuth:
3757
def get(self, context):
38-
58+
3959
return ""
4060

4161
def set(self, case, data, context):
4262
case.headers = case.headers or {}
43-
case.headers["Authorization"] = f"Bearer {data}"
44-
case.headers["Cookie"] = f"kc-access={data}"
63+
case.headers["Authorization"] = f"Bearer {data}"
64+
case.headers["Cookie"] = f"kc-access={data}"

0 commit comments

Comments
 (0)