Skip to content

Commit b781e6a

Browse files
authored
Merge pull request #37 from cmeissner/record_and_replay_tests
provide pytests
2 parents 43d96eb + ce43834 commit b781e6a

34 files changed

+2470
-100
lines changed

.github/workflows/main.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
3+
on:
4+
- push
5+
- pull_request
6+
7+
jobs:
8+
tests:
9+
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
- name: Set up Python
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: '3.x'
18+
- name: Prepare test environment
19+
run: |
20+
python -m pip install --upgrade pip
21+
make test-setup
22+
- name: Run all tests
23+
run:
24+
make test-all

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
"python.pythonPath": "${workspaceFolder}/venv/bin/python",
33
"python.autoComplete.extraPaths": ["./tests/scripts"],
44
"python.envFile": "${workspaceFolder}/.env",
5-
"restructuredtext.confPath": "${workspaceFolder}/docs"
5+
"restructuredtext.confPath": "${workspaceFolder}/docs",
6+
"python.analysis.extraPaths": [
7+
"./tests/scripts"
8+
]
69
}

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ doc:
5656
sphinx-apidoc -M -f -o docs/plugins/ phpypam
5757
make -C docs html
5858

59-
test-setup:
59+
test-setup: | tests/vars/server.yml
6060
pip install --upgrade -r requirements-dev.txt
6161

