Esempio n. 1
0
class NotModifiedTest(BaseTest):
    def setUp(self):
        super(NotModifiedTest, self).setUp()
        self.stored = self.collection.create_record({})

        self.resource = BaseResource(request=self.get_request(),
                                     context=self.get_context())
        self.resource.collection_get()
        current = self.last_response.headers['ETag']
        self.resource.request.headers['If-None-Match'] = current

    def test_collection_returns_200_if_change_meanwhile(self):
        self.resource.request.headers['If-None-Match'] = '"42"'
        self.resource.collection_get()  # Not raising.

    def test_collection_returns_304_if_no_change_meanwhile(self):
        try:
            self.resource.collection_get()
        except httpexceptions.HTTPNotModified as e:
            error = e
        self.assertEqual(error.code, 304)
        self.assertIsNotNone(error.headers.get('ETag'))
        self.assertIsNotNone(error.headers.get('Last-Modified'))

    def test_single_record_returns_304_if_no_change_meanwhile(self):
        self.resource.record_id = self.stored['id']
        try:
            self.resource.get()
        except httpexceptions.HTTPNotModified as e:
            error = e
        self.assertEqual(error.code, 304)
        self.assertIsNotNone(error.headers.get('ETag'))
        self.assertIsNotNone(error.headers.get('Last-Modified'))

    def test_single_record_last_modified_is_returned(self):
        self.resource.timestamp = 0
        self.resource.record_id = self.stored['id']
        try:
            self.resource.get()
        except httpexceptions.HTTPNotModified as e:
            error = e
        self.assertNotIn('1970', error.headers['Last-Modified'])

    def test_if_none_match_empty_raises_invalid(self):
        self.resource.request.headers['If-None-Match'] = '""'
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_if_none_match_without_quotes_raises_invalid(self):
        self.resource.request.headers['If-None-Match'] = '1234'
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_if_none_match_not_integer_raises_invalid(self):
        self.resource.request.headers['If-None-Match'] = '"ab"'
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)
Esempio n. 2
0
 def test_collection_timestamp_is_not_updated_if_no_field_changed(self):
     self.resource.request.json = {'data': {'some': 'change'}}
     self.resource.patch()
     self.resource = BaseResource(self.get_request())
     self.resource.collection_get()['data']
     last_modified = int(self.last_response.headers['ETag'][1:-1])
     self.assertEquals(self.result['last_modified'], last_modified)
Esempio n. 3
0
 def test_the_timestamp_header_is_equal_to_last_modification(self):
     result = self.resource.collection_post()['data']
     modification = result['last_modified']
     self.resource = BaseResource(self.get_request())
     self.resource.collection_get()
     header = int(self.last_response.headers['ETag'][1:-1])
     self.assertEqual(header, modification)
Esempio n. 4
0
    def setUp(self):
        super(NotModifiedTest, self).setUp()
        self.stored = self.db.create(self.resource, 'bob', {})

        self.resource = BaseResource(self.get_request())
        self.resource.collection_get()
        current = self.last_response.headers['Last-Modified']
        self.resource.request.headers['If-Modified-Since'] = current
Esempio n. 5
0
    def setUp(self):
        super(NotModifiedTest, self).setUp()
        self.stored = self.collection.create_record({})

        self.resource = BaseResource(request=self.get_request(), context=self.get_context())
        self.resource.collection_get()
        current = self.last_response.headers["ETag"]
        self.resource.request.headers["If-None-Match"] = current
Esempio n. 6
0
class NotModifiedTest(BaseTest):
    def setUp(self):
        super(NotModifiedTest, self).setUp()
        self.stored = self.db.create(self.resource, 'bob', {})

        self.resource = BaseResource(self.get_request())
        self.resource.collection_get()
        current = self.last_response.headers['Last-Modified']
        self.resource.request.headers['If-Modified-Since'] = current

    def test_collection_returns_304_if_no_change_meanwhile(self):
        try:
            self.resource.collection_get()
        except httpexceptions.HTTPNotModified as e:
            error = e
        self.assertEqual(error.code, 304)
        self.assertIsNotNone(error.headers.get('Last-Modified'))

    def test_single_record_returns_304_if_no_change_meanwhile(self):
        self.resource.record_id = self.stored['id']
        try:
            self.resource.get()
        except httpexceptions.HTTPNotModified as e:
            error = e
        self.assertEqual(error.code, 304)
        self.assertIsNotNone(error.headers.get('Last-Modified'))
