Skip to content

Commit

Permalink
Suuport for httponly/secure, min. Store and Validate support. Imp msgs.
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-verma committed May 23, 2021
1 parent ae03dc9 commit 14e9def
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 38 deletions.
23 changes: 12 additions & 11 deletions arjuna/interact/http/model/internal/processor/cookie/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,23 @@ class CookieValidator(AsserterMixIn):

def __init__(self, session, cookie_yaml):
super().__init__()
cookies = session.parsed_cookies
for cookie_name, cookie_val in cookie_yaml.items():
msg = f"Cookie with name {cookie_name} was not found in session cookies."
msg = f"Cookie with name >>{cookie_name}<< was not found in session cookies."
try:
self.asserter.assert_true(cookie_name in session.cookies, msg=msg)
except AssertionError:
self.asserter.fail(msg=msg)
attr_map = {
"secure": None,
"HttpOnly": None
}
cookie_val_to_match = None

cookie = cookies[cookie_name]

if type(cookie_val) is dict:
for k,v in cookie_val.items():
if k in raw_map:
raw_map[k] = v
if "value" in cookie_val:
cookie_val_to_match = cookie_val["value"]
if k == "value":
self.asserter.assert_equal(cookie.value, cookie_val["value"], f"Cookie value for >>{cookie_name}<< does not match expected value.")
elif k.lower() == "httponly":
self.asserter.assert_true(cookie.httponly, f"Cookie >>{cookie_name}<< does not have >>HttpOnly flag<< set.")
elif k.lower() == "secure":
self.asserter.assert_true(cookie.secure, f"Cookie >>{cookie_name}<< does not have >>secure flag<< set.")
else:
cookie_val_to_match = str(cookie_val)
self.asserter.assert_equal(cookie.value, str(cookie_val), f"Cookie value for >>{cookie_name}<< does not match expected value.")
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ def __init__(self, response):
super().__init__(response)

def _eval_pattern_exists(self, pattern, exists):
self.asserter.assert_true(exists, f"Expected pattern {pattern} was not found.")
self.asserter.assert_true(exists, f"Expected pattern >>{pattern}<< was not found.")

def _eval_pattern_match(self, pattern, actual, expected):
self.asserter.assert_equal(actual, expected, "Value for pattern {pattern} does not match.")
self.asserter.assert_equal(actual, expected, f"Value for pattern >>{pattern}<< does not match.")

class UnexpectedJsonValidator(_JsonValidator):

def __init__(self, response):
super().__init__(response)

def _eval_pattern_exists(self, pattern, exists):
self.asserter.assert_false(exists, f"Unexpected pattern {pattern} was found.")
self.asserter.assert_false(exists, f"Unexpected pattern >>{pattern}<< was found.")

def _eval_pattern_match(self, pattern, actual, expected):
self.asserter.assert_equal(actual, expected, "Value for pattern {pattern} does not match.")
self.asserter.assert_equal(actual, expected, f"Value for pattern >>{pattern}<< does not match.")

