Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Postgres Django backend doesn't allow the null character in non-null strings #1045

Closed
mr-bo-jangles opened this issue Dec 22, 2017 · 12 comments
Assignees
Labels
bug something is clearly wrong here

Comments

@mr-bo-jangles
Copy link

mr-bo-jangles commented Dec 22, 2017

Generating Model instances with @given using a postgres backend db for testing gives me the following error.

It's making it really hard to test my stuff with Hypothesis because I'll have to manually specify all text fields to use something like text(alphabet=characters(blacklisted_categories=('Cc', 'Cs')))

Error
Traceback (most recent call last):
  File "/vagrant/savings_champion/products/tests/models.py", line 33, in test_check_bonus
    user=models(User),
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 1001, in wrapped_test
    state.run()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 725, in run
    runner.run()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/engine.py", line 393, in run
    self._run()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/engine.py", line 791, in _run
    self.generate_new_examples()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/engine.py", line 706, in generate_new_examples
    self.test_function(last_data)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/engine.py", line 153, in test_function
    self._test_function(data)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 691, in evaluate_test_data
    escalate_hypothesis_internal_error()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 663, in evaluate_test_data
    result = self.execute(data, collect=True)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 578, in execute
    result = self.test_runner(data, run)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/executors.py", line 78, in <lambda>
    lambda: function(data)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/executors.py", line 33, in execute
    return function()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/executors.py", line 78, in <lambda>
    lambda: function(data)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 552, in run
    args, kwargs = data.draw(self.search_strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 121, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 136, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/core.py", line 180, in do_draw
    return self.base.do_draw(data)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/lazy.py", line 158, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 56, in newtuple
    return tuple(xs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/strategies.py", line 507, in do_draw
    return self.pack(data.draw(self.mapped_strategy))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/lazy.py", line 158, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/strategies.py", line 507, in do_draw
    return self.pack(data.draw(self.mapped_strategy))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 56, in newtuple
    return tuple(xs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/lazy.py", line 158, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/strategies.py", line 1550, in do_draw
    return f(draw, *args, **kwargs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/extra/django/models.py", line 161, in _models_impl
    return draw(strat)[0]
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/strategies.py", line 1548, in draw
    return data.draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/strategies.py", line 507, in do_draw
    return self.pack(data.draw(self.mapped_strategy))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/lazy.py", line 158, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 56, in newtuple
    return tuple(xs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/strategies.py", line 507, in do_draw
    return self.pack(data.draw(self.mapped_strategy))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 56, in newtuple
    return tuple(xs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/collections.py", line 60, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/strategies.py", line 1550, in do_draw
    return f(draw, *args, **kwargs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/extra/django/models.py", line 161, in _models_impl
    return draw(strat)[0]
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/strategies.py", line 1548, in draw
    return data.draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 125, in draw
    return self.__draw(strategy)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/internal/conjecture/data.py", line 132, in __draw
    return strategy.do_draw(self)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/searchstrategy/strategies.py", line 507, in do_draw
    return self.pack(data.draw(self.mapped_strategy))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/hypothesis/strategies.py", line 970, in <lambda>
    lambda value: target(*value[0], **value[1])
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/query.py", line 487, in get_or_create
    return self.get(**lookup), False
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/query.py", line 397, in get
    num = len(clone)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/query.py", line 254, in __len__
    self._fetch_all()
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/query.py", line 1179, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/query.py", line 54, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 1063, in execute_sql
    cursor.execute(sql, params)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/raven/contrib/django/client.py", line 123, in execute
    return real_execute(self, sql, params)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/ubuntu/.local/lib/python3.5/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
ValueError: A string literal cannot contain NUL (0x00) characters.
@mr-bo-jangles
Copy link
Author

I think a possible solution would be to either a) further restrict the set of allowed characters based upon the db backend for the model as this isn't something we can change or b) make it possible to configure this via settings

@jackfirth
Copy link

Would it be enough to name that compound strategy and export it for use in your tests instead? They're first class values, so that'd look something like this:

# helper.py

postgres_text = text(alphabet=characters(blacklisted_categories=('Cc', 'Cs')))

# test.py

from helper import postgres_text

@given(postgres_text, ...)
def test_something(pg_txt, ...):
  ...

@mr-bo-jangles
Copy link
Author

mr-bo-jangles commented Dec 22, 2017 via email

@Zac-HD Zac-HD added the bug something is clearly wrong here label Dec 23, 2017
@Zac-HD
Copy link
Member

Zac-HD commented Dec 23, 2017

Ah, we're only a few months late to the party! This is a validation error in Django 2, so I'll blacklist null characters in our default TextField / CharField strategies.

@mr-bo-jangles
Copy link
Author

Updated to Hypothesis 3.44.13 and tried this out, I'm still getting ValueError: A string literal cannot contain NUL (0x00) characters.

@Zac-HD
Copy link
Member

Zac-HD commented Jan 8, 2018

That's certainly not good - can you post a reproducing example? Minimal examples are ideal but anything that would let me reproduce this would be great!

@eduzen
Copy link
Contributor

eduzen commented Dec 15, 2018

@Zac-HD I have the same problem trying to test a django detail view. So I tried to make a minimal sample and I decided to make a model test. I tried to reduce it to the minimal expresion:

@given(st.text(min_size=4), st.text(min_size=4))
@pytest.mark.django_db
def test_book_detail_ok(client, first_name, last_name):
    author = Author.objects.create(first_name=first_name, last_name=last_name)
    assert author

The error is with this example:

Falsifying example: test_book_detail_ok(client=<django.test.client.Client at 0x7f526d78c160>, first_name='0000', last_name='000/')
...
django.db.transaction.TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

If this is a bug I would like to contribute in a solution 😄

@DRMacIver
Copy link
Member

@eduzen This is a separate issue. The problem you're seeing there is that due to limitations in how pytest fixtures work, Hypothesis is not compatible with pytest style tests for Django. You need to use the test case subclasseds.

@eduzen
Copy link
Contributor

eduzen commented Dec 15, 2018

@DRMacIver thank you. Sorry for the noise.

@DRMacIver
Copy link
Member

No worries! It's a very non-obvious error message (for reasons that are mostly out of our control 😢) so confusion is understandable.

@eduzen
Copy link
Contributor

eduzen commented Dec 16, 2018

Hi @DRMacIver, I did what you said about the test case subclasseds:

class Author(models.Model):
    """Model representing an author."""

    first_name = models.CharField(max_length=100, blank=True, null=True)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField(null=True, blank=True)
    slug = models.SlugField(max_length=50, blank=True, null=True)

class TestCompany(TestCase):
    @given(st.text(), st.text())
    def test_can_find_unique_name(self, first_name, last_name):
        assert  Author.objects.create(first_name=first_name, last_name=last_name)

And I get:

E               ValueError: A string literal cannot contain NUL (0x00) characters.
/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py:85: ValueError
-------------------------------------------------------------------- Hypothesis ---------------------------------------------------------------------
Falsifying example: test_can_find_unique_name(self=<books.tests.models.test_author.TestCompany testMethod=test_can_find_unique_name>, first_name='',last_name='\x00')

is it ok? or is the same thing reported? I also tried with min_sizegreater than 1

@Zac-HD
Copy link
Member

Zac-HD commented Dec 17, 2018

I think you might need to check the Hypothesis for Django docs - I'd write it as follows, but I'm not entirely sure what you're testing here.

class TestCompany(hypothesis.extra.django.TestCase):
    @given(hypothesis.extra.django.models.models(Author))
    def test_can_find_unique_name(self, author):
        assert author

Alternatively, you can specify the alphabet=st.characters(...) argument to st.text() to exclude the null character, or just filter or assume your way around it - you're just generating strings which are invalid for Django databases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something is clearly wrong here
Projects
None yet
Development

No branches or pull requests

5 participants