Skip to content

Commit 14e9def

Browse files
committed
Suuport for httponly/secure, min. Store and Validate support. Imp msgs.
1 parent ae03dc9 commit 14e9def

File tree

12 files changed

+136
-38
lines changed

12 files changed

+136
-38
lines changed

arjuna/interact/http/model/internal/processor/cookie/validator.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,23 @@ class CookieValidator(AsserterMixIn):
2121

2222
def __init__(self, session, cookie_yaml):
2323
super().__init__()
24+
cookies = session.parsed_cookies
2425
for cookie_name, cookie_val in cookie_yaml.items():
25-
msg = f"Cookie with name {cookie_name} was not found in session cookies."
26+
msg = f"Cookie with name >>{cookie_name}<< was not found in session cookies."
2627
try:
2728
self.asserter.assert_true(cookie_name in session.cookies, msg=msg)
2829
except AssertionError:
2930
self.asserter.fail(msg=msg)
30-
attr_map = {
31-
"secure": None,
32-
"HttpOnly": None
33-
}
34-
cookie_val_to_match = None
31+
32+
cookie = cookies[cookie_name]
33+
3534
if type(cookie_val) is dict:
3635
for k,v in cookie_val.items():
37-
if k in raw_map:
38-
raw_map[k] = v
39-
if "value" in cookie_val:
40-
cookie_val_to_match = cookie_val["value"]
36+
if k == "value":
37+
self.asserter.assert_equal(cookie.value, cookie_val["value"], f"Cookie value for >>{cookie_name}<< does not match expected value.")
38+
elif k.lower() == "httponly":
39+
self.asserter.assert_true(cookie.httponly, f"Cookie >>{cookie_name}<< does not have >>HttpOnly flag<< set.")
40+
elif k.lower() == "secure":
41+
self.asserter.assert_true(cookie.secure, f"Cookie >>{cookie_name}<< does not have >>secure flag<< set.")
4142
else:
42-
cookie_val_to_match = str(cookie_val)
43+
self.asserter.assert_equal(cookie.value, str(cookie_val), f"Cookie value for >>{cookie_name}<< does not match expected value.")

arjuna/interact/http/model/internal/processor/json/validator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ def __init__(self, response):
6767
super().__init__(response)
6868

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

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

7575
class UnexpectedJsonValidator(_JsonValidator):
7676

7777
def __init__(self, response):
7878
super().__init__(response)
7979

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

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