14 changes: 9 additions & 5 deletions arjuna/interact/http/model/internal/processor/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from .cookie.validator import CookieValidator
from .text.validator import ExpectedTextValidator, UnexpectedTextValidator
from .header.store import HeaderExtractor
from .text.store import TextExtractor
from .xml.store import XmlExtractor
from .validator import Validator
from arjuna.interact.http.model.internal.helper.yaml import convert_yaml_obj_to_content
from arjuna.tpi.engine.asserter import AsserterMixIn
Expand Down Expand Up @@ -82,9 +84,7 @@ def __init__(self, session, codes=None, url=None, headers={}, cookies={}, has={}

for k in self.__repr["validate"]:
if k not in self.__repr["store"]:
raise Exception(f"{k} in validate section is not defined in store section of message.")

print(self.__repr)
raise Exception(f"{k} in validate section is not defined in store section of message.")

@property
def _session(self):
Expand Down Expand Up @@ -154,6 +154,10 @@ def validate(self, response):
for name, sdict in self.store.items():
if sdict["header"]:
HeaderExtractor(response).store(name, sdict["header"])
elif sdict["regex"]:
TextExtractor(response).store(name, sdict["regex"])
elif sdict["xpath"]:
XmlExtractor(response).store(name, sdict["xpath"])
if self.validations:
validator = Validator(response)
for k, target in self.validations.items():
Expand Down Expand Up @@ -183,7 +187,7 @@ def _validate(self, response):
assert response.request.url == self.url
if self.headers:
headers = {k:str(v) for k,v in convert_yaml_obj_to_content(self.headers).items()}
response.assert_headers(self.headers, msg="dummy")
response.assert_headers(headers, msg="One or more headers do not match expected values.")
if self.cookies:
CookieValidator(self._session, convert_yaml_obj_to_content(self.cookies))

Expand Down Expand Up @@ -211,4 +215,4 @@ def _validate(self, response):
headers = {k:str(v) for k,v in convert_yaml_obj_to_content(self.headers).items()}
for k,v in headers.items():
if k in response.headers:
self.asserter.assert_not_equal(response.headers[k], v, f"HTTP response header {k} value matches {v} which is unexpected.")
self.asserter.assert_not_equal(response.headers[k], v, f"HTTP response header >>{k}<< value matches >>{v}<< which is unexpected.")
36 changes: 36 additions & 0 deletions arjuna/interact/http/model/internal/processor/text/store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is a part of Arjuna
# Copyright 2015-2021 Rahul Verma

# Website: www.RahulVerma.net

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from arjuna.tpi.helper.arjtype import NotFound

class TextExtractor:

def __init__(self, response):
self.__response = response

@property
def response(self):
return self.__response

def store(self, name, regex):
try:
value = self.response.text.find(regex)
except Exception:
value = NotFound()
finally:
self.response.store[name] = value

10 changes: 5 additions & 5 deletions arjuna/interact/http/model/internal/processor/text/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,28 @@ def assert_has_patterns(self, patterns):
def assert_match_for_patterns(self, patterns):
match_dict = self.__extract_patterns([k for k in patterns.keys()])
for regex, actual in match_dict.items():
self._eval_pattern_match(regex, actual, expected)
self._eval_pattern_match(regex, actual, patterns[regex])

class ExpectedTextValidator(TextValidator):

def __init__(self, response):
super().__init__(response)

def _eval_pattern_exists(self, pattern, exists):
self.asserter.assert_true(exists, f"Expected pattern {pattern} was not found.")
self.asserter.assert_true(exists, f"Expected pattern >>{pattern}<< was not found.")

def _eval_pattern_match(self, pattern, actual, expected):
self.asserter.assert_equal(actual, expected, "Value for pattern {pattern} does not match.")
self.asserter.assert_equal(actual, expected, f"Value for pattern >>{pattern}<< does not match.")

class UnexpectedTextValidator(TextValidator):

def __init__(self, response):
super().__init__(response)

def _eval_pattern_exists(self, pattern, exists):
self.asserter.assert_false(exists, f"Unexpected pattern {pattern} was found.")
self.asserter.assert_false(exists, f"Unexpected pattern >>{pattern}<< was found.")

def _eval_pattern_match(self, pattern, actual, expected):
self.asserter.assert_equal(actual, expected, "Value for pattern {pattern} does not match.")
self.asserter.assert_equal(actual, expected, f"Value for pattern >>{pattern}<< does not match.")


9 changes: 5 additions & 4 deletions arjuna/interact/http/model/internal/processor/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,16 @@ def response(self):
return self.__response

def validate(self, name, target):
print(self.__response.store)
stored_value = self.__response.store[name]

def validate_found(expected_to_be_found):
exists = True
if isinstance(stored_value, NotFound):
exists = False
if expected_to_be_found:
self.asserter.assert_true(exists, f"No value was extracted for name {name}")
self.asserter.assert_true(exists, f"No value was extracted for name >>{name}<<.")
else:
self.asserter.assert_false(exists, "{} was extracted for name {}. It should not exist.".format(stored_value, name))
self.asserter.assert_false(exists, ">>{}<< was extracted for name >>{}<<. It should not exist.".format(stored_value, name))

if "exists" not in target:
validate_found(True)
Expand All @@ -59,7 +58,9 @@ def validate_found(expected_to_be_found):
if type(stored_value) is str:
proc_stored_value = Text(stored_value)
for item in v:
getattr(proc_stored_value, "assert_contains")(item, msg="Dummy msg.")
getattr(proc_stored_value, "assert_contains")(item, msg=f"Extracted value for >>{name}<< does not contain >>{item}<<.")
elif k == "min":
self.asserter.assert_min(stored_value, v, msg=f"Extracted value for >>{name}<< did not match min value criterion.")



Expand Down
36 changes: 36 additions & 0 deletions arjuna/interact/http/model/internal/processor/xml/store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is a part of Arjuna
# Copyright 2015-2021 Rahul Verma

# Website: www.RahulVerma.net

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from arjuna.tpi.helper.arjtype import NotFound

class XmlExtractor:

def __init__(self, response):
self.__response = response

@property
def response(self):
return self.__response

def store(self, name, xpath):
try:
value = self.response.html.find_with_xpath(xpath)
except Exception:
value = NotFound()
finally:
self.response.store[name] = value

7 changes: 6 additions & 1 deletion arjuna/interact/http/model/internal/repr/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

class HttpRequestYamlRepr:

def __init__(self, session, req_yaml):
def __init__(self, session, req_yaml, *, label):
self.__label = label
if "method" in req_yaml:
self.__method = req_yaml["method"]
del req_yaml["method"]
Expand Down Expand Up @@ -48,6 +49,10 @@ def __init__(self, session, req_yaml):

self.__attrs = req_yaml

@property
def label(self):
return self.__label

@property
def method(self):
return self.__method
Expand Down
19 changes: 15 additions & 4 deletions arjuna/interact/http/model/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def __init__(self, session, req_repr, resp_processor):
self.__session = session
self.__req_repr = req_repr
self.__resp_processor = resp_processor
self.__label = req_repr.label

@property
def label(self):
return self.__label

@property
def _req(self):
Expand All @@ -42,13 +47,13 @@ def send(self):
except AttributeError:
raise Exception(f"Unsupported HTTP method: {method}")
else:
response = call(self._req.route, **self._req.attrs)
response = call(self._req.route, label=self.label, **self._req.attrs)
self.__resp_processor.validate(response)
return response

@classmethod
def root(cls, session):
req_repr = HttpRequestYamlRepr(session, CIStringDict())
req_repr = HttpRequestYamlRepr(session, CIStringDict(), label="Root")
resp_proc = HttpResponseYamlRepr(session, CIStringDict())
return HttpMessage(session, req_repr, resp_proc)

Expand All @@ -68,11 +73,17 @@ def from_file(cls, session, msg_file_name, **fargs):
if msg_yaml is None:
return cls.root(session)

if "label" in msg_yaml:
label = msg_yaml["label"]
del msg_yaml["label"]
else:
label = msg_file_name

if "request" in msg_yaml:
req_repr = HttpRequestYamlRepr(session, CIStringDict(msg_yaml["request"].as_map()))
req_repr = HttpRequestYamlRepr(session, CIStringDict(msg_yaml["request"].as_map()), label=label)
del msg_yaml["request"]
else:
req_repr = HttpRequestYamlRepr(session, CIStringDict())
req_repr = HttpRequestYamlRepr(session, CIStringDict(), label=label)

resp_proc = HttpResponseYamlRepr(session, CIStringDict(msg_yaml.as_map()))

Expand Down
6 changes: 5 additions & 1 deletion arjuna/tpi/helper/arjtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,11 @@ def right(self, node):
class NotFound:
'''
To differentiate a not found object from Python's None.
Always evalutes to False in a boolean context.
'''
pass

def __bool__(self):
return False

NetworkPacketInfo = namedtuple("NetworkPacketInfo", "label request response sub_network_packets")
4 changes: 2 additions & 2 deletions arjuna/tpi/httpauto/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ def value(self):
return self._cookie.value

@property
def secure_enabled(self):
def secure(self):
return self._cookie.secure

@property
def httponly_enabled(self):
def httponly(self):
return self._cookie.has_nonstandard_attr("HttpOnly")


Expand Down
2 changes: 1 addition & 1 deletion arjuna/tpi/parser/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ def findall(self, repattern):
def find(self, repattern):
all = re.findall(repattern, self.content)
if all:
return all
return all[0]
else:
raise Exception(f"No match found for {repattern} in {self.content}")

Expand Down

0 comments on commit 14e9def

Please sign in to comment.