# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# https://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import json

from botocore.stub import Stubber

import boto3
from tests import unittest


class TestTableResourceCustomizations(unittest.TestCase):

    maxDiff = None

    def setUp(self):
        self.resource = boto3.resource('dynamodb', 'us-east-1')
        self.table = self.resource.Table('mytable')

    def test_resource_has_batch_writer_added(self):
        assert hasattr(self.table, 'batch_writer')

    def test_operation_without_output(self):
        stubber = Stubber(self.table.meta.client)
        stubber.add_response('tag_resource', {})
        arn = 'arn:aws:dynamodb:us-west-2:123456789:table/mytable'

        with stubber:
            self.table.meta.client.tag_resource(
                ResourceArn=arn, Tags=[{'Key': 'project', 'Value': 'val'}]
            )

        stubber.assert_no_pending_responses()

    def test_batch_write_does_not_double_serialize(self):
        # If multiple items reference the same Python object, the
        # object does not get double-serialized.
        # https://github.com/boto/boto3/issues/3474

        used_twice = {'pkey': 'foo1', 'otherfield': {'foo': 1, 'bar': 2}}
        batch_writer = self.table.batch_writer()

        # The default Stubber compares the request payload to the
        # "expected_params" before automatic serialization happens. This custom
        # event handler uses the same technique as the Stubber to record the
        # serialized request body, but later in the request lifecycle.
        class LateStubber:
            def __init__(self, client):
                self.intercepted_request_body = None
                client.meta.events.register_first(
                    'before-call.*.*',
                    self.late_request_interceptor,
                )

            def late_request_interceptor(self, event_name, params, **kwargs):
                if self.intercepted_request_body is not None:
                    raise AssertionError(
                        'LateStubber was called more than once, but only one '
                        'request is expected'
                    )
                body_str = params.get('body', b'').decode('utf-8')
                try:
                    self.intercepted_request_body = json.loads(body_str)
                except Exception:
                    raise AssertionError(
                        'Expected JSON request body, but failed to JSON decode'
                    )

        late_stubber = LateStubber(self.table.meta.client)

        with Stubber(self.table.meta.client) as stubber:
            stubber.add_response(
                'batch_write_item',
                service_response={'UnprocessedItems': {}},
            )
            batch_writer.put_item(Item=used_twice)
            batch_writer.put_item(Item=used_twice)
            batch_writer._flush()

        expected_request_body = {
            'RequestItems': {
                'mytable': [
                    {
                        'PutRequest': {
                            'Item': {
                                'pkey': {'S': 'foo1'},
                                'otherfield': {
                                    'M': {'foo': {'N': '1'}, 'bar': {'N': '2'}}
                                },
                            }
                        }
                    },
                    {
                        'PutRequest': {
                            'Item': {
                                'pkey': {'S': 'foo1'},
                                'otherfield': {
                                    'M': {'foo': {'N': '1'}, 'bar': {'N': '2'}}
                                },
                            }
                        }
                    },
                ]
            }
        }

        assert late_stubber.intercepted_request_body == expected_request_body
