def test_is_empty_when_directly_instantiated(self):
     r = Response(ident='abc',
                  code=404,
                  payload=None,
                  generated_datetime=datetime.utcnow().replace(
                      tzinfo=timezone.utc).isoformat(),
                  response_hash=hashlib.sha1(
                      'fake-json'.encode()).hexdigest())
     assert r.is_empty()
Beispiel #2
0
    def test_get_lpa_by_sirius_uid_is_proxied_no_cache(self, mock_boto3):

        test_id = '700012348745'

        mock_sirius_provider = mock.MagicMock()

        test_response = Response.factory(ident='700098765432',
                                         code=200,
                                         payload_json=json.dumps(
                                             {'Testing': 'One'}))

        mock_sirius_provider.get_lpa_by_sirius_uid.return_value = test_response

        # ---

        wrapper = CacheProviderWrapper(mock_sirius_provider)

        result = wrapper.get_lpa_by_sirius_uid(test_id)

        assert isinstance(result, Response)
        assert result is test_response

        # Check the request was passed on to the real data provider
        mock_sirius_provider.get_lpa_by_sirius_uid.assert_called_once_with(
            test_id)
Beispiel #3
0
    def test_updating_existing_cache_item(self, mock_datetime, mock_boto3):
        """
        If an item is already in the cache, only metadata will be updated. i.e. not the payload.
        """

        # This is used to determine the expiry timestamp DynamoDB should expect.
        zeroed_timestamp = datetime.datetime.fromtimestamp(
            0)  # Set now().timestamp() to 0
        mock_datetime.now.return_value = zeroed_timestamp

        # ---

        # Response the data provider will return

        test_response = Response.factory(ident='700098765432',
                                         code=200,
                                         payload_json=json.dumps(
                                             {'Testing': 'One'}))

        mock_sirius_provider = mock.MagicMock()
        mock_sirius_provider.get_lpa_by_lpa_online_tool_id.return_value = test_response

        # ---

        # Cache response. If the new hash matches, it'll be considered an update.

        mock_boto3.resource.return_value.Table.return_value.get_item.return_value = {
            'Item': {
                'payload': json.dumps({'Testing': 'Two'}),
                'response_hash':
                test_response.response_hash,  # Note we ensure the hashes match
                'cached': '2019-04-17T20:08:50.349878+00:00',
                'response_code': 200,
            }
        }

        # ---

        wrapper = CacheProviderWrapper(mock_sirius_provider)

        result = wrapper.get_lpa_by_lpa_online_tool_id(test_response.ident)

        assert isinstance(result, Response)

        # ---

        # Expect only metadata fields.
        expression_attribute_values = {
            ':datatime':
            test_response.generated_datetime,
            ':expires':
            math.floor((zeroed_timestamp +
                        CacheProviderWrapper.CACHE_TTL).timestamp()),
        }

        mock_boto3.resource.return_value.Table.return_value.update_item.assert_called_once_with(
            Key={'id': test_response.ident},
            UpdateExpression='SET cached=:datatime, expires=:expires',
            ExpressionAttributeValues=expression_attribute_values)
Beispiel #4
0
    def test_successful_cache_and_provider_lookup(self, mock_boto3):

        mock_sirius_provider = mock.MagicMock()

        test_response = Response.factory(ident='700098765432',
                                         code=200,
                                         payload_json=json.dumps(
                                             {'Testing': 'One'}))

        # The real provider will return a response. We should see this returned, not the cached version.
        mock_sirius_provider.get_lpa_by_sirius_uid.return_value = test_response

        # ---

        test_id = '700012348745'
        payload = {'Testing': 'Two'}
        response_hash = 'xxx'
        cached = '2019-04-17T20:08:50.349878+00:00'

        mock_boto3.resource.return_value.Table.return_value.get_item.return_value = {
            'Item': {
                'payload': json.dumps(payload),
                'response_hash': response_hash,
                'cached': cached,
            }
        }

        # ---

        wrapper = CacheProviderWrapper(mock_sirius_provider)

        result = wrapper.get_lpa_by_sirius_uid(test_id)

        # We should see the response that the cache returned
        assert isinstance(result, Response)

        # Response should match the one from the real data provider
        assert result.ident == test_response.ident
        assert result.payload == test_response.payload
        assert result.response_hash == test_response.response_hash
        assert result.generated_datetime == test_response.generated_datetime

        # And not the one from the cache
        assert not result.ident == test_id
        assert not result.payload == payload
        assert not result.response_hash == response_hash
        assert not result.generated_datetime == cached

        # These should have been called
        mock_boto3.resource.return_value.Table.return_value.get_item.assert_called_once(
        )
        mock_sirius_provider.get_lpa_by_sirius_uid.assert_called_once_with(
            test_id)