arjuna/interact/http/model/internal/processor/response.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from .cookie.validator import CookieValidator
2121
from .text.validator import ExpectedTextValidator, UnexpectedTextValidator
2222
from .header.store import HeaderExtractor
23+
from .text.store import TextExtractor
24+
from .xml.store import XmlExtractor
2325
from .validator import Validator
2426
from arjuna.interact.http.model.internal.helper.yaml import convert_yaml_obj_to_content
2527
from arjuna.tpi.engine.asserter import AsserterMixIn
@@ -82,9 +84,7 @@ def __init__(self, session, codes=None, url=None, headers={}, cookies={}, has={}
8284

8385
for k in self.__repr["validate"]:
8486
if k not in self.__repr["store"]:
85-
raise Exception(f"{k} in validate section is not defined in store section of message.")
86-
87-
print(self.__repr)
87+
raise Exception(f"{k} in validate section is not defined in store section of message.")
8888

8989
@property
9090
def _session(self):
@@ -154,6 +154,10 @@ def validate(self, response):
154154
for name, sdict in self.store.items():
155155
if sdict["header"]:
156156
HeaderExtractor(response).store(name, sdict["header"])
157+
elif sdict["regex"]:
158+
TextExtractor(response).store(name, sdict["regex"])
159+
elif sdict["xpath"]:
160+
XmlExtractor(response).store(name, sdict["xpath"])
157161
if self.validations:
158162
validator = Validator(response)
159163
for k, target in self.validations.items():
@@ -183,7 +187,7 @@ def _validate(self, response):
183187
assert response.request.url == self.url
184188
if self.headers:
185189
headers = {k:str(v) for k,v in convert_yaml_obj_to_content(self.headers).items()}
186-
response.assert_headers(self.headers, msg="dummy")
190+
response.assert_headers(headers, msg="One or more headers do not match expected values.")
187191
if self.cookies:
188192
CookieValidator(self._session, convert_yaml_obj_to_content(self.cookies))
189193

@@ -211,4 +215,4 @@ def _validate(self, response):
211215
headers = {k:str(v) for k,v in convert_yaml_obj_to_content(self.headers).items()}
212216
for k,v in headers.items():
213217
if k in response.headers:
214-
self.asserter.assert_not_equal(response.headers[k], v, f"HTTP response header {k} value matches {v} which is unexpected.")
218+
self.asserter.assert_not_equal(response.headers[k], v, f"HTTP response header >>{k}<< value matches >>{v}<< which is unexpected.")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This file is a part of Arjuna
2+
# Copyright 2015-2021 Rahul Verma
3+
4+
# Website: www.RahulVerma.net
5+
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
from arjuna.tpi.helper.arjtype import NotFound
19+
20+
class TextExtractor:
21+
22+
def __init__(self, response):
23+
self.__response = response
24+
25+
@property
26+
def response(self):
27+
return self.__response
28+
29+
def store(self, name, regex):
30+
try:
31+
value = self.response.text.find(regex)
32+
except Exception:
33+
value = NotFound()
34+
finally:
35+
self.response.store[name] = value
36+

arjuna/interact/http/model/internal/processor/text/validator.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,28 +60,28 @@ def assert_has_patterns(self, patterns):
6060
def assert_match_for_patterns(self, patterns):
6161
match_dict = self.__extract_patterns([k for k in patterns.keys()])
6262
for regex, actual in match_dict.items():
63-
self._eval_pattern_match(regex, actual, expected)
63+
self._eval_pattern_match(regex, actual, patterns[regex])
6464

6565
class ExpectedTextValidator(TextValidator):
6666

6767
def __init__(self, response):
6868
super().__init__(response)
6969

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

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

7676
class UnexpectedTextValidator(TextValidator):
7777

7878
def __init__(self, response):
7979
super().__init__(response)
8080

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

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

8787

arjuna/interact/http/model/internal/processor/validator.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,16 @@ def response(self):
3030
return self.__response
3131

3232
def validate(self, name, target):
33-
print(self.__response.store)
3433
stored_value = self.__response.store[name]
3534

3635
def validate_found(expected_to_be_found):
3736
exists = True
3837
if isinstance(stored_value, NotFound):
3938
exists = False
4039
if expected_to_be_found:
41-
self.asserter.assert_true(exists, f"No value was extracted for name {name}")
40+
self.asserter.assert_true(exists, f"No value was extracted for name >>{name}<<.")
4241
else:
43-
self.asserter.assert_false(exists, "{} was extracted for name {}. It should not exist.".format(stored_value, name))
42+
self.asserter.assert_false(exists, ">>{}<< was extracted for name >>{}<<. It should not exist.".format(stored_value, name))
4443

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

6465

6566

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This file is a part of Arjuna
2+
# Copyright 2015-2021 Rahul Verma
3+
4+
# Website: www.RahulVerma.net
5+
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
from arjuna.tpi.helper.arjtype import NotFound
19+
20+
class XmlExtractor:
21+
22+
def __init__(self, response):
23+
self.__response = response
24+
25+
@property
26+
def response(self):
27+
return self.__response
28+
29+
def store(self, name, xpath):
30+
try:
31+
value = self.response.html.find_with_xpath(xpath)
32+
except Exception:
33+
value = NotFound()
34+
finally:
35+
self.response.store[name] = value
36+

arjuna/interact/http/model/internal/repr/request.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
class HttpRequestYamlRepr:
2121

22-
def __init__(self, session, req_yaml):
22+
def __init__(self, session, req_yaml, *, label):
23+
self.__label = label
2324
if "method" in req_yaml:
2425
self.__method = req_yaml["method"]
2526
del req_yaml["method"]
@@ -48,6 +49,10 @@ def __init__(self, session, req_yaml):
4849

4950
self.__attrs = req_yaml
5051

52+
@property
53+
def label(self):
54+
return self.__label
55+
5156
@property
5257
def method(self):
5358
return self.__method

arjuna/interact/http/model/message.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ def __init__(self, session, req_repr, resp_processor):
2626
self.__session = session
2727
self.__req_repr = req_repr
2828
self.__resp_processor = resp_processor
29+
self.__label = req_repr.label
30+
31+
@property
32+
def label(self):
33+
return self.__label
2934

3035
@property
3136
def _req(self):
@@ -42,13 +47,13 @@ def send(self):
4247
except AttributeError:
4348
raise Exception(f"Unsupported HTTP method: {method}")
4449
else:
45-
response = call(self._req.route, **self._req.attrs)
50+
response = call(self._req.route, label=self.label, **self._req.attrs)
4651
self.__resp_processor.validate(response)
4752
return response
4853

4954
@classmethod
5055
def root(cls, session):
51-
req_repr = HttpRequestYamlRepr(session, CIStringDict())
56+
req_repr = HttpRequestYamlRepr(session, CIStringDict(), label="Root")
5257
resp_proc = HttpResponseYamlRepr(session, CIStringDict())
5358
return HttpMessage(session, req_repr, resp_proc)
5459

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

76+
if "label" in msg_yaml:
77+
label = msg_yaml["label"]
78+
del msg_yaml["label"]
79+
else:
80+
label = msg_file_name
81+
7182
if "request" in msg_yaml:
72-
req_repr = HttpRequestYamlRepr(session, CIStringDict(msg_yaml["request"].as_map()))
83+
req_repr = HttpRequestYamlRepr(session, CIStringDict(msg_yaml["request"].as_map()), label=label)
7384
del msg_yaml["request"]
7485
else:
75-
req_repr = HttpRequestYamlRepr(session, CIStringDict())
86+
req_repr = HttpRequestYamlRepr(session, CIStringDict(), label=label)
7687

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

arjuna/tpi/helper/arjtype.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,11 @@ def right(self, node):
543543
class NotFound:
544544
'''
545545
To differentiate a not found object from Python's None.
546+
547+
Always evalutes to False in a boolean context.
546548
'''
547-
pass
549+
550+
def __bool__(self):
551+
return False
548552

549553
NetworkPacketInfo = namedtuple("NetworkPacketInfo", "label request response sub_network_packets")

arjuna/tpi/httpauto/session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ def value(self):
4242
return self._cookie.value
4343

4444
@property
45-
def secure_enabled(self):
45+
def secure(self):
4646
return self._cookie.secure
4747

4848
@property
49-
def httponly_enabled(self):
49+
def httponly(self):
5050
return self._cookie.has_nonstandard_attr("HttpOnly")
5151

5252

arjuna/tpi/parser/text.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ def findall(self, repattern):
264264
def find(self, repattern):
265265
all = re.findall(repattern, self.content)
266266
if all:
267-
return all
267+
return all[0]
268268
else:
269269
raise Exception(f"No match found for {repattern} in {self.content}")
270270

0 commit comments

Comments
 (0)