Esempio n. 7
0
class PatchTest(BaseTest):
    def setUp(self):
        super(PatchTest, self).setUp()
        self.stored = self.collection.create_record({})
        self.resource.record_id = self.stored['id']
        self.resource.request.json = {'data': {'some': 'change'}}
        self.resource.mapping.typ.unknown = 'preserve'
        self.result = self.resource.patch()['data']

    def test_modify_record_updates_timestamp(self):
        before = self.stored['last_modified']
        after = self.result['last_modified']
        self.assertNotEquals(after, before)

    def test_patch_record_returns_updated_fields(self):
        self.assertEquals(self.stored['id'], self.result['id'])
        self.assertEquals(self.result['some'], 'change')

    def test_record_timestamp_is_not_updated_if_none_for_missing_field(self):
        self.resource.request.json = {'data': {'plop': None}}
        result = self.resource.patch()['data']
        self.assertEquals(self.result['last_modified'],
                          result['last_modified'])

    def test_record_timestamp_is_not_updated_if_no_field_changed(self):
        self.resource.request.json = {'data': {'some': 'change'}}
        result = self.resource.patch()['data']
        self.assertEquals(self.result['last_modified'],
                          result['last_modified'])

    def test_collection_timestamp_is_not_updated_if_no_field_changed(self):
        self.resource.request.json = {'data': {'some': 'change'}}
        self.resource.patch()
        self.resource = BaseResource(request=self.get_request(),
                                     context=self.get_context())
        self.resource.collection_get()['data']
        last_modified = int(self.last_response.headers['ETag'][1:-1])
        self.assertEquals(self.result['last_modified'], last_modified)

    def test_timestamp_is_not_updated_if_no_change_after_preprocessed(self):
        with mock.patch.object(self.resource, 'process_record') as mocked:
            mocked.return_value = self.result
            self.resource.request.json = {'data': {'some': 'plop'}}
            result = self.resource.patch()['data']
            self.assertEquals(self.result['last_modified'],
                              result['last_modified'])

    def test_returns_changed_fields_among_provided_if_behaviour_is_diff(self):
        self.resource.request.json = {'data': {'unread': True, 'position': 10}}
        self.resource.request.headers['Response-Behavior'] = 'diff'
        with mock.patch.object(self.resource.collection, 'update_record',
                               return_value={'unread': True, 'position': 0}):
            result = self.resource.patch()['data']
        self.assertDictEqual(result, {'position': 0})

    def test_returns_changed_fields_if_behaviour_is_light(self):
        self.resource.request.json = {'data': {'unread': True, 'position': 10}}
        self.resource.request.headers['Response-Behavior'] = 'light'
        with mock.patch.object(self.resource.collection, 'update_record',
                               return_value={'unread': True, 'position': 0}):
            result = self.resource.patch()['data']
        self.assertDictEqual(result, {'unread': True, 'position': 0})
Esempio n. 8
0
class PatchTest(BaseTest):
    def setUp(self):
        super(PatchTest, self).setUp()
        self.stored = self.collection.create_record({})
        self.resource.record_id = self.stored['id']
        self.resource.request.json = {'data': {'position': 10}}
        schema = ResourceSchema()
        schema.add(colander.SchemaNode(colander.Boolean(), name='unread',
                                       missing=colander.drop))
        schema.add(colander.SchemaNode(colander.Int(), name='position',
                                       missing=colander.drop))
        self.resource.mapping = schema
        self.result = self.resource.patch()['data']

    def test_etag_is_provided(self):
        self.assertIn('ETag', self.last_response.headers)

    def test_etag_contains_record_new_timestamp(self):
        expected = ('"%s"' % self.result['last_modified'])
        self.assertEqual(expected, self.last_response.headers['ETag'])

    def test_etag_contains_old_timestamp_if_no_field_changed(self):
        self.resource.request.json = {'data': {'position': 10}}
        self.resource.patch()['data']
        expected = ('"%s"' % self.result['last_modified'])
        self.assertEqual(expected, self.last_response.headers['ETag'])

    def test_modify_record_updates_timestamp(self):
        before = self.stored['last_modified']
        after = self.result['last_modified']
        self.assertNotEquals(after, before)

    def test_patch_record_returns_updated_fields(self):
        self.assertEquals(self.stored['id'], self.result['id'])
        self.assertEquals(self.result['position'], 10)

    def test_record_timestamp_is_not_updated_if_none_for_missing_field(self):
        self.resource.request.json = {'data': {'polo': None}}
        result = self.resource.patch()['data']
        self.assertEquals(self.result['last_modified'],
                          result['last_modified'])

    def test_record_timestamp_is_not_updated_if_no_field_changed(self):
        self.resource.request.json = {'data': {'position': 10}}
        result = self.resource.patch()['data']
        self.assertEquals(self.result['last_modified'],
                          result['last_modified'])

    def test_collection_timestamp_is_not_updated_if_no_field_changed(self):
        self.resource.request.json = {'data': {'position': 10}}
        self.resource.patch()
        self.resource = BaseResource(request=self.get_request(),
                                     context=self.get_context())
        self.resource.collection_get()['data']
        last_modified = int(self.last_response.headers['ETag'][1:-1])
        self.assertEquals(self.result['last_modified'], last_modified)

    def test_timestamp_is_not_updated_if_no_change_after_preprocessed(self):
        with mock.patch.object(self.resource, 'process_record') as mocked:
            mocked.return_value = self.result
            self.resource.request.json = {'data': {'position': 20}}
            result = self.resource.patch()['data']
            self.assertEquals(self.result['last_modified'],
                              result['last_modified'])

    def test_returns_changed_fields_among_provided_if_behaviour_is_diff(self):
        self.resource.request.json = {'data': {'unread': True, 'position': 15}}
        self.resource.request.headers['Response-Behavior'] = 'diff'
        with mock.patch.object(self.resource.collection, 'update_record',
                               return_value={'unread': True, 'position': 0}):
            result = self.resource.patch()['data']
        self.assertDictEqual(result, {'position': 0})

    def test_returns_changed_fields_if_behaviour_is_light(self):
        self.resource.request.json = {'data': {'unread': True, 'position': 15}}
        self.resource.request.headers['Response-Behavior'] = 'light'
        with mock.patch.object(self.resource.collection, 'update_record',
                               return_value={'unread': True, 'position': 0}):
            result = self.resource.patch()['data']
        self.assertDictEqual(result, {'unread': True, 'position': 0})
