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:
marina-p 2022-02-16 14:16:21 -08:00 коммит произвёл GitHub
Родитель 6afa397dab
Коммит fec027c320
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 1160 добавлений и 57 удалений

Просмотреть файл

@ -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