def test_filter_datetime_field(self):
        """Test filtering by a datetime field."""
        PersonModel.mock()

        parameters = {'filter[created_at]': date.today().strftime('%Y-%m-%d')}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_computed_field(self):
        """Test filtering by a computed field."""
        PersonModel.mock(is_employed=True)

        parameters = {'filter[employed_integer]': '1'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_decimal_field(self):
        """Test filtering by a decimal field."""
        PersonModel.mock(rate='12.51')

        parameters = {'filter[rate]': '12.51'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_multiple_string_field(self):
        """Test filtering by multiple string fields."""
        PersonModel.mock(name='A PRODUCT Wildcard')

        parameters = {'filter[name]': 'prod,test,card'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_enum_field(self):
        """Test filtering by an enum field."""
        PersonModel.mock(gender='male')

        parameters = {'filter[gender]': 'male'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_boolean_field(self):
        """Test filtering by a boolean field."""
        PersonModel.mock(is_employed=True)

        parameters = {'filter[is_employed]': '1'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_integer_field(self):
        """Test filtering by an integer field."""
        PersonModel.mock(age=80)

        parameters = {'filter[age]': '80'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_filter_many_to_many_relationship(self):
        """Test filtering by a many-to-many relationship field."""
        company = CompanyModel.mock(name="company")
        PersonModel.mock(companies=[company])

        parameters = {'filter[companies.name]': 'COMPANY'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_sort_field_descending(self):
        """Test sorting a field in descending order."""
        PersonModel.mock(name="A")
        PersonModel.mock(name="B")

        parameters = {'sort': '-name'}
        query = Resource(self.model, parameters).sort_query(self.query)

        result = query.all()
        self.assertEqual(result[0].name, 'B')
    def test_filter_string_field(self):
        """Test filtering by a string field."""
        PersonModel.mock(name='A PRODUCT Wildcard')

        parameters = {'filter[name]': 'prod'}
        query = Resource(
            self.model, parameters, MarshmallowDriver, PersonSchema).\
            filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_offset(self):
        """Test offsetting a page by the offset parameter."""
        PersonModel.mock(name='First')
        PersonModel.mock(name='Second')

        parameters = {'offset': '1'}
        query = Resource(self.model, parameters).paginate_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0].name == 'Second')
    def test_page_size(self):
        """Test limiting a page by the page[size] parameter."""
        PersonModel.mock(name='First')
        PersonModel.mock(name='Second')

        parameters = {'page[size]': '1'}
        query = Resource(self.model, parameters).paginate_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0].name == 'First')
    def test_include_foreign_key(self):
        """Test including a foreign key relationship field."""
        PersonModel.mock()
        model = EmployeeModel.mock(person_id=1)

        include = ['person']
        iv_objects, errors = IncludeValue.generate(EmployeeSchema, include)
        data = IncludeValue.include(iv_objects, model)

        self.assertTrue(errors == [])
        self.assertTrue(len(data) == 1)
    def test_sort_field_ascending(self):
        """Test sorting a field in ascending order."""
        PersonModel.mock(name="A")
        PersonModel.mock(name="B")

        parameters = {'sort': 'name'}
        query = Resource(
            self.model, parameters, marshmallow.MarshmallowDriver, self.view).\
            sort_query(self.query)

        result = query.all()
        self.assertEqual(result[0].name, 'A')
    def test_filter_decimal_field_invalid_value(self):
        """Test filtering by an invalid decimal value."""
        PersonModel.mock(rate='12.51')

        try:
            parameters = {'filter[rate]': 'a'}
            Resource(self.model, parameters).filter_query(self.query)
        except JSONAPIError as exc:
            message = exc.message
            self.assertIn('detail', message['errors'][0])
            self.assertTrue(
                message['errors'][0]['source']['parameter'] == 'filter[rate]')
    def test_sort_invalid_relationship(self):
        """Test sorting against non-existant relationship attribute."""
        PersonModel.mock()

        try:
            parameters = {'sort': 'companies.x'}
            Resource(self.model, parameters).sort_query(self.query)
        except JSONAPIError as exc:
            message = exc.message
            self.assertTrue(
                message['errors'][0]['source']['parameter'] == 'sort')
            self.assertIn('detail', message['errors'][0])
    def test_sort_relationship_descending(self):
        """Test sorting a relationhip's field in descending order."""
        a = CompanyModel.mock(name="Last")
        b = CompanyModel.mock(name="First")
        PersonModel.mock(name="A", companies=[a])
        PersonModel.mock(name="B", companies=[b])

        parameters = {'sort': '-companies.name'}
        query = Resource(self.model, parameters).sort_query(self.query)

        result = query.all()
        self.assertEqual(result[0].companies[0].name, 'Last')
    def test_limit(self):
        """Test limiting a page by the limit parameter."""
        PersonModel.mock(name='First')
        PersonModel.mock(name='Second')

        parameters = {'limit': '1'}
        query = Resource(
            self.model, parameters, MarshmallowDriver, PersonSchema).\
            paginate_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0].name == 'First')
    def test_page_number(self):
        """Test offsetting a page by the page[number] parameter."""
        PersonModel.mock(name='First')
        PersonModel.mock(name='Second')

        parameters = {'page[number]': '1', 'page[size]': '1'}
        query = Resource(
            self.model, parameters, MarshmallowDriver, PersonSchema).\
            paginate_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0].name == 'Second')
    def test_filter_enum_field_invalid_value(self):
        """Test filtering by an invalid enum value."""
        PersonModel.mock(gender='male')

        try:
            parameters = {'filter[gender]': 'mal'}
            Resource(self.model, parameters).filter_query(self.query)
        except JSONAPIError as exc:
            message = exc.message
            self.assertIn('detail', message['errors'][0])
            self.assertTrue(
                message['errors'][0]['source']['parameter'] ==
                'filter[gender]')
    def test_sort_invalid_field(self):
        """Test sorting against non-existant attribute."""
        PersonModel.mock()

        try:
            parameters = {'sort': 'x'}
            Resource(
                self.model, parameters, marshmallow.MarshmallowDriver,
                self.view).sort_query(self.query)
        except JSONAPIError as exc:
            message = exc.message
            self.assertTrue(
                message['errors'][0]['source']['parameter'] == 'sort')
            self.assertIn('detail', message['errors'][0])
    def test_include_empty_relationship(self):
        """Test including an empty one-to-one relationship."""
        model = PersonModel.mock()

        parameters = {'include': 'employee'}
        included = Resource(self.model, parameters).compound_response(model)
        self.assertTrue(len(included) == 0)
    def test_include_many_to_many(self):
        """Test including a many-to-many relationship."""
        company = CompanyModel.mock()
        model = PersonModel.mock(companies=[company])

        parameters = {'include': 'companies'}
        included = Resource(self.model, parameters).compound_response(model)
        self.assertTrue(len(included) == 1)
    def test_include_one_to_one(self):
        """Test including a one-to-one relationship."""
        model = PersonModel.mock()
        EmployeeModel.mock(person_id=1)

        parameters = {'include': 'employee'}
        included = Resource(self.model, parameters).compound_response(model)
        self.assertTrue(len(included) == 1)
    def test_include_nested(self):
        """Test including a nested relationship."""
        company = CompanyModel.mock()
        model = PersonModel.mock(companies=[company])
        EmployeeModel.mock(person_id=model.id)

        parameters = {'include': 'employee.person.companies'}
        included = Resource(self.model, parameters).compound_response(model)
        self.assertTrue(len(included) == 1)
    def test_filter_one_to_many_relationship(self):
        """Test filtering by a foreign key relationship field."""
        person = PersonModel.mock()
        EmployeeModel.mock(name="employee", person_id=person.id)

        parameters = {'filter[employee.name]': 'EMPLOYEE'}
        query = Resource(self.model, parameters).filter_query(self.query)

        result = query.all()
        self.assertEqual(len(result), 1)
    def test_include_one_to_many_relationship(self):
        """Test including a one to many relationship field."""
        model = PersonModel.mock()
        EmployeeModel.mock(person_id=1)

        include = ['employee']
        iv_objects, errors = IncludeValue.generate(self.view, include)
        data = IncludeValue.include(iv_objects, model)

        self.assertTrue(errors == [])
        self.assertTrue(len(data) == 1)
    def test_include_many_to_many_relationship(self):
        """Test including a many to many relationship field."""
        company_1 = CompanyModel.mock()
        company_2 = CompanyModel.mock()
        model = PersonModel.mock(companies=[company_1, company_2])

        include = ['companies']
        iv_objects, errors = IncludeValue.generate(self.view, include)
        data = IncludeValue.include(iv_objects, model)

        self.assertTrue(errors == [])
        self.assertTrue(len(data) == 2)
    def test_include_missing_field(self):
        """Test including an unknown field."""
        model = PersonModel.mock()

        try:
            parameters = {'include': 'wxyz'}
            Resource(self.model, parameters).compound_response(model)
            self.assertTrue(False)
        except JSONAPIError as exc:
            message = exc.message
            self.assertTrue(
                message['errors'][0]['source']['parameter'] == 'include')
            self.assertIn('detail', message['errors'][0])
    def test_include_multiple_relationships(self):
        """Test including multiple relationship fields of differing
        types.
        """
        company = CompanyModel.mock()
        model = PersonModel.mock(companies=[company])
        EmployeeModel.mock(person_id=1)

        include = ['companies', 'employee']
        iv_objects, errors = IncludeValue.generate(self.view, include)
        data = IncludeValue.include(iv_objects, model)

        self.assertTrue(errors == [])
        self.assertTrue(len(data) == 2)