Enable fuzzing the last request without re-creating the whole sequence. (#470)
Added new engine option as follows: ``` "cache_prefix_sequence_settings": { "per_request_settings": [ { "methods": [ "GET", "HEAD" ], "endpoints": "*", "reset_after_success": false } ] } ``` Testing: - demo_server manual testing - added unit test - manual testing with large services to confirm all the resources are cleaned up Closes #469
This commit is contained in:
Родитель
6afa397dab
Коммит
fec027c320
|
@ -262,7 +262,7 @@ When set, polls for async resource creation before continuing
|
|||
Set to True to ignore socked data decoding failures
|
||||
See: https://github.com/microsoft/restler-fuzzer/issues/164
|
||||
|
||||
## Per resource settings:
|
||||
## Per resource settings
|
||||
Certain settings can be applied to specific endpoints.
|
||||
These settings a defined in a per_resource_settings dict.
|
||||
For example:
|
||||
|
@ -278,6 +278,79 @@ The above snippet will set the producer timing delay to 5 and
|
|||
activate create-once for the specified endpoint.
|
||||
Per resource settings override any global settings.
|
||||
|
||||
## Sequence exploration settings
|
||||
The following settings can be applied to control how RESTler
|
||||
executes request sequences.
|
||||
|
||||
__create_prefix_once__
|
||||
For requests that have dependencies on pre-requisite resources,
|
||||
RESTler executes the entire sequence of requests required to fuzz
|
||||
the current request every time it fuzzes the request,
|
||||
except for if the request type has a ```GET``` or ```HEAD``` method.
|
||||
This default maximizes reproducibility. ```GET``` and ```HEAD``` methods
|
||||
are assumed to have no side effects on the resources created by the API,
|
||||
so pre-requisite resources are not re-created when fuzzing them.
|
||||
|
||||
All requests prior to the current request being fuzzed (the _sequence prefix_) can either be re-executed every time, or saved for testing all combinations of the current request. This can be controlled on a per-method or per-endpoint basis, as follows.
|
||||
|
||||
1. Execute the entire sequence, except for
|
||||
requests with `GET` methods.
|
||||
|
||||
```json
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": ["GET"],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
2. Always execute the entire sequence for every combination.
|
||||
Providing an empty list overrides the default behavior as described earlier.
|
||||
|
||||
```json
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. Do not re-execute the entire sequence for a specific resource.
|
||||
```json
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": ["GET", "PUT", "PATCH"],
|
||||
"endpoints": ["/customer/{customerId}"],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
4. Do not re-execute the entire sequence in all cases, except when a successful request deletes or modifies the pre-requisites set up by the previous requests.
|
||||
```json
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": ["GET", "HEAD"],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": false
|
||||
},
|
||||
{
|
||||
"methods": ["PUT", "POST", "PATCH", "DELETE"],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### producer_timing_delay: int (default global_producer_timing_delay)
|
||||
The per resource producer timing delay, in seconds.
|
||||
This is a dict type object where the key is the request's endpoint
|
||||
|
@ -312,7 +385,7 @@ instead of the default dictionary.
|
|||
"per_resource_settings": {
|
||||
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/dnsZones/{zoneName}/{recordType}/{relativeRecordSetName}": {
|
||||
"producer_timing_delay": 1,
|
||||
"create_once": 1
|
||||
"create_once": 1,
|
||||
"custom_dictionary": "c:\\restler\\custom_dict1.json"
|
||||
},
|
||||
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/dnsZones/{zoneName}" {
|
||||
|
|
|
@ -282,6 +282,8 @@ def render_one(seq_to_render, ith, checkers, generation, global_lock):
|
|||
print("Unsupported fuzzing_mode:", Settings().fuzzing_mode)
|
||||
assert False
|
||||
|
||||
# Release any saved dynamic objects
|
||||
dependencies.clear_saved_local_dyn_objects()
|
||||
return valid_renderings
|
||||
|
||||
def render_parallel(seq_collection, fuzzing_pool, checkers, generation, global_lock):
|
||||
|
@ -701,6 +703,7 @@ def generate_sequences(fuzzing_requests, checkers, fuzzing_jobs=1):
|
|||
logger.write_to_main("Timed out...")
|
||||
timeout_reached = True
|
||||
seq_collection_exhausted = True
|
||||
|
||||
# Increase fuzzing generation after timeout because the code
|
||||
# that does it would have never been reached. This is done so
|
||||
# the previous generation's test summary is logged correctly.
|
||||
|
|
|
@ -9,6 +9,7 @@ import copy
|
|||
import time
|
||||
import json
|
||||
import datetime
|
||||
from enum import Enum
|
||||
|
||||
import engine.core.async_request_utilities as async_request_utilities
|
||||
import engine.core.request_utilities as request_utilities
|
||||
|
@ -63,6 +64,11 @@ class RenderedSequence(object):
|
|||
self.final_request_response = final_request_response
|
||||
self.final_response_datetime = response_datetime
|
||||
|
||||
class RenderedPrefixStatus(Enum):
|
||||
NONE = 1
|
||||
VALID = 2
|
||||
INVALID = 3
|
||||
|
||||
class Sequence(object):
|
||||
""" Implements basic sequence logic. """
|
||||
def __init__(self, requests=None):
|
||||
|
@ -84,13 +90,25 @@ class Sequence(object):
|
|||
# A list of all requests in this sequence that were sent;
|
||||
# as the exact data that was rendered and set to the server
|
||||
self._sent_request_data_list = []
|
||||
# Indicates whether the prefix of this sequence has been rendered.
|
||||
# If so, the dynamic objects created in the prefix have been saved, and
|
||||
# must be freed when the sequence has finished rendering.
|
||||
self.rendered_prefix_status = RenderedPrefixStatus.NONE
|
||||
|
||||
# Indicates that this sequence should only render its prefix once.
|
||||
self.create_prefix_once = False
|
||||
# If a cached sequence prefix is present, indicates that this sequence
|
||||
# should re-render it after a valid sequence rendering.
|
||||
self.re_render_prefix_on_success = None
|
||||
|
||||
self.executed_requests_count = 0
|
||||
|
||||
def __iter__(self):
|
||||
""" Iterate over Sequences objects. """
|
||||
return iter(self.requests)
|
||||
|
||||
def __add__(self, other):
|
||||
""" Add two sequnces
|
||||
""" Add two sequences
|
||||
|
||||
@return: None
|
||||
@rtype : None
|
||||
|
@ -289,7 +307,7 @@ class Sequence(object):
|
|||
|
||||
def render(self, candidate_values_pool, lock, preprocessing=False, postprocessing=False):
|
||||
""" Core routine that performs the rendering of restler sequences. In
|
||||
principal all requests of a sequence are being constantly rendered with
|
||||
principle, all requests of a sequence are being constantly rendered with
|
||||
a specific values combination @param request._current_combination_id
|
||||
which we know in the past led to a valid rendering and only the last
|
||||
request of the sequence is being rendered iteratively with all feasible
|
||||
|
@ -315,53 +333,34 @@ class Sequence(object):
|
|||
@rtype : RenderedSequence
|
||||
"""
|
||||
# Try rendering all primitive type value combinations for last request
|
||||
request = self.last_request
|
||||
|
||||
# for clarity reasons, don't log requests whose render iterator is over
|
||||
if request._current_combination_id <\
|
||||
request.num_combinations(candidate_values_pool):
|
||||
CUSTOM_LOGGING(self, candidate_values_pool)
|
||||
def render_prefix():
|
||||
"""
|
||||
Renders the last known valid combination of the prefix of this sequence.
|
||||
Depending on user settings, this may either be re-rendered for every
|
||||
sequence or only once prior to rendering all of the request combinations
|
||||
for the last request.
|
||||
|
||||
self._sent_request_data_list = []
|
||||
"""
|
||||
if self.create_prefix_once and self.rendered_prefix_status == RenderedPrefixStatus.VALID:
|
||||
return None, None
|
||||
|
||||
datetime_format = "%Y-%m-%d %H:%M:%S"
|
||||
response_datetime_str = None
|
||||
timestamp_micro = None
|
||||
for rendered_data, parser, tracked_parameters in\
|
||||
request.render_iter(candidate_values_pool,
|
||||
skip=request._current_combination_id,
|
||||
preprocessing=preprocessing):
|
||||
# Hold the lock (because other workers may be rendering the same
|
||||
# request) and check whether the current rendering is known from the
|
||||
# past to lead to invalid status codes. If so, skip the current
|
||||
# rendering.
|
||||
if lock is not None:
|
||||
lock.acquire()
|
||||
should_skip = Monitor().is_invalid_rendering(request)
|
||||
if lock is not None:
|
||||
lock.release()
|
||||
last_req = self.requests[-1]
|
||||
|
||||
# Skip the loop and don't forget to increase the counter.
|
||||
if should_skip:
|
||||
RAW_LOGGING("Skipping rendering: {}".\
|
||||
format(request._current_combination_id))
|
||||
request._current_combination_id += 1
|
||||
continue
|
||||
self.create_prefix_once, self.re_render_prefix_on_success = Settings().get_cached_prefix_request_settings(last_req.endpoint, last_req.method)
|
||||
|
||||
# Clean up internal state
|
||||
self.status_codes = []
|
||||
dependencies.reset_tlb()
|
||||
dependencies.clear_saved_local_dyn_objects()
|
||||
dependencies.start_saving_local_dyn_objects()
|
||||
|
||||
sequence_failed = False
|
||||
request._tracked_parameters = {}
|
||||
request.update_tracked_parameters(tracked_parameters)
|
||||
|
||||
# Step A: Static template rendering
|
||||
# Render last known valid combination of primitive type values
|
||||
# for every request until the last
|
||||
current_request = None
|
||||
prev_request = None
|
||||
prev_response = None
|
||||
response_datetime_str = None
|
||||
for i in range(len(self.requests) - 1):
|
||||
last_tested_request_idx = i
|
||||
prev_request = self.requests[i]
|
||||
|
@ -447,10 +446,7 @@ class Sequence(object):
|
|||
prev_response.has_valid_code(),
|
||||
False))
|
||||
|
||||
|
||||
# Render candidate value combinations seeking for valid error codes
|
||||
request._current_combination_id += 1
|
||||
|
||||
self.rendered_prefix_status = RenderedPrefixStatus.INVALID if sequence_failed else RenderedPrefixStatus.VALID
|
||||
if sequence_failed:
|
||||
self.status_codes.append(
|
||||
status_codes_monitor.RequestExecutionStatus(
|
||||
|
@ -463,6 +459,63 @@ class Sequence(object):
|
|||
)
|
||||
Monitor().update_status_codes_monitor(self, self.status_codes, lock)
|
||||
|
||||
self.executed_requests_count = len(self.requests) - 1
|
||||
return prev_response, response_datetime_str
|
||||
|
||||
request = self.last_request
|
||||
|
||||
# for clarity reasons, don't log requests whose render iterator is over
|
||||
if request._current_combination_id <\
|
||||
request.num_combinations(candidate_values_pool):
|
||||
CUSTOM_LOGGING(self, candidate_values_pool)
|
||||
|
||||
self._sent_request_data_list = []
|
||||
|
||||
datetime_format = "%Y-%m-%d %H:%M:%S"
|
||||
response_datetime_str = None
|
||||
timestamp_micro = None
|
||||
for rendered_data, parser, tracked_parameters in\
|
||||
request.render_iter(candidate_values_pool,
|
||||
skip=request._current_combination_id,
|
||||
preprocessing=preprocessing):
|
||||
# Hold the lock (because other workers may be rendering the same
|
||||
# request) and check whether the current rendering is known from the
|
||||
# past to lead to invalid status codes. If so, skip the current
|
||||
# rendering.
|
||||
if lock is not None:
|
||||
lock.acquire()
|
||||
should_skip = Monitor().is_invalid_rendering(request)
|
||||
if lock is not None:
|
||||
lock.release()
|
||||
|
||||
# Skip the loop and don't forget to increase the counter.
|
||||
if should_skip:
|
||||
RAW_LOGGING("Skipping rendering: {}".\
|
||||
format(request._current_combination_id))
|
||||
request._current_combination_id += 1
|
||||
continue
|
||||
|
||||
request._tracked_parameters = {}
|
||||
request.update_tracked_parameters(tracked_parameters)
|
||||
|
||||
# Step A: Static template rendering
|
||||
# Render last known valid combination of primitive type values
|
||||
# for every request until the last
|
||||
try:
|
||||
self.executed_requests_count = 0
|
||||
prev_response, response_datetime_str = render_prefix()
|
||||
finally:
|
||||
dependencies.stop_saving_local_dyn_objects()
|
||||
|
||||
# Render candidate value combinations seeking for valid error codes
|
||||
request._current_combination_id += 1
|
||||
|
||||
if self.rendered_prefix_status == RenderedPrefixStatus.INVALID:
|
||||
# A failure to re-render a previously successful sequence prefix may be a
|
||||
# transient issue. Reset the prefix state so it is re-rendered again
|
||||
# for the next combination.
|
||||
self.rendered_prefix_status = RenderedPrefixStatus.NONE
|
||||
|
||||
if lock is not None:
|
||||
lock.acquire()
|
||||
# Deep copying here will try copying anything the class has access
|
||||
|
@ -479,7 +532,7 @@ class Sequence(object):
|
|||
response_datetime=response_datetime_str)
|
||||
|
||||
# Step B: Dynamic template rendering
|
||||
# substitute reference placeholders with ressoved values
|
||||
# substitute reference placeholders with resolved values
|
||||
# for the last request
|
||||
if not Settings().ignore_dependencies:
|
||||
rendered_data = self.resolve_dependencies(rendered_data)
|
||||
|
@ -498,6 +551,7 @@ class Sequence(object):
|
|||
return RenderedSequence(failure_info=FailureInformation.MISSING_STATUS_CODE)
|
||||
|
||||
self.append_data_to_sent_list(rendered_data, parser, response, max_async_wait_time=req_async_wait)
|
||||
self.executed_requests_count = self.executed_requests_count + 1
|
||||
|
||||
rendering_is_valid = not parser_exception_occurred\
|
||||
and not resource_error\
|
||||
|
@ -538,11 +592,18 @@ class Sequence(object):
|
|||
# to including the shared client monitor, which we update in the
|
||||
# above code block holding the lock, but then we release the
|
||||
# lock and one thread can be updating while another is copying.
|
||||
# This is a typlical nasty read after write syncronization bug.
|
||||
# This is a typical nasty read after write syncronization bug.
|
||||
duplicate = copy.deepcopy(self)
|
||||
if lock is not None:
|
||||
lock.release()
|
||||
|
||||
# Free the dynamic objects from a saved prefix sequence if the request succeeded and
|
||||
# the prefix should be re-rendered for the next combination.
|
||||
if self.rendered_prefix_status is not None:
|
||||
if rendering_is_valid and self.re_render_prefix_on_success == True:
|
||||
self.rendered_prefix_status = None
|
||||
dependencies.stop_saving_local_dyn_objects(reset=True)
|
||||
|
||||
# return a rendered clone if response indicates a valid status code
|
||||
if rendering_is_valid or Settings().ignore_feedback:
|
||||
return RenderedSequence(duplicate, valid=True, final_request_response=response,
|
||||
|
@ -560,6 +621,10 @@ class Sequence(object):
|
|||
final_request_response=response,
|
||||
response_datetime=response_datetime_str)
|
||||
|
||||
# Since all of the renderings have been tested, clear the rendered prefix status
|
||||
# and release local dynamic objects, since they are no longer needed.
|
||||
self.rendered_prefix_status = RenderedPrefixStatus.NONE
|
||||
dependencies.clear_saved_local_dyn_objects()
|
||||
return RenderedSequence(None)
|
||||
|
||||
def append_data_to_sent_list(self, rendered_data, parser, response, producer_timing_delay=0, max_async_wait_time=0):
|
||||
|
|
|
@ -179,7 +179,7 @@ class StatusCodesMonitor(object):
|
|||
lock.acquire()
|
||||
|
||||
seq_length = sequence.length
|
||||
self._requests_count['main_driver'] += seq_length
|
||||
self._requests_count['main_driver'] += sequence.executed_requests_count
|
||||
seq_definition = sequence.definition
|
||||
seq_hash = sequence.hex_definition
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@ threadLocal = threading.local()
|
|||
# Keep TLS tlb to enforce mutual exclusion when using >1 fuzzing jobs.
|
||||
threadLocal.tlb = {}
|
||||
tlb = threadLocal.tlb
|
||||
# The 'local_dyn_objects_cache' tracks dynamic objects that are created while
|
||||
# rendering the sequence prefix and must not be deleted until the prefix is no longer in use.
|
||||
threadLocal.local_dyn_objects_cache = {}
|
||||
local_dyn_objects_cache = threadLocal.local_dyn_objects_cache
|
||||
gc_paused = False
|
||||
|
||||
# Keep a global registry for garbage collection (deletion) of dynamic objects.
|
||||
dyn_objects_cache = {}
|
||||
|
@ -109,6 +114,15 @@ def get_variable(type):
|
|||
|
||||
return encoded_value
|
||||
|
||||
def __add_variable_to_dyn_cache(type, value, obj_cache, cache_lock):
|
||||
# Keep track of all dynamic objects ever created.
|
||||
if cache_lock is not None:
|
||||
cache_lock.acquire()
|
||||
if type not in obj_cache:
|
||||
obj_cache[type] = []
|
||||
obj_cache[type].append(value)
|
||||
if cache_lock is not None:
|
||||
cache_lock.release()
|
||||
|
||||
def set_variable(type, value):
|
||||
""" Setter for dynamic variable (a.k.a. dependency).
|
||||
|
@ -128,15 +142,10 @@ def set_variable(type, value):
|
|||
tlb[type] = value
|
||||
# thread_id = threading.current_thread().ident
|
||||
# print("Setting: {} / Value: {} ({})".format(type, value, thread_id))
|
||||
|
||||
# Keep track of all dynamic objects ever created.
|
||||
if dyn_objects_cache_lock is not None:
|
||||
dyn_objects_cache_lock.acquire()
|
||||
if type not in dyn_objects_cache:
|
||||
dyn_objects_cache[type] = []
|
||||
dyn_objects_cache[type].append(value)
|
||||
if dyn_objects_cache_lock is not None:
|
||||
dyn_objects_cache_lock.release()
|
||||
if gc_paused:
|
||||
__add_variable_to_dyn_cache(type, value, local_dyn_objects_cache, None)
|
||||
else:
|
||||
__add_variable_to_dyn_cache(type, value, dyn_objects_cache, dyn_objects_cache_lock)
|
||||
|
||||
def set_variable_no_gc(type, value):
|
||||
""" Setter for dynamic variable that should never be garbage collected.
|
||||
|
@ -162,6 +171,50 @@ def reset_tlb():
|
|||
for k in tlb:
|
||||
tlb[k] = None
|
||||
|
||||
def clear_saved_local_dyn_objects():
|
||||
""" Moves all of the saved objects from the saved objects cache to the regular tlb, so
|
||||
they can be deleted.
|
||||
"""
|
||||
if gc_paused:
|
||||
raise Exception("Error: cannot clear saved dynamic objects while they are being saved.")
|
||||
|
||||
if dyn_objects_cache_lock is not None:
|
||||
dyn_objects_cache_lock.acquire()
|
||||
for type in local_dyn_objects_cache:
|
||||
if type not in dyn_objects_cache:
|
||||
dyn_objects_cache[type] = []
|
||||
dyn_objects_cache[type] = dyn_objects_cache[type] + local_dyn_objects_cache[type]
|
||||
if dyn_objects_cache_lock is not None:
|
||||
dyn_objects_cache_lock.release()
|
||||
|
||||
local_dyn_objects_cache.clear()
|
||||
|
||||
|
||||
def start_saving_local_dyn_objects():
|
||||
""" Instead of adding saved dynamic objects to the regular tlb, add it to the saved tlb.
|
||||
This will save them until explicitly released.
|
||||
"""
|
||||
global gc_paused
|
||||
if gc_paused:
|
||||
raise Exception("Error: the GC is already paused.")
|
||||
|
||||
gc_paused = True
|
||||
|
||||
def stop_saving_local_dyn_objects(reset=False):
|
||||
""" Set the state so that any future objects that are created are
|
||||
added to the regular TLB and will be garbage collected automatically.
|
||||
|
||||
@param reset: If 'True', also release the saved objects.
|
||||
@type reset: Bool
|
||||
|
||||
@return: None
|
||||
@rtype : None
|
||||
"""
|
||||
global gc_paused
|
||||
gc_paused = False
|
||||
if reset:
|
||||
clear_saved_local_dyn_objects()
|
||||
|
||||
def set_saved_dynamic_objects():
|
||||
""" Saves the current dynamic objects cache to a separate
|
||||
cache and then clears the dynamic objects cache. This
|
||||
|
|
|
@ -305,6 +305,18 @@ MAX_SEQUENCE_LENGTH_DEFAULT = 100
|
|||
TARGET_PORT_MAX = (1<<16)-1
|
||||
TIME_BUDGET_DEFAULT = 24.0*30 # ~1 month
|
||||
|
||||
SEQ_RENDERING_SETTINGS_DEFAULT = {
|
||||
# While fuzzing HEAD and GET request combinations, only render the prefix once.
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": ["GET", "HEAD"],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": False
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEST_SERVER_ID = 'unit_test'
|
||||
DEFAULT_VERSION = '0.0.0'
|
||||
|
||||
|
@ -413,6 +425,8 @@ class RestlerSettings(object):
|
|||
self._max_combinations = SettingsArg('max_combinations', int, MAX_COMBINATIONS_DEFAULT, user_args, minval=0)
|
||||
## Settings for advanced combinations testing, such as testing multiple schema combinations
|
||||
self._combinations_args = SettingsArg('test_combinations_settings', dict, {}, user_args)
|
||||
## Settings for caching the sequence prefixes when rendering request combinations
|
||||
self._seq_rendering_settings = SettingsArg('sequence_exploration_settings', dict, {}, user_args)
|
||||
## Maximum time to wait for a response after sending a request (seconds)
|
||||
self._max_request_execution_time = SettingsArg('max_request_execution_time', (int, float), MAX_REQUEST_EXECUTION_TIME_DEFAULT, user_args, minval=0, min_exactok=False, maxval=MAX_REQUEST_EXECUTION_TIME_MAX)
|
||||
## Maximum length of any sequence
|
||||
|
@ -614,7 +628,6 @@ class RestlerSettings(object):
|
|||
return self._retry_args.val['interval_sec']
|
||||
return None
|
||||
|
||||
|
||||
@property
|
||||
def ignore_decoding_failures(self):
|
||||
return self._ignore_decoding_failures.val
|
||||
|
@ -655,6 +668,31 @@ class RestlerSettings(object):
|
|||
def wait_for_async_resource_creation(self):
|
||||
return self._wait_for_async_resource_creation.val
|
||||
|
||||
def get_cached_prefix_request_settings(self, endpoint, method):
|
||||
def get_settings():
|
||||
if 'create_prefix_once' in self._seq_rendering_settings.val:
|
||||
return self._seq_rendering_settings.val['create_prefix_once']
|
||||
return SEQ_RENDERING_SETTINGS_DEFAULT['create_prefix_once']
|
||||
|
||||
prefix_cache_settings = get_settings()
|
||||
|
||||
# Find the settings matching the request endpoint, then check whether they include the request method.
|
||||
endpoint_settings = list(filter(lambda x : 'endpoints' in x and \
|
||||
(x['endpoints'] == "*" or endpoint in x['endpoints']),
|
||||
prefix_cache_settings))
|
||||
req_settings = list(filter(lambda x : 'methods' in x and \
|
||||
(x['methods'] == "*" or method.upper() in x['methods']),
|
||||
endpoint_settings))
|
||||
|
||||
create_prefix_once = False
|
||||
re_render_prefix_on_success = None
|
||||
if len(req_settings) > 0:
|
||||
create_prefix_once = True
|
||||
re_render_prefix_on_success = False
|
||||
if 'reset_after_success' in req_settings[0]:
|
||||
re_render_prefix_on_success = req_settings[0]['reset_after_success']
|
||||
return create_prefix_once, re_render_prefix_on_success
|
||||
|
||||
def _set_per_resource_args(self, args: dict):
|
||||
""" Sets the per-resource settings
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": [ "GET", "HEAD" ],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": false
|
||||
},
|
||||
{
|
||||
"methods": [ "PUT" ],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,560 @@
|
|||
2022-02-15 00:06:07.651: Will refresh token: python D:\git\restler-fuzzer\restler\unit_tests\log_baseline_test_files\unit_test_server_auth.py
|
||||
2022-02-15 00:06:07.715: New value: {'user1':{}, 'user2':{}}
|
||||
Authorization: valid_unit_test_token
|
||||
Authorization: shadow_unit_test_token
|
||||
|
||||
Generation-1: Rendering Sequence-1
|
||||
|
||||
Request: 1 (Remaining candidate combinations: 1)
|
||||
Request hash: 23db74a21a5b43c1601d160252a5cd1b7ae89805
|
||||
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
2022-02-15 00:06:07.732: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.733: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
|
||||
Generation-1: Rendering Sequence-2
|
||||
|
||||
Request: 1 (Remaining candidate combinations: 1)
|
||||
Request hash: 44414ad093616e18a9e2f845ae9d453eb6e4c8bc
|
||||
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
2022-02-15 00:06:07.752: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.753: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
|
||||
Generation-3: Rendering Sequence-1
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Remaining candidate combinations: 2)
|
||||
Request hash: 2d217a8041860f0742efe1b22a09893cd0e89ca5
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/C'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:07.789: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.791: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
2022-02-15 00:06:07.793: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.795: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.797: Sending: 'GET /C HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header1\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 20\r\n\r\n{"A": "A", "B": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.798: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"C": {}}'
|
||||
|
||||
|
||||
Generation-3: Rendering Sequence-1
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Remaining candidate combinations: 1)
|
||||
Request hash: 2d217a8041860f0742efe1b22a09893cd0e89ca5
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/C'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:07.818: Sending: 'GET /C HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header2\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 20\r\n\r\n{"A": "A", "B": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.820: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"C": {}}'
|
||||
|
||||
|
||||
Generation-3: Rendering Sequence-2
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Remaining candidate combinations: 2)
|
||||
Request hash: a3b2722ed766df82f0d1d1aafd2e81f23d4a0aa2
|
||||
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
2022-02-15 00:06:07.847: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.849: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
2022-02-15 00:06:07.851: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.854: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.857: Sending: 'PUT /D/D HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header1\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 22\r\n\r\n{"A": "A", "B": "B"}\r\n'
|
||||
|
||||
2022-02-15 00:06:07.858: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "D"}'
|
||||
|
||||
|
||||
Generation-3: Rendering Sequence-2
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Remaining candidate combinations: 1)
|
||||
Request hash: a3b2722ed766df82f0d1d1aafd2e81f23d4a0aa2
|
||||
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
2022-02-15 00:06:07.878: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.881: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
2022-02-15 00:06:07.883: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.885: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.889: Sending: 'PUT /D/D HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header2\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 22\r\n\r\n{"A": "A", "B": "B"}\r\n'
|
||||
|
||||
2022-02-15 00:06:07.890: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "D"}'
|
||||
|
||||
|
||||
Generation-4: Rendering Sequence-1
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Current combination: 1 / 2)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 4 (Remaining candidate combinations: 2)
|
||||
Request hash: fb149c026c182b9719b5b5b37ef0ef3f0950c55a
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/E'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"D": "'
|
||||
- restler_static_string: '_READER_DELIM_post_d_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:07.924: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.926: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
2022-02-15 00:06:07.928: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.929: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
2022-02-15 00:06:07.931: Sending: 'PUT /D/D HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header1\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 22\r\n\r\n{"A": "A", "B": "B"}\r\n'
|
||||
|
||||
2022-02-15 00:06:07.933: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "D"}'
|
||||
|
||||
2022-02-15 00:06:07.934: Sending: 'GET /E HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header1\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 10\r\n\r\n{"D": "D"}'
|
||||
|
||||
2022-02-15 00:06:07.936: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"E": {}}'
|
||||
|
||||
|
||||
Generation-4: Rendering Sequence-1
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Current combination: 1 / 2)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 4 (Remaining candidate combinations: 1)
|
||||
Request hash: fb149c026c182b9719b5b5b37ef0ef3f0950c55a
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/E'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"D": "'
|
||||
- restler_static_string: '_READER_DELIM_post_d_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:07.960: Sending: 'GET /E HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header2\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 10\r\n\r\n{"D": "D"}'
|
||||
|
||||
2022-02-15 00:06:07.963: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"E": {}}'
|
||||
|
||||
|
||||
Generation-4: Rendering Sequence-2
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Current combination: 2 / 2)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 4 (Remaining candidate combinations: 2)
|
||||
Request hash: fb149c026c182b9719b5b5b37ef0ef3f0950c55a
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/E'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"D": "'
|
||||
- restler_static_string: '_READER_DELIM_post_d_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:07.993: Sending: 'PUT /A/A HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.994: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "A"}'
|
||||
|
||||
2022-02-15 00:06:07.996: Sending: 'PUT /B/B HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 0\r\n\r\n'
|
||||
|
||||
2022-02-15 00:06:07.998: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "B"}'
|
||||
|
||||
2022-02-15 00:06:08.003: Sending: 'PUT /D/D HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header2\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 22\r\n\r\n{"A": "A", "B": "B"}\r\n'
|
||||
|
||||
2022-02-15 00:06:08.004: Received: 'HTTP/1.1 201 Created\r\nRestler Test\r\n\r\n{"name": "D"}'
|
||||
|
||||
2022-02-15 00:06:08.006: Sending: 'GET /E HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header1\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 10\r\n\r\n{"D": "D"}'
|
||||
|
||||
2022-02-15 00:06:08.008: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"E": {}}'
|
||||
|
||||
|
||||
Generation-4: Rendering Sequence-2
|
||||
|
||||
Request: 1 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/A/A'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 2 (Current combination: 1 / 1)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/B/B'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 3 (Current combination: 2 / 2)
|
||||
- restler_static_string: 'PUT '
|
||||
- restler_static_string: '/D/D'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"A": "'
|
||||
- restler_static_string: '_READER_DELIM_post_a_READER_DELIM'
|
||||
- restler_static_string: '", "B": "'
|
||||
- restler_static_string: '_READER_DELIM_post_b_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
- restler_static_string: '\r\n'
|
||||
|
||||
Request: 4 (Remaining candidate combinations: 1)
|
||||
Request hash: fb149c026c182b9719b5b5b37ef0ef3f0950c55a
|
||||
|
||||
- restler_static_string: 'GET '
|
||||
- restler_static_string: '/E'
|
||||
- restler_static_string: ' HTTP/1.1\r\n'
|
||||
- restler_static_string: 'Accept: application/json\r\n'
|
||||
- restler_static_string: 'Host: unittest\r\n'
|
||||
- restler_static_string: 'Content-Type: application/json\r\n'
|
||||
- restler_static_string: 'Extra-Header: '
|
||||
+ restler_fuzzable_group: [Header1, Header2, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
+ restler_refreshable_authentication_token: [token_refresh_cmd, token_refresh_interval, ...]
|
||||
- restler_static_string: '\r\n'
|
||||
- restler_static_string: '{'
|
||||
- restler_static_string: '"D": "'
|
||||
- restler_static_string: '_READER_DELIM_post_d_READER_DELIM'
|
||||
- restler_static_string: '"'
|
||||
- restler_static_string: '}'
|
||||
|
||||
2022-02-15 00:06:08.029: Sending: 'GET /E HTTP/1.1\r\nAccept: application/json\r\nHost: unittest\r\nContent-Type: application/json\r\nExtra-Header: Header2\r\nAuthorization: valid_unit_test_token\r\nContent-Length: 10\r\n\r\n{"D": "D"}'
|
||||
|
||||
2022-02-15 00:06:08.031: Received: 'HTTP/1.1 200 OK\r\nRestler Test\r\n\r\n{"E": {}}'
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# This grammar was created manually.
|
||||
# There is no corresponding OpenAPI spec.
|
||||
# This grammar is identical to abc_test_grammar.py, except
|
||||
# additional fuzzable groups are introduced so each request type
|
||||
# has multiple combinations.
|
||||
from __future__ import print_function
|
||||
import json
|
||||
|
||||
from engine import primitives
|
||||
from engine.core import requests
|
||||
from engine.errors import ResponseParsingException
|
||||
from engine import dependencies
|
||||
|
||||
_post_a = dependencies.DynamicVariable(
|
||||
"_post_a"
|
||||
)
|
||||
|
||||
_post_b = dependencies.DynamicVariable(
|
||||
"_post_b"
|
||||
)
|
||||
|
||||
_post_d = dependencies.DynamicVariable(
|
||||
"_post_d"
|
||||
)
|
||||
|
||||
def parse_A(data):
|
||||
temp_123 = None
|
||||
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except Exception as error:
|
||||
raise ResponseParsingException("Exception parsing response, data was not valid json: {}".format(error))
|
||||
|
||||
try:
|
||||
temp_123 = str(data["name"])
|
||||
except Exception as error:
|
||||
pass
|
||||
|
||||
if temp_123:
|
||||
dependencies.set_variable("_post_a", temp_123)
|
||||
|
||||
def parse_B(data):
|
||||
temp_123 = None
|
||||
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except Exception as error:
|
||||
raise ResponseParsingException("Exception parsing response, data was not valid json: {}".format(error))
|
||||
|
||||
try:
|
||||
temp_123 = str(data["name"])
|
||||
except Exception as error:
|
||||
pass
|
||||
|
||||
if temp_123:
|
||||
dependencies.set_variable("_post_b", temp_123)
|
||||
|
||||
|
||||
def parse_D(data):
|
||||
temp_123 = None
|
||||
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except Exception as error:
|
||||
raise ResponseParsingException("Exception parsing response, data was not valid json: {}".format(error))
|
||||
|
||||
try:
|
||||
temp_123 = str(data["name"])
|
||||
except Exception as error:
|
||||
pass
|
||||
|
||||
if temp_123:
|
||||
dependencies.set_variable("_post_d", temp_123)
|
||||
|
||||
|
||||
req_collection = requests.RequestCollection([])
|
||||
|
||||
request = requests.Request([
|
||||
primitives.restler_static_string("PUT "),
|
||||
primitives.restler_static_string("/A/A"),
|
||||
primitives.restler_static_string(" HTTP/1.1\r\n"),
|
||||
primitives.restler_static_string("Accept: application/json\r\n"),
|
||||
primitives.restler_static_string("Host: restler.unit.test.server.com\r\n"),
|
||||
primitives.restler_static_string("Content-Type: application/json\r\n"),
|
||||
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
{
|
||||
'post_send':
|
||||
{
|
||||
'parser': parse_A,
|
||||
'dependencies':
|
||||
[
|
||||
_post_a.writer()
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
requestId="/A/{A}"
|
||||
)
|
||||
req_collection.add_request(request)
|
||||
|
||||
request = requests.Request([
|
||||
primitives.restler_static_string("PUT "),
|
||||
primitives.restler_static_string("/B/B"),
|
||||
primitives.restler_static_string(" HTTP/1.1\r\n"),
|
||||
primitives.restler_static_string("Accept: application/json\r\n"),
|
||||
primitives.restler_static_string("Host: restler.unit.test.server.com\r\n"),
|
||||
primitives.restler_static_string("Content-Type: application/json\r\n"),
|
||||
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
{
|
||||
'post_send':
|
||||
{
|
||||
'parser': parse_B,
|
||||
'dependencies':
|
||||
[
|
||||
_post_b.writer()
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
requestId="/B/{B}"
|
||||
)
|
||||
req_collection.add_request(request)
|
||||
|
||||
|
||||
request = requests.Request([
|
||||
primitives.restler_static_string("GET "),
|
||||
primitives.restler_static_string("/C"),
|
||||
primitives.restler_static_string(" HTTP/1.1\r\n"),
|
||||
primitives.restler_static_string("Accept: application/json\r\n"),
|
||||
primitives.restler_static_string("Host: restler.unit.test.server.com\r\n"),
|
||||
primitives.restler_static_string("Content-Type: application/json\r\n"),
|
||||
primitives.restler_static_string("Extra-Header: "),
|
||||
primitives.restler_fuzzable_group("Extra-Header", ["Header1", "Header2"]),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_static_string("{"),
|
||||
primitives.restler_static_string('"A": "'),
|
||||
primitives.restler_static_string(_post_a.reader()),
|
||||
primitives.restler_static_string('", "B": "'),
|
||||
primitives.restler_static_string(_post_b.reader()),
|
||||
primitives.restler_static_string('"'),
|
||||
primitives.restler_static_string("}"),
|
||||
],
|
||||
requestId="/C"
|
||||
)
|
||||
req_collection.add_request(request)
|
||||
|
||||
|
||||
request = requests.Request([
|
||||
primitives.restler_static_string("PUT "),
|
||||
primitives.restler_static_string("/D/D"),
|
||||
primitives.restler_static_string(" HTTP/1.1\r\n"),
|
||||
primitives.restler_static_string("Accept: application/json\r\n"),
|
||||
primitives.restler_static_string("Host: restler.unit.test.server.com\r\n"),
|
||||
primitives.restler_static_string("Content-Type: application/json\r\n"),
|
||||
primitives.restler_static_string("Extra-Header: "),
|
||||
primitives.restler_fuzzable_group("Extra-Header", ["Header1", "Header2"]),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_static_string("{"),
|
||||
primitives.restler_static_string('"A": "'),
|
||||
primitives.restler_static_string(_post_a.reader()),
|
||||
primitives.restler_static_string('", "B": "'),
|
||||
primitives.restler_static_string(_post_b.reader()),
|
||||
primitives.restler_static_string('"'),
|
||||
primitives.restler_static_string("}"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
{
|
||||
'post_send':
|
||||
{
|
||||
'parser': parse_D,
|
||||
'dependencies':
|
||||
[
|
||||
_post_d.writer()
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
],
|
||||
requestId="/D/{D}"
|
||||
)
|
||||
req_collection.add_request(request)
|
||||
|
||||
|
||||
request = requests.Request([
|
||||
primitives.restler_static_string("GET "),
|
||||
primitives.restler_static_string("/E"),
|
||||
primitives.restler_static_string(" HTTP/1.1\r\n"),
|
||||
primitives.restler_static_string("Accept: application/json\r\n"),
|
||||
primitives.restler_static_string("Host: restler.unit.test.server.com\r\n"),
|
||||
primitives.restler_static_string("Content-Type: application/json\r\n"),
|
||||
primitives.restler_static_string("Extra-Header: "),
|
||||
primitives.restler_fuzzable_group("Extra-Header", ["Header1", "Header2"]),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_refreshable_authentication_token("authentication_token_tag"),
|
||||
primitives.restler_static_string("\r\n"),
|
||||
primitives.restler_static_string("{"),
|
||||
primitives.restler_static_string('"D": "'),
|
||||
primitives.restler_static_string(_post_d.reader()),
|
||||
primitives.restler_static_string('"'),
|
||||
primitives.restler_static_string("}"),
|
||||
],
|
||||
requestId="/E"
|
||||
)
|
||||
req_collection.add_request(request)
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
]
|
||||
}
|
||||
}
|
|
@ -60,6 +60,15 @@
|
|||
"custom_dictionary": "c:\\restler\\custom_dict2.json"
|
||||
}
|
||||
},
|
||||
"sequence_exploration_settings": {
|
||||
"create_prefix_once": [
|
||||
{
|
||||
"methods": [ "GET", "HEAD" ],
|
||||
"endpoints": "*",
|
||||
"reset_after_success": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"checkers": {
|
||||
"namespacerule": {
|
||||
"mode": "exhaustive"
|
||||
|
|
|
@ -70,14 +70,19 @@ class FunctionalityTests(unittest.TestCase):
|
|||
except subprocess.CalledProcessError:
|
||||
self.fail(f"Restler returned non-zero exit code: {result.returncode} {result.stdout}")
|
||||
|
||||
def run_abc_smoke_test(self, test_file_dir, grammar_file_name, fuzzing_mode):
|
||||
def run_abc_smoke_test(self, test_file_dir, grammar_file_name, fuzzing_mode, settings_file=None, dictionary_file_name=None):
|
||||
grammar_file_path = os.path.join(test_file_dir, grammar_file_name)
|
||||
dict_file_path = os.path.join(test_file_dir, "abc_dict.json")
|
||||
if dictionary_file_name is None:
|
||||
dictionary_file_name = "abc_dict.json"
|
||||
dict_file_path = os.path.join(test_file_dir, dictionary_file_name)
|
||||
args = Common_Settings + [
|
||||
'--fuzzing_mode', f"{fuzzing_mode}",
|
||||
'--restler_grammar', f'{grammar_file_path}',
|
||||
'--custom_mutations', f'{dict_file_path}'
|
||||
]
|
||||
if settings_file:
|
||||
settings_file_path = os.path.join(test_file_dir, settings_file)
|
||||
args = args + ['--settings', f'{settings_file_path}']
|
||||
self.run_restler_engine(args)
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -145,6 +150,66 @@ class FunctionalityTests(unittest.TestCase):
|
|||
except TestFailedException:
|
||||
self.fail("Smoke test failed: Fuzzing")
|
||||
|
||||
|
||||
def test_abc_minimal_smoke_test_prefix_cache(self):
|
||||
""" This checks that the directed smoke test executes the expected
|
||||
sequences in Test mode with test-all-combinations and when caching prefix sequences for several
|
||||
request types via the engine settings.
|
||||
|
||||
Let 5 requests A, B, C, D, E where:
|
||||
- A and B have no pre-requisites
|
||||
- C and D both depend on A and B (they are identical)
|
||||
- E depends on D
|
||||
|
||||
In the current implementation, all sequences for A, B, C, D will be
|
||||
rendered "from scratch", but the sequence for E will reuse the 'D' prefix (as confirmed by a separate test above).
|
||||
|
||||
This test introduces 2 combinations for C, D, and E, and confirms that for the second combination,
|
||||
no requests are re-rendered for C and D (GET requests), but the combination is re-rendered for the PUT
|
||||
(as specified in the settings file).
|
||||
|
||||
|
||||
"""
|
||||
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_combinations.py", "test-all-combinations")
|
||||
experiments_dir = self.get_experiments_dir()
|
||||
|
||||
## Make sure all requests were successfully rendered. This is because the comparisons below do not
|
||||
## take status codes into account
|
||||
|
||||
## Make sure the right number of requests was sent.
|
||||
testing_summary_file_path = os.path.join(experiments_dir, "logs", "testing_summary.json")
|
||||
|
||||
try:
|
||||
with open(testing_summary_file_path, 'r') as file:
|
||||
testing_summary = json.loads(file.read())
|
||||
total_requests_sent = testing_summary["total_requests_sent"]["main_driver"]
|
||||
num_fully_valid = testing_summary["num_fully_valid"]
|
||||
self.assertEqual(num_fully_valid, 5)
|
||||
self.assertLessEqual(total_requests_sent, 22)
|
||||
|
||||
default_parser = FuzzingLogParser(os.path.join(Test_File_Directory, "abc_smoke_test_testing_log_all_combinations.txt"))
|
||||
test_parser = FuzzingLogParser(self.get_network_log_path(experiments_dir, logger.LOG_TYPE_TESTING))
|
||||
self.assertTrue(default_parser.diff_log(test_parser))
|
||||
except TestFailedException:
|
||||
self.fail("Smoke test failed: Fuzzing")
|
||||
|
||||
# Now run the same test with the additional settings file.
|
||||
self.run_abc_smoke_test(Test_File_Directory, "abc_test_grammar_combinations.py", "test-all-combinations",
|
||||
settings_file="abc_smoke_test_settings_prefix_cache.json")
|
||||
experiments_dir = self.get_experiments_dir()
|
||||
testing_summary_file_path = os.path.join(experiments_dir, "logs", "testing_summary.json")
|
||||
try:
|
||||
with open(testing_summary_file_path, 'r') as file:
|
||||
testing_summary = json.loads(file.read())
|
||||
total_requests_sent = testing_summary["total_requests_sent"]["main_driver"]
|
||||
num_fully_valid = testing_summary["num_fully_valid"]
|
||||
self.assertEqual(num_fully_valid, 5)
|
||||
self.assertLessEqual(total_requests_sent, 20)
|
||||
|
||||
except TestFailedException:
|
||||
self.fail("Smoke test failed: Fuzzing")
|
||||
|
||||
|
||||
def test_ab_all_combinations_with_sequence_failure(self):
|
||||
""" This checks that sequence failures are correctly reported in the
|
||||
spec coverage file for a minimal grammar.
|
||||
|
@ -161,7 +226,7 @@ class FunctionalityTests(unittest.TestCase):
|
|||
|
||||
The test checks that the sequence failure sample requests are correct.
|
||||
"""
|
||||
self.run_abc_smoke_test(Test_File_Directory, "ab_flaky_b_grammar.py", "test-all-combinations")
|
||||
self.run_abc_smoke_test(Test_File_Directory, "ab_flaky_b_grammar.py", "test-all-combinations", settings_file="always_render_full_seq_settings.json")
|
||||
experiments_dir = self.get_experiments_dir()
|
||||
|
||||
# Make sure all requests were successfully rendered. This is because the comparisons below do not
|
||||
|
|
Загрузка…
Ссылка в новой задаче