Skip to content

Commit

Permalink
Uniqueness tests implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
Travis Dart committed Jul 7, 2020
1 parent e26f7b0 commit d25d29c
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 31 deletions.
15 changes: 2 additions & 13 deletions formstorm/FormElement.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,10 @@


class FormElement(object):
def __init__(self,
good=[],
bad=[],
values=[],
only_if=[],
not_if=[],
unique=False,
fk_field=None):
def __init__(self, good=[], bad=[], is_unique=False):
self.bad = bad
self.good = good
self.values = values
self.only_if = only_if
self.not_if = not_if

self.is_unique = is_unique

def build_iterator(self, form, field_name, is_e2e):
"""
Expand Down Expand Up @@ -67,6 +57,5 @@ def _replace_all_q(value_list):
self.iterator = chain(
[(x, True) for x in self.good],
[(x, False) for x in self.bad],
self.values
)
return self.iterator
60 changes: 44 additions & 16 deletions formstorm/FormTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@ def submit_form(self, form_values):

def _build_elements(self, fields_to_ignore=[]):
elements = {}
unique_elements = []
for e in dir(self):
if e in fields_to_ignore:
continue

# Filter out this class's FormElement properties
if type(getattr(self, e)) is FormElement:
elements[e] = getattr(self, e).build_iterator(
form_element = getattr(self, e)
if type(form_element) is FormElement:
elements[e] = form_element.build_iterator(
is_e2e=self.is_e2e,
form=self.form,
field_name=e
)

return elements
if form_element.is_unique:
unique_elements.append(e)

return elements, unique_elements

def __init__(self):
self._is_modelform = ModelForm in self.form.mro()
Expand All @@ -50,7 +55,7 @@ def __init__(self):
# ('AA...A', False)
# ]
# }
elements = self._build_elements()
elements, self.unique_elements = self._build_elements()

# Build iterable from the iterables of the sub-objects. For example:
# self._iterator = [
Expand All @@ -72,20 +77,32 @@ def __init__(self):
# {'subtitle': ('AA...A', False), 'title': ('AA...A', False) }
# ]
self._iterator = dict_combo(elements)

# Generate test values for multi-field validation:
for v in getattr(self, "additional_values", {}):
# The easiest way to explain the v2 operation is by example:
# v = ({"one": 1, "two": 2}, True)
# v2 = {"one": (1, True), "two": (2, True)}
v2 = dict(zip(v[0].keys(), [(y, v[1]) for y in v[0].values()]))
# Build all the combinations of the other fields.
new_elements = self._build_elements(fields_to_ignore=v[0].keys())
new_elements = self._build_elements(
fields_to_ignore=v[0].keys()
)[0] # Just take the elements and discard the unique_elements.
# Combine the values with the element from additional_values.
addl_iterator = dict_combo(new_elements, base_dict=v2)
self._iterator = itertools.chain(self._iterator, addl_iterator)

def _run(self, is_uniqueness_test=False):
def run(self):
# To run uniqueness tests, the form must be a modelform,
# and at least some good values must be specified.
# Don't run if there aren't any unique_elements.
should_run_uniqueness_test = True if self.unique_elements else False
has_run_uniqueness_test = False if self.unique_elements else True

if should_run_uniqueness_test and not self._is_modelform:
raise RuntimeError(
"Uniqueness tests can only be run on ModelForms"
)

# i is a dictionary containing tuples in the form (value, is_good)
for i in self._iterator:
# if any field is invalid, the form is invalid.
Expand All @@ -94,7 +111,7 @@ def _run(self, is_uniqueness_test=False):
# Remove None values from dict.
form_values = {k: v[0] for k, v in i.items() if v[0] is not None}

if self._is_modelform and not is_uniqueness_test:
if self._is_modelform:
sid = transaction.savepoint()

self.submit_form(form_values)
Expand All @@ -104,16 +121,27 @@ def _run(self, is_uniqueness_test=False):
# print("errors", self.bound_form.errors)
raise AssertionError

if is_uniqueness_test and form_is_good:
self.submit_form(form_values)
assert not self.is_good()
if self._is_modelform:
if not has_run_uniqueness_test and form_is_good:
# There's no way to verify that the uniqueness constraint
# was the one that triggered the error. However, if the
# form was previously valid, and now it's not, and we've
# submitted the same input, then we can conclude that
# non-uniqueness was the issue.
self.submit_form(form_values)
assert not self.is_good()
for field in self.unique_elements:
assert self.bound_form.has_error(field)
has_run_uniqueness_test = True

if self._is_modelform and not is_uniqueness_test:
transaction.savepoint_rollback(sid)

def run(self):
self._run(is_uniqueness_test=False)
# self._run(is_uniqueness_test=True)

if should_run_uniqueness_test and not has_run_uniqueness_test:
# If we make it to the end without having run the uniqueness test,
# then it must be because no good input was specified.
raise RuntimeError(
"Good input must be given to run uniqueness test."
)

def run_uniqueness_tests(self):
pass
Expand Down
1 change: 0 additions & 1 deletion tests/fstestapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class Genre(models.Model):

class Book(models.Model):
def clean(self):
# Don't allow draft entries to have a pub_date.
if len(self.title) + len(self.subtitle) > 150:
raise ValidationError(
"Title and subtitle can't have a combined length greater than "
Expand Down
2 changes: 1 addition & 1 deletion tests/fstestapp/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class BookFormTest(FormTest):
title = FormElement(
good=["Moby Dick"],
bad=[None, "", "A"*101],
is_unique=True
)
subtitle = FormElement(
good=[None, "", "or The Whale"],
Expand Down Expand Up @@ -110,7 +111,6 @@ def test_dict_combo_with_base_dict(self):
assert list(x) == y



class BookTestCase(TestCase):
def setUp(self):
Genre(name="Mystery").save()
Expand Down

0 comments on commit d25d29c

Please sign in to comment.