Esempio n. 9
0
class SinceModifiedTest(ThreadMixin, BaseTest):

    def setUp(self):
        super(SinceModifiedTest, self).setUp()

        with mock.patch.object(self.db, '_bump_timestamp') as msec_mocked:
            for i in range(6):
                msec_mocked.return_value = i
                self.resource.collection_post()
                self.resource.request.validated = {}  # reset next

    def test_filter_with_since_is_exclusive(self):
        self.resource.request.GET = {'_since': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 2)

    def test_filter_with_to_is_exclusive(self):
        self.resource.request.GET = {'_to': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 3)

    def test_the_timestamp_header_is_equal_to_last_modification(self):
        result = self.resource.collection_post()
        modification = result['last_modified']

        self.resource = BaseResource(self.get_request())
        self.resource.collection_get()
        header = int(self.last_response.headers['Last-Modified'])
        self.assertEqual(header, modification)

    def test_filter_with_since_accepts_numeric_value(self):
        self.resource.request.GET = {'_since': '6'}
        self.resource.collection_post()
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 1)

    def test_filter_with_since_rejects_non_numeric_value(self):
        self.resource.request.GET = {'_since': 'abc'}
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_filter_with_since_rejects_decimal_value(self):
        self.resource.request.GET = {'_since': '1.2'}
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_filter_from_last_modified_is_exclusive(self):
        result = self.resource.collection_post()
        current = result['last_modified']

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 0)

    def test_filter_with_last_modified_includes_deleted_items(self):
        self.resource.collection_post()
        result = self.resource.collection_post()
        current = result['last_modified']

        self.resource.record_id = result['id']
        self.resource.delete()

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 1)
        self.assertTrue(result['items'][0]['deleted'])

    def test_filter_from_last_header_value_is_exclusive(self):
        result = self.resource.collection_get()
        current = int(self.last_response.headers['Last-Modified'])

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 0)

    def test_filter_works_with_empty_list(self):
        self.resource.db_kwargs['user_id'] = 'alice'
        self.resource.request.GET = {'_since': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['items']), 0)

    def test_timestamp_are_always_identical_on_read(self):

        def read_timestamp():
            self.resource.collection_get()
            return int(self.last_response.headers['Last-Modified'])

        before = read_timestamp()
        now = read_timestamp()
        after = read_timestamp()
        self.assertEqual(before, now)
        self.assertEqual(now, after)

    def test_timestamp_are_always_incremented_on_creation(self):

        def read_timestamp():
            record = self.resource.collection_post()
            return record['last_modified']

        before = read_timestamp()
        now = read_timestamp()
        after = read_timestamp()
        self.assertTrue(before < now < after)

    def test_records_created_during_fetch_are_above_fetch_timestamp(self):

        timestamps = {}

        def long_fetch():
            """Simulate a overhead while reading on storage."""

            def delayed_get(*args, **kwargs):
                time.sleep(.100)  # 100 msec
                return [], 0

            with mock.patch.object(self.db, 'get_all', delayed_get):
                self.resource.collection_get()
                fetch_at = self.last_response.headers['Last-Modified']
                timestamps['fetch'] = int(fetch_at)

        # Create a real record with no patched timestamp
        self.resource.collection_post()

        # Some client start fetching
        thread = self._create_thread(target=long_fetch)
        thread.start()

        # Create record while other is fetching
        time.sleep(.020)  # 20 msec
        record = self.resource.collection_post()
        timestamps['post'] = record['last_modified']

        # Wait for the fetch to finish
        thread.join()

        # Make sure fetch timestamp is below (for next fetch)
        self.assertTrue(timestamps['post'] > timestamps['fetch'])