Beispiel #5
0
    def test_adding_new_cache_item(self, mock_datetime, mock_boto3):
        """
        If an item isn't already in the cache, the whole response should be inserted.
        """

        # This is used to determine the expiry timestamp DynamoDB should expect.
        zeroed_timestamp = datetime.datetime.fromtimestamp(
            0)  # Set now().timestamp() to 0
        mock_datetime.now.return_value = zeroed_timestamp

        # ---

        mock_sirius_provider = mock.MagicMock()

        test_response = Response.factory(ident='700098765432',
                                         code=200,
                                         payload_json=json.dumps(
                                             {'Testing': 'One'}))

        mock_sirius_provider.get_lpa_by_lpa_online_tool_id.return_value = test_response

        wrapper = CacheProviderWrapper(mock_sirius_provider)

        result = wrapper.get_lpa_by_lpa_online_tool_id(test_response.ident)

        assert isinstance(result, Response)

        # ---

        # Expect all fields.
        expression_attribute_values = {
            ':datatime':
            test_response.generated_datetime,
            ':expires':
            math.floor((zeroed_timestamp +
                        CacheProviderWrapper.CACHE_TTL).timestamp()),
            ':code':
            200,
            ':hash':
            test_response.response_hash,
            ':payload':
            json.dumps(test_response.payload),
        }

        mock_boto3.resource.return_value.Table.return_value.update_item.assert_called_once_with(
            Key={'id': test_response.ident},
            UpdateExpression=
            'SET cached=:datatime, expires=:expires, response_code=:code, response_hash=:hash, payload=:payload',
            ExpressionAttributeValues=expression_attribute_values)
Beispiel #6
0
    def test_handle_returning_a_valid_response(self):
        """
        We expect our response object to be returned, with the relevant headers.
        """
        test_datetinme = '2019-05-20T10:00:40.513493+00:00'

        input = Response(ident='1',
                         code=200,
                         payload={'Test': True},
                         generated_datetime=test_datetinme,
                         response_hash='xxx')

        # Mock the handle function
        mock_handler = mock.MagicMock()
        mock_handler.return_value = input
        HandlerBase.handle = mock_handler

        response = HandlerBase.get_handler()({
            'resource': 'test',
            'pathParameters': 'test'
        }, {})

        assert isinstance(response, dict)
        assert 'statusCode' in response and response['statusCode'] is 200
        assert 'body' in response and response['body'] == json.dumps(
            input.payload)
        assert 'headers' in response and 'Age' in response[
            'headers'] and 'Date' in response['headers']
        assert response['headers']['Date'] == 'Mon, 20 May 2019 10:00:40 GMT'

        # Age will vary so we need to calculator it
        date = datetime.fromisoformat(test_datetinme)
        age = (datetime.utcnow().replace(tzinfo=timezone.utc) - date).seconds

        assert isinstance(response['headers']['Age'],
                          int) and response['headers']['Age'] > 0
        assert age - 2 <= response['headers'][
            'Age'] <= age + 2  # Allow some wiggle room
    def _lookup_in_cache(self, cache_id):
        """
        Looks up the given `cache_id` in DynamoDB

        :return: Response
        """
        response = self.cache_table.get_item(Key={'id': cache_id})

        if 'Item' in response:
            response = response['Item']

            if 'payload' in response \
                    and 'response_hash' in response \
                    and 'cached' in response \
                    and 'response_code' in response:

                return Response(
                    ident=cache_id,
                    code=response['response_code'],
                    response_hash=response['response_hash'],
                    generated_datetime=response['cached'],
                    payload=json.loads(response['payload']),
                )
 def test_is_empty_when_it_is_not_emoty(self):
     r = Response.factory('123', 200, json.dumps([{"test": True}]))
     assert not r.is_empty()
 def test_is_empty_when_empty_with_dict(self):
     r = Response.factory('123', 200, json.dumps({}))
     assert r.is_empty()
 def test_is_empty_when_empty_with_none(self):
     r = Response.factory('123', 404, json.dumps(None))
     assert r.is_empty()