|
@@ -1,5 +1,6 @@
|
|
import json
|
|
import json
|
|
import re
|
|
import re
|
|
|
|
+import math
|
|
|
|
|
|
|
|
|
|
def basic_body_verification(body, url, is_json_check=True):
|
|
def basic_body_verification(body, url, is_json_check=True):
|
|
@@ -10,7 +11,6 @@ def basic_body_verification(body, url, is_json_check=True):
|
|
problems encountered, always as a list. If len(problems) > 0,
|
|
problems encountered, always as a list. If len(problems) > 0,
|
|
then the response body does not have to be examined further and the caller
|
|
then the response body does not have to be examined further and the caller
|
|
should handle the failing problem(s).
|
|
should handle the failing problem(s).
|
|
-
|
|
|
|
Plaintext and Fortunes set `is_json_check` to False
|
|
Plaintext and Fortunes set `is_json_check` to False
|
|
'''
|
|
'''
|
|
|
|
|
|
@@ -134,8 +134,8 @@ def verify_helloworld_object(json_object, url):
|
|
|
|
|
|
def verify_randomnumber_object(db_object, url, max_infraction='fail'):
|
|
def verify_randomnumber_object(db_object, url, max_infraction='fail'):
|
|
'''
|
|
'''
|
|
- Ensures that `db_object` is a JSON object with
|
|
|
|
- keys 'id' and 'randomNumber' that both map to ints.
|
|
|
|
|
|
+ Ensures that `db_object` is a JSON object with
|
|
|
|
+ keys 'id' and 'randomNumber' that both map to ints.
|
|
Should closely resemble:
|
|
Should closely resemble:
|
|
{ "id": 2354, "randomNumber": 8952 }
|
|
{ "id": 2354, "randomNumber": 8952 }
|
|
'''
|
|
'''
|
|
@@ -201,11 +201,11 @@ def verify_randomnumber_object(db_object, url, max_infraction='fail'):
|
|
|
|
|
|
def verify_randomnumber_list(expected_len, headers, body, url, max_infraction='fail'):
|
|
def verify_randomnumber_list(expected_len, headers, body, url, max_infraction='fail'):
|
|
'''
|
|
'''
|
|
- Validates that the object is a list containing a number of
|
|
|
|
|
|
+ Validates that the object is a list containing a number of
|
|
randomnumber object. Should closely resemble:
|
|
randomnumber object. Should closely resemble:
|
|
[{ "id": 2354, "randomNumber": 8952 }, { "id": 4421, "randomNumber": 32 }, ... ]
|
|
[{ "id": 2354, "randomNumber": 8952 }, { "id": 4421, "randomNumber": 32 }, ... ]
|
|
'''
|
|
'''
|
|
-
|
|
|
|
|
|
+
|
|
response, problems = basic_body_verification(body, url)
|
|
response, problems = basic_body_verification(body, url)
|
|
|
|
|
|
if len(problems) > 0:
|
|
if len(problems) > 0:
|
|
@@ -249,16 +249,58 @@ def verify_randomnumber_list(expected_len, headers, body, url, max_infraction='f
|
|
|
|
|
|
return problems
|
|
return problems
|
|
|
|
|
|
|
|
+def verify_updates(old_worlds, new_worlds, updates_expected, url):
|
|
|
|
+ '''
|
|
|
|
+ Validates that the /updates requests actually updated values in the database and didn't
|
|
|
|
+ just return a JSON list of the correct number of World items.
|
|
|
|
+sz
|
|
|
|
+ old_worlds a JSON object containing the state of the Worlds table BEFORE the /updates requests
|
|
|
|
+ new_worlds a JSON object containing the state of the Worlds table AFTER the /updates requests
|
|
|
|
+ If no items were updated, this validation test returns a "fail."
|
|
|
|
+
|
|
|
|
+ If only some items were updated (within a 5% margin of error), this test returns a "warn".
|
|
|
|
+ This is to account for the unlikely, but possible situation where an entry in the World
|
|
|
|
+ table is updated to the same value it was previously set as.
|
|
|
|
+ '''
|
|
|
|
+ successful_updates = 0
|
|
|
|
+ problems = []
|
|
|
|
+
|
|
|
|
+ n = 0
|
|
|
|
+ while n < len(old_worlds) and successful_updates == 0:
|
|
|
|
+ print(old_worlds[n]['1'])
|
|
|
|
+ for i in range(1, 10001):
|
|
|
|
+ try:
|
|
|
|
+ entry_id = str(i)
|
|
|
|
+ if entry_id in old_worlds[n] and entry_id in new_worlds[n]:
|
|
|
|
+ if old_worlds[n][entry_id] != new_worlds[n][entry_id]:
|
|
|
|
+ successful_updates += 1
|
|
|
|
+ except Exception as e:
|
|
|
|
+ print e
|
|
|
|
+ n += 1
|
|
|
|
+
|
|
|
|
+ if successful_updates == 0:
|
|
|
|
+ problems.append(
|
|
|
|
+ ("fail", "No items were updated in the database.", url))
|
|
|
|
+ elif successful_updates <= (updates_expected * 0.90):
|
|
|
|
+ problems.append(
|
|
|
|
+ ("fail", "Only %s items were updated in the database out of roughly %s expected." % (successful_updates, updates_expected), url))
|
|
|
|
+ elif successful_updates <= (updates_expected * 0.95):
|
|
|
|
+ problems.append(
|
|
|
|
+ ("warn",
|
|
|
|
+ "There may have been an error updating the database. Only %s items were updated in the database out of the roughly %s expected." % (
|
|
|
|
+ successful_updates, updates_expected),
|
|
|
|
+ url))
|
|
|
|
+
|
|
|
|
+ return problems
|
|
|
|
|
|
-def verify_query_cases(self, cases, url):
|
|
|
|
|
|
+def verify_query_cases(self, cases, url, check_updates=False):
|
|
'''
|
|
'''
|
|
- The the /updates and /queries tests accept a `queries` parameter
|
|
|
|
|
|
+ The /updates and /queries tests accept a `queries` parameter
|
|
that is expected to be between 1-500.
|
|
that is expected to be between 1-500.
|
|
This method execises a framework with different `queries` parameter values
|
|
This method execises a framework with different `queries` parameter values
|
|
then verifies that the framework responds appropriately.
|
|
then verifies that the framework responds appropriately.
|
|
The `cases` parameter should be a list of 2-tuples containing the query case
|
|
The `cases` parameter should be a list of 2-tuples containing the query case
|
|
and the consequence level should the cases fail its verifications, e.g.:
|
|
and the consequence level should the cases fail its verifications, e.g.:
|
|
-
|
|
|
|
cases = [
|
|
cases = [
|
|
('2', 'fail'),
|
|
('2', 'fail'),
|
|
('0', 'fail'),
|
|
('0', 'fail'),
|
|
@@ -266,7 +308,6 @@ def verify_query_cases(self, cases, url):
|
|
('501', 'warn'),
|
|
('501', 'warn'),
|
|
('', 'fail')
|
|
('', 'fail')
|
|
]
|
|
]
|
|
-
|
|
|
|
The reason for using 'warn' is generally for a case that will be allowed in the
|
|
The reason for using 'warn' is generally for a case that will be allowed in the
|
|
current run but that may/will be a failing case in future rounds. The cases above
|
|
current run but that may/will be a failing case in future rounds. The cases above
|
|
suggest that not sanitizing the `queries` parameter against non-int input, or failing
|
|
suggest that not sanitizing the `queries` parameter against non-int input, or failing
|
|
@@ -277,6 +318,11 @@ def verify_query_cases(self, cases, url):
|
|
MAX = 500
|
|
MAX = 500
|
|
MIN = 1
|
|
MIN = 1
|
|
|
|
|
|
|
|
+ # Only load in the World table if we are doing an Update verification
|
|
|
|
+ world_db_before = {}
|
|
|
|
+ if check_updates:
|
|
|
|
+ world_db_before = self.get_current_world_table()
|
|
|
|
+
|
|
for q, max_infraction in cases:
|
|
for q, max_infraction in cases:
|
|
case_url = url + q
|
|
case_url = url + q
|
|
headers, body = self.request_headers_and_body(case_url)
|
|
headers, body = self.request_headers_and_body(case_url)
|
|
@@ -295,6 +341,14 @@ def verify_query_cases(self, cases, url):
|
|
expected_len, headers, body, case_url, max_infraction)
|
|
expected_len, headers, body, case_url, max_infraction)
|
|
problems += verify_headers(headers, case_url)
|
|
problems += verify_headers(headers, case_url)
|
|
|
|
|
|
|
|
+ # Only check update changes if we are doing an Update verification and if we're testing
|
|
|
|
+ # the highest number of queries, to ensure that we don't accidentally FAIL for a query
|
|
|
|
+ # that only updates 1 item and happens to set its randomNumber to the same value it
|
|
|
|
+ # previously held
|
|
|
|
+ if check_updates and queries >= MAX:
|
|
|
|
+ world_db_after = self.get_current_world_table()
|
|
|
|
+ problems += verify_updates(world_db_before, world_db_after, MAX, case_url)
|
|
|
|
+
|
|
except ValueError:
|
|
except ValueError:
|
|
warning = (
|
|
warning = (
|
|
'%s given for stringy `queries` parameter %s\n'
|
|
'%s given for stringy `queries` parameter %s\n'
|