Esempio n. 10
0
class SinceModifiedTest(ThreadMixin, BaseTest):

    def setUp(self):
        super(SinceModifiedTest, self).setUp()

        self.resource.request.validated = {'data': {}}

        with mock.patch.object(self.collection.storage,
                               '_bump_timestamp') as msec_mocked:
            for i in range(6):
                msec_mocked.return_value = i
                self.resource.collection_post()

    def test_filter_with_since_is_exclusive(self):
        self.resource.request.GET = {'_since': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 2)

    def test_filter_with__to_is_exclusive(self):
        self.resource.request.GET = {'_to': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 3)

    def test_filter_with__before_is_exclusive(self):
        self.resource.request.GET = {'_before': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 3)

    def test_filter_with__to_return_an_alert_header(self):
        self.resource.request.GET = {'_to': '3'}
        self.resource.collection_get()
        self.assertIn('Alert', self.resource.request.response.headers)
        alert = self.resource.request.response.headers['Alert']
        self.assertDictEqual(
            decode_header(json.loads(alert)),
            {
                'code': 'soft-eol',
                'message': ('_to is now deprecated, '
                            'you should use _before instead'),
                'url': ('http://cliquet.rtfd.org/en/2.4.0/api/resource'
                        '.html#list-of-available-url-parameters')
            })

    def test_the_timestamp_header_is_equal_to_last_modification(self):
        result = self.resource.collection_post()['data']
        modification = result['last_modified']
        self.resource = BaseResource(request=self.get_request(),
                                     context=self.get_context())
        self.resource.collection_get()
        header = int(self.last_response.headers['ETag'][1:-1])
        self.assertEqual(header, modification)

    def test_filter_with_since_accepts_numeric_value(self):
        self.resource.request.GET = {'_since': '6'}
        self.resource.collection_post()
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 1)

    def test_filter_with_since_rejects_non_numeric_value(self):
        self.resource.request.GET = {'_since': 'abc'}
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_filter_with_since_rejects_decimal_value(self):
        self.resource.request.GET = {'_since': '1.2'}
        self.assertRaises(httpexceptions.HTTPBadRequest,
                          self.resource.collection_get)

    def test_filter_from_last_modified_is_exclusive(self):
        result = self.resource.collection_post()['data']
        current = result['last_modified']

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 0)

    def test_filter_with_last_modified_includes_deleted_data(self):
        self.resource.collection_post()
        result = self.resource.collection_post()['data']
        current = result['last_modified']

        self.resource.record_id = result['id']
        self.resource.delete()

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 1)
        self.assertTrue(result['data'][0]['deleted'])

    def test_filter_from_last_header_value_is_exclusive(self):
        self.resource.collection_get()
        current = int(self.last_response.headers['ETag'][1:-1])

        self.resource.request.GET = {'_since': six.text_type(current)}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 0)

    def test_filter_works_with_empty_list(self):
        self.resource.collection.parent_id = 'alice'
        self.resource.request.GET = {'_since': '3'}
        result = self.resource.collection_get()
        self.assertEqual(len(result['data']), 0)

    def test_timestamp_are_always_identical_on_read(self):

        def read_timestamp():
            self.resource.collection_get()
            return int(self.last_response.headers['ETag'][1:-1])

        before = read_timestamp()
        now = read_timestamp()
        after = read_timestamp()
        self.assertEqual(before, now)
        self.assertEqual(now, after)

    def test_timestamp_are_always_incremented_on_creation(self):

        def read_timestamp():
            record = self.resource.collection_post()['data']
            return record['last_modified']

        before = read_timestamp()
        now = read_timestamp()
        after = read_timestamp()
        self.assertTrue(before < now < after)

    def test_records_created_during_fetch_are_above_fetch_timestamp(self):

        timestamps = {}

        def long_fetch():
            """Simulate a overhead while reading on storage."""

            def delayed_get(*args, **kwargs):
                time.sleep(.100)  # 100 msec
                return [], 0

            with mock.patch.object(self.collection.storage,
                                   'get_all', delayed_get):
                self.resource.collection_get()
                fetch_at = self.last_response.headers['ETag'][1:-1]
                timestamps['fetch'] = int(fetch_at)

        # Create a real record with no patched timestamp
        self.resource.collection_post()

        # Some client start fetching
        thread = self._create_thread(target=long_fetch)
        thread.start()

        # Create record while other is fetching
        time.sleep(.020)  # 20 msec
        record = self.resource.collection_post()['data']
        timestamps['post'] = record['last_modified']

        # Wait for the fetch to finish
        thread.join()

        # Make sure fetch timestamp is below (for next fetch)
        self.assertTrue(timestamps['post'] > timestamps['fetch'])