Skip to content

Commit

Permalink
test refactoring, new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
KissPeter committed Feb 11, 2020
1 parent a98179c commit ae68729
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ build:
idle_timeout: 10
test:
commands:
- command: 'cd test && pytest --durations=10 --show-capture=stdout -v -rP test.py'
- command: 'pytest --durations=10 --show-capture=stdout -v -rP test'
on_node: 1
idle_timeout: 10
filter:
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ install:
script:
- python fuzzer.py -h
- pip3 install -r test/requirements_for_test.txt
- cd test && python3 test_application.py&
- cd test && pytest --durations=10 --show-capture=stdout -vv -rP test.py
- python3 test/test_application.py&
- pytest --durations=10 --show-capture=stdout -vv -rP test
148 changes: 65 additions & 83 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,9 @@
#!/usr/bin/env python3 -X utf8
import json
import os
import tempfile

import pytest
import requests
from test.test_utils import BaseTest

from fuzzer import Fuzzer


class TestClass(object):

@classmethod
def setup_class(cls):
"""
Setup test class at initialization
"""
cls.report_dir = tempfile.mkdtemp()
cls.report_files = list()
cls.test_app_url = "http://127.0.0.1:5000/"
print('Setup_class with report dir: {}'.format(cls.report_dir))
with open('./test_swagger_definition.json', 'r') as f:
cls.swagger = json.loads(f.read())

def teardown_method(self, method):
"""
Clears the report directory at the end of each test run
:param method: test method
"""
print('Removing {} report files...'.format(len(self.report_files)))
for f in self.report_files:
filepath = '{}/{}'.format(self.report_dir, f)
os.remove(filepath)

def query_last_call(self):
"""
Queries the test application and gets the details of the last call which sent by the fuzzer
:return: dict
"""
_resp = requests.get('{}{}'.format(self.test_app_url, 'last_call'), timeout=1)
assert _resp.status_code == 200, 'Response headers: {}, response body: {}'.format(_resp.headers, _resp.content)
return json.loads(_resp.content.decode("utf-8"))

def fuzz(self, api_resources):
"""
Call APIFuzzer with the given api definition
:type api_resources: dict
"""
with pytest.raises(SystemExit):
prog = Fuzzer(api_resources=api_resources,
report_dir=self.report_dir,
test_level=1,
alternate_url=self.test_app_url,
test_result_dst=None,
log_level='Debug',
basic_output=False,
auth_headers={}
)
prog.prepare()
prog.run()

def get_last_report_file(self):
self.report_files = os.listdir(self.report_dir)
with open("{}/{}".format(self.report_dir, self.report_files[0]), mode='r', encoding='utf-8') as f:
return json.loads(f.read())

def fuzz_and_get_last_call(self, api_path, api_def):
self.swagger.pop('paths')
self.swagger['paths'] = {}
self.swagger['paths'][api_path] = api_def
self.fuzz(self.swagger)
last_call = self.query_last_call()
assert last_call['resp_status'] == 500, '{} received, full response: {}'.format(last_call['resp_status'],
last_call)
print('api_path: {}, api_def: {} \nlast_call: {}'.format(api_path, api_def, last_call))
return last_call

def repot_basic_check(self):
required_report_fields = ['status', 'sub_reports', 'name', 'request_headers', 'state', 'request_method',
'reason', 'request_url', 'response', 'test_number']
last_report = self.get_last_report_file()
assert_msg = json.dumps(last_report, sort_keys=True, indent=2)
for field in required_report_fields:
assert field in last_report.keys(), assert_msg
if last_report.get('parsed_status_code') is not None:
assert last_report['parsed_status_code'] == 500, assert_msg
class TestSwagger(BaseTest):

def test_single_path_parameter(self):
test_url = '/path_param'
Expand Down Expand Up @@ -132,3 +51,66 @@ def test_single_query_string(self):
_, last_value_sent = last_call['req_url'].split("=")
assert not isinstance(last_value_sent, int), last_call['req_url']
self.repot_basic_check()

def test_multiple_query_strings(self):
api_path = '/query_multiple_params'
api_def = {
"get": {
"parameters": [
{
"name": "int_query_param1",
"in": "query",
"required": True,
"type": "number",
"format": "double"
},
{
"name": "int_query_param2",
"in": "query",
"required": True,
"type": "number",
"format": "double"
}
]
}
}
last_call = self.fuzz_and_get_last_call(api_path, api_def)
# last_call['req_url']
# http://127.0.0.1:5000/query_multiple_params?int_query_param1=667.5&int_query_param2=%10
_, query_string = last_call['req_url'].split("?", maxsplit=1)
_, val_1, _, val_2 = query_string.replace('&', '=').split('=')
assert float(val_1), 'last_qery_url: {}, value1: {}'.format(last_call['req_url'], val_1)
assert not isinstance(val_2, int), 'last_qery_url: {}, value2: {}'.format(last_call['req_url'], val_2)
self.repot_basic_check()