62+
tests/vars/server.yml:
63+
cp $@.example $@
64+
@echo "Please configure $@ to point to your phpIPAM instance for recording."
65+
6266
test-all:
6367
coverage run -m pytest tests/test_cases/* -v
6468

requirements-dev.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ setuptools
99
PyYAML
1010
bumpversion
1111
pytest
12+
pytest-replay
1213
pytest-vcr
14+
pytest-xdist
1315
coverage

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""Provide some base configurations for tests."""
2+
import phpypam
3+
import pytest
4+
import py.path
5+
import yaml
6+
7+
from urllib.parse import urlparse, urlunparse
8+
9+
TEST_CASES_PATH = py.path.local(__file__).realpath() / '..' / 'test_cases'
10+
11+
with open('tests/vars/server.yml') as c:
12+
server = yaml.safe_load(c)
13+
14+
15+
@pytest.fixture(scope='module')
16+
def pi(*arg, **kwargs):
17+
"""Create a phpypam.api object and return it.
18+
19+
:return: object phpypam
20+
:rtype: phpypam.api
21+
"""
22+
url = kwargs.pop('url', server['url'])
23+
app_id = kwargs.pop('app_id', server['app_id'])
24+
username = kwargs.pop('username', server['username'])
25+
password = kwargs.pop('password', server['password'])
26+
ssl_verify = kwargs.pop('ssl_verify', True)
27+
28+
return phpypam.api(
29+
url=url,
30+
app_id=app_id,
31+
username=username,
32+
password=password,
33+
ssl_verify=ssl_verify,
34+
**kwargs
35+
)
36+
37+
38+
def find_all_test_cases():
39+
"""Generate list of test cases.
40+
41+
:yield: generates each test case as list item
42+
:rtype: str
43+
"""
44+
for c in TEST_CASES_PATH.listdir(sort=True):
45+
c = c.basename
46+
if c.endswith('.py'):
47+
yield c.replace('.py', '')
48+
49+
50+
TEST_CASES = list(find_all_test_cases())
51+
52+
53+
def pytest_addoption(parser):
54+
"""Change command line options defaults.
55+
56+
We want run our tests only in three modes
57+
`live` - interact with an existing API
58+
`record` - interact with an existing API and record the interactions
59+
`replay` - replay previouly recorded interactions with API
60+
61+
:param parser: A parser object
62+
:type parser: object parser
63+
"""
64+
parser.addoption(
65+
"--vcrmode",
66+
action="store",
67+
default="replay",
68+
choices=["replay", "record", "live"],
69+
help="mode for vcr recording; one of ['replay', 'record', 'live']",
70+
)
71+
72+
73+
@pytest.fixture
74+
def vcrmode(request):
75+
"""Return vcrmode of a request.
76+
77+
:param request: A request object
78+
:type request: object request
79+
:return: vcrmode
80+
:rtype: str
81+
"""
82+
return request.config.getoption("vcrmode")
83+
84+
85+
def cassette_name(test_name=None):
86+
"""Generate cassette_name."""
87+
return 'tests/fixtures/{0}.yml'.format(test_name)
88+
89+
90+
FILTER_REQUEST_HEADERS = ['Authorization', 'Cookie', 'Token']
91+
FILTER_RESPONSE_HEADERS = ['Apipie-Checksum', 'Date', 'ETag', 'Server', 'Set-Cookie', 'Via', 'X-Powered-By', 'X-Request-Id', 'X-Runtime']
92+
93+
94+
def filter_response(response):
95+
"""Filter headers before recording.
96+
97+
:param response: A response object where we want to filter the headers from.
98+
:type response: object response
99+
:return: response
100+
:rtype: object response
101+
"""
102+
for header in FILTER_RESPONSE_HEADERS:
103+
# headers should be case insensitive, but for some reason they weren't for me
104+
response['headers'].pop(header.lower(), None)
105+
response['headers'].pop(header, None)
106+
107+
return response
108+
109+
110+
def filter_request_uri(request):
111+
"""Filter uri before recording.
112+
113+
:param request: A request object where we want to filter the uri from.
114+
:type request: object request
115+
:return: request
116+
:rtype: object request
117+
"""
118+
request.uri = urlunparse(urlparse(request.uri)._replace(netloc="ipam.example.org"))
119+
return request
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '0'
13+
User-Agent:
14+
- python-requests/2.24.0
15+
method: POST
16+
uri: https://ipam.example.org/api/ansible/user
17+
response:
18+
body:
19+
string: !!binary |
20+
H4sIAAAAAAAAAw3NTQqDMBAG0KuUb53IZCpCZt/SXaF/CzciySxsUYuJVBDvXg/weCvCGBXCRAZp
21+
DkFTguRpVoPY5hayIo8fHSBIL+tPzfXyvoVnPZw12uV3rx4eBrp8u0l3CSYm65yl8sAkRy9cYjPI
22+
Xb8vVJDb/o6954ZzAAAA
23+
headers:
24+
Cache-Control:
25+
- no-cache
26+
Connection:
27+
- Keep-Alive
28+
Content-Encoding:
29+
- gzip
30+
Content-Type:
31+
- application/json; charset=utf-8
32+
Date:
33+
- Wed, 04 Nov 2020 13:39:24 GMT
34+
Expires:
35+
- Thu, 19 Nov 1981 08:52:00 GMT
36+
Keep-Alive:
37+
- timeout=5, max=100
38+
Pragma:
39+
- no-cache
40+
Server:
41+
- Apache/2.4.37 (centos) OpenSSL/1.1.1c
42+
Set-Cookie:
43+
- phpipam=rtcrr3al96v1m7oro2vnsc0lc4; expires=Thu, 05-Nov-2020 13:39:24 GMT;
44+
Max-Age=86400; path=/; HttpOnly
45+
Transfer-Encoding:
46+
- chunked
47+
Vary:
48+
- Accept-Encoding
49+
X-Powered-By:
50+
- PHP/7.2.24
51+
status:
52+
code: 200
53+
message: OK
54+
- request:
55+
body: null
56+
headers:
57+
Accept:
58+
- '*/*'
59+
Accept-Encoding:
60+
- gzip, deflate
61+
Connection:
62+
- keep-alive
63+
User-Agent:
64+
- python-requests/2.24.0
65+
method: GET
66+
uri: https://ipam.example.org/api/ansible/addresses/search/10.10.0.4
67+
response:
68+
body:
69+
string: !!binary |
70+
H4sIAAAAAAAAA6tWSs5PSVWyMjIw0FEqLk1OTi0uVrJKS8wpTtVRygVyEtOBskqOKSlFQI5CXn6J
71+
Qlp+aV6Kko5SSWYuUMpAz8DAtBYAByXdEUcAAAA=
72+
headers:
73+
Cache-Control:
74+
- no-cache
75+
Connection:
76+
- Keep-Alive
77+
Content-Encoding:
78+
- gzip
79+
Content-Type:
80+
- application/json; charset=utf-8
81+
Date:
82+
- Wed, 04 Nov 2020 13:39:24 GMT
83+
Expires:
84+
- Thu, 19 Nov 1981 08:52:00 GMT
85+
Keep-Alive:
86+
- timeout=5, max=100
87+
Pragma:
88+
- no-cache
89+
Server:
90+
- Apache/2.4.37 (centos) OpenSSL/1.1.1c
91+
Set-Cookie:
92+
- phpipam=vl2d6ijho4ddb26e964bhfd8gn; expires=Thu, 05-Nov-2020 13:39:24 GMT;
93+
Max-Age=86400; path=/; HttpOnly
94+
Transfer-Encoding:
95+
- chunked
96+
Vary:
97+
- Accept-Encoding
98+
X-Powered-By:
99+
- PHP/7.2.24
100+
status:
101+
code: 200
102+
message: OK
103+
version: 1
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '0'
13+
User-Agent:
14+
- python-requests/2.24.0
15+
method: POST
16+
uri: https://ipam.example.org/api/ansible/user
17+
response:
18+
body:
19+
string: !!binary |
20+
H4sIAAAAAAAAA6tWSs5PSVWyMjUw0FEqLk1OTi0uVrJKS8wpTtVRygVyEtOBskqeeWWJOZkpCqXF
21+
qUV5ibmpCvlFCgWJxcXl+UUpSjpKJZm5QFUGegYG5rUApmB0WFIAAAA=
22+
headers:
23+
Cache-Control:
24+
- no-cache
25+
Connection:
26+
- close
27+
Content-Encoding:
28+
- gzip
29+
Content-Type:
30+
- application/json; charset=utf-8
31+
Date:
32+
- Wed, 04 Nov 2020 13:39:17 GMT
33+
Expires:
34+
- Thu, 19 Nov 1981 08:52:00 GMT
35+
Pragma:
36+
- no-cache
37+
Server:
38+
- Apache/2.4.37 (centos) OpenSSL/1.1.1c
39+
Set-Cookie:
40+
- phpipam=ushkqmr1t9bhu636sohm3qjdmg; expires=Thu, 05-Nov-2020 13:39:17 GMT;
41+
Max-Age=86400; path=/; HttpOnly
42+
Transfer-Encoding:
43+
- chunked
44+
Vary:
45+
- Accept-Encoding
46+
X-Powered-By:
47+
- PHP/7.2.24
48+
status:
49+
code: 500
50+
message: Internal Server Error
51+
- request:
52+
body: null
53+
headers:
54+
Accept:
55+
- '*/*'
56+
Accept-Encoding:
57+
- gzip, deflate
58+
Connection:
59+
- keep-alive
60+
Content-Length:
61+
- '0'
62+
User-Agent:
63+
- python-requests/2.24.0
64+
method: POST
65+
uri: https://ipam.example.org/api/ansible/user
66+
response:
67+
body:
68+
string: !!binary |
69+
H4sIAAAAAAAAA6tWSs5PSVWyMjUw0FEqLk1OTi0uVrJKS8wpTtVRygVyEtOBskqeeWWJOZkpCqXF
70+
qUV5ibmpCvlFCgWJxcXl+UUpSjpKJZm5QFUGegYGprUAJAJCalIAAAA=
71+
headers:
72+
Cache-Control:
73+
- no-cache
74+
Connection:
75+
- close
76+
Content-Encoding:
77+
- gzip
78+
Content-Type:
79+
- application/json; charset=utf-8
80+
Date:
81+
- Wed, 04 Nov 2020 13:39:17 GMT
82+
Expires:
83+
- Thu, 19 Nov 1981 08:52:00 GMT
84+
Pragma:
85+
- no-cache
86+
Server:
87+
- Apache/2.4.37 (centos) OpenSSL/1.1.1c
88+
Set-Cookie:
89+
- phpipam=tpi619vqreehocgqhe8b0fe9fe; expires=Thu, 05-Nov-2020 13:39:17 GMT;
90+
Max-Age=86400; path=/; HttpOnly
91+
Transfer-Encoding:
92+
- chunked
93+
Vary:
94+
- Accept-Encoding
95+
X-Powered-By:
96+
- PHP/7.2.24
97+
status:
98+
code: 500
99+
message: Internal Server Error
100+
version: 1

0 commit comments

Comments
 (0)