def test_multiple_path_params(self):
test_url = '/multiple_path_params'
api_path = "/".join([test_url, '{int_path_param1}', '{int_path_param2}'])
api_def = {
"get": {
"parameters": [
{
"name": "int_path_param1",
"in": "path",
"required": True,
"type": "number",
"format": "double"
},
{
"name": "int_path_param2",
"in": "path",
"required": True,
"type": "number",
"format": "double"
}
]
}
}
last_call = self.fuzz_and_get_last_call(api_path, api_def)
# last_call['req_path']
# 'req_path': '/multiple_path_params/667.5/\x10'
_, test_url_reported, param1, param2 = last_call['req_path'].split("/", maxsplit=3)
assert test_url[1:] == test_url_reported, last_call['req_path'] # heading / shall be removed
assert float(param1), 'req_path: {}, param1: {}'.format(last_call['req_url'], param1)
assert not isinstance(param2, int), 'req_path: {}, param2: {}'.format(last_call['req_url'], param2)
self.repot_basic_check()
16 changes: 14 additions & 2 deletions test/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,28 @@ def extract(d):

@app.route('/path_param/<integer_id>', methods=['GET'])
@catch_custom_exception
def transform(integer_id):
def path_param(integer_id):
return 'ID: {}'.format(int(integer_id))


@app.route('/multiple_path_params/<int_path_param1>/<int_path_param2>', methods=['GET'])
@catch_custom_exception
def transform(int_path_param1, int_path_param2):
return 'int_path_param1: {}, int_path_param2: {}'.format(int(int_path_param1), int(int_path_param2))


@app.route('/query')
@catch_custom_exception
def data():
def query():
return 'ID: {}'.format(int(request.args.get('integer_id')))


@app.route('/query_multiple_params')
@catch_custom_exception
def query_multiple_params():
return 'ID: {}'.format(int(request.args.get('int_query_param2')))


@app.route('/last_call', methods=['GET'])
def last_call():
_return = last_request_data.get_data()
Expand Down
101 changes: 87 additions & 14 deletions test/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,93 @@
import json
import os
import tempfile

import psutil
import pytest
import requests

from fuzzer import Fuzzer

def get_test_server_pid(call=None):
_return = list()
for proc in psutil.net_connections('tcp4'):
if proc.laddr.port == 5000 and proc.pid:
_return.append(proc.pid)
print('{} - found process: {}'.format(call, proc.pid))
if len(_return) < 1:
print('{} - no process found which listens on port 5000'.format(call))
return _return

class BaseTest:

def stop_test_server():
for pid in get_test_server_pid("Stop"):
if pid:
os.kill(pid, 9)
@classmethod
def setup_class(cls):
"""
Setup test class at initialization
"""
cls.report_dir = tempfile.mkdtemp()
cls.report_files = list()
cls.test_app_url = "http://127.0.0.1:5000/"
print('Setup_class with report dir: {}'.format(cls.report_dir))
if os.path.exists('test/test_swagger_definition.json'):
src_file = 'test/test_swagger_definition.json'
elif os.path.exists('./test_swagger_definition.json'):
src_file = './test_swagger_definition.json'
else:
print('Failed to find test file')
src_file = None
with open(src_file, 'r') as f:
cls.swagger = json.loads(f.read())

def teardown_method(self, method):
"""
Clears the report directory at the end of each test run
:param method: test method
"""
print('Removing {} report files...'.format(len(self.report_files)))
for f in self.report_files:
filepath = '{}/{}'.format(self.report_dir, f)
os.remove(filepath)

def query_last_call(self):
"""
Queries the test application and gets the details of the last call which sent by the fuzzer
:return: dict
"""
_resp = requests.get('{}{}'.format(self.test_app_url, 'last_call'), timeout=1)
assert _resp.status_code == 200, 'Response headers: {}, response body: {}'.format(_resp.headers, _resp.content)
return json.loads(_resp.content.decode("utf-8"))

def fuzz(self, api_resources):
"""
Call APIFuzzer with the given api definition
:type api_resources: dict
"""
with pytest.raises(SystemExit):
prog = Fuzzer(api_resources=api_resources,
report_dir=self.report_dir,
test_level=1,
alternate_url=self.test_app_url,
test_result_dst=None,
log_level='Debug',
basic_output=False,
auth_headers={}
)
prog.prepare()
prog.run()

def get_last_report_file(self):
self.report_files = os.listdir(self.report_dir)
with open("{}/{}".format(self.report_dir, self.report_files[0]), mode='r', encoding='utf-8') as f:
return json.loads(f.read())

def fuzz_and_get_last_call(self, api_path, api_def):
self.swagger.pop('paths')
self.swagger['paths'] = {}
self.swagger['paths'][api_path] = api_def
self.fuzz(self.swagger)
last_call = self.query_last_call()
assert last_call['resp_status'] == 500, '{} received, full response: {}'.format(last_call['resp_status'],
last_call)
print('api_path: {}, api_def: {} \nlast_call: {}'.format(api_path, api_def, last_call))
return last_call

def repot_basic_check(self):
required_report_fields = ['status', 'sub_reports', 'name', 'request_headers', 'state', 'request_method',
'reason', 'request_url', 'response', 'test_number']
last_report = self.get_last_report_file()
assert_msg = json.dumps(last_report, sort_keys=True, indent=2)
for field in required_report_fields:
assert field in last_report.keys(), assert_msg
if last_report.get('parsed_status_code') is not None:
assert last_report['parsed_status_code'] == 500, assert_msg

0 comments on commit ae68729

Please sign in to comment.