ENVIRONMENT = os.getenv('ENVIRONMENT', 'dev')
class BaseModel(Model):
    class Meta:
class Item(BaseModel):
class Picture(BaseModel):
class User(BaseModel, UserMixin):
class Address(BaseModel):
class Order(BaseModel):
    class Meta:
class OrderItem(BaseModel):
class Favorite(BaseModel):
    def save(self, *args, **kwargs):
    def json_list(cls, objs_list):
    def json(self, include_data=[]):
    def validate_input(cls, data, partial=False):
    def search(cls, query, dataset, limit=-1,
               attributes=None, weights=None,
               threshold=search.config.THRESHOLD):
    def __str__(self):
    def is_favorite(self, item):
def on_delete_item_handler(model_class, instance):
    def filename(self):
    def __str__(self):
def on_delete_picture_handler(model_class, instance):
    def exists(email):
    def hash_password(password):
    def verify_password(self, password):
    def add_favorite(user, item):
    def delete_favorite(self, obj):
    def order_items(self):
    def empty_order(self):
    def create_order(user, address, items):
    def update_items(self, items, update_total=True, new_address=None):
    def edit_items_quantity(self, items):
    def delete_items(self, items):
    def create_items(self, items):
    def add_item(self, item, quantity=1):
    def remove_item(self, item, quantity=1):
    def add_item(self, quantity=1):
    def remove_item(self, quantity=1):
    def _calculate_subtotal(self): class TestCase:
    """
    Created TestCase to avoid duplicated code in the other tests
    """
    TEST_DB = SqliteDatabase(':memory:')

    @classmethod
    def setup_class(cls):
        """
        When a Test is created override the ``database`` attribute for all
        the tables with a SqliteDatabase in memory.
        """
        for table in TABLES:
            table._meta.database = cls.TEST_DB
            table.create_table(fail_silently=True)

        cls.app = app.test_client()

    def setup_method(self):
        """
        When setting up a new test method clear all the tables
        """
        for table in TABLES:
            table.delete().execute() RESULTS = json.load(fo) def add_address(user, country='Italy', city='Pistoia', post_code='51100',
                address='Via Verdi 12', phone='3294882773', id=None):

    return Address.create(
        uuid=id or uuid.uuid4(),
        user=user,
        country=country,
        city=city,
        post_code=post_code,
        address=address,
        phone=phone,
    ) def add_admin_user(email, psw, id=None):
    """
    Create a single user in the test database.
    If an email is provided it will be used, otherwise it will be generated
    by the function before adding the User to the database.
    """
    email = email or 'johndoe{}@email.com'.format(int(random.random() * 100))

    return User.create(
        first_name='John Admin',
        last_name='Doe',
        email=email,
        password=User.hash_password(psw),
        uuid=id or uuid.uuid4(),
        admin=True,
    ) def add_user(email, password, id=None, first_name='John', last_name='Doe'):
    """
    Create a single user in the test database.
    If an email is provided it will be used, otherwise it will be generated
    by the function before adding the User to the database.
    """
    email = email or 'johndoe{}@email.com'.format(int(random.random() * 100))

    return User.create(
        first_name=first_name,
        last_name=last_name,
        email=email,
        password=User.hash_password(password),
        uuid=id or uuid.uuid4(),
    ) def count_order_items(order):
    """ Given an Order instance count the total items in the order. """
    tot = 0
    for oi in order.order_items:
        tot += oi.quantity
    return tot def format_jsonapi_request(type_, data):
    """
    Given the attributes and relationships of a resource, compile the jsonapi
    post data for the request.
    All key - value pairs except ``relationships`` will be mapped inside
    ``['data']['attributes']`` of the request.
    Relationships key value will be mapped inside ``['data']['relationship']``

    .. NOTE::
        All relationship **must** map to the related field inside the Schema of
        the type and have a ``type`` and ``id`` properties.

    .. code-block:: python

        data = {
            "<attribute field": "bar"
            "relationships": {
                "<field_name>": {
                    "type": "<Resource type_>",
                    "id": < Resource id >
                },
                "<field_name>": [
                    {
                        "type": "item",
                        "id": < Resource id >,
                        "<metadata>": < metadata_value(ie. quantity of items) >
                    }
                ]
            }
        }

    """
    rels = {}
    if 'relationships' in data:
        rels = {k: {'data': v} for k, v in data['relationships'].items()}
        del data['relationships']

    retval = {
        'data': {
            'type': type_,
            'attributes': data,
            'relationships': rels
        }
    }
    return retval def open_with_auth(app, url, method, username, password, content_type, data):
    """
    Generic call to app for http request, required for requests that need
    to send a ``Basic Auth`` request to the server.
    """

    AUTH_TYPE = 'Basic'
    bytes_auth = bytes('{}:{}'.format(username, password), 'ascii')
    auth_str = '{} {}'.format(
        AUTH_TYPE, b64encode(bytes_auth).decode('ascii'))

    return app.open(url,
                    method=method,
                    headers={'Authorization': auth_str},
                    content_type=content_type,
                    data=data) def assert_valid_response(data, expected):
    """
    Take a flask app response.data and the expected test result, normalize them
    sorting the `included` and `errors` lists if present, then assert their
    equality
    """
    def sort_data_lists(data, attribute, key):
        """
        sort a given data structure's attribute(list) using the given key function
        """
        try:
            data[attribute] = sorted(data[attribute], key=key)
        except KeyError:
            pass

    # sort functions for included and errors attributes
    def included_sorter(i): return i['type']

    def errors_sorter(e): return e['source']['pointer']

    try:
        # lazy load of the response data, so we can pass either the parsed json
        # response or the json string
        data = json.loads(data)
    except TypeError:
        # data has already been parsed
        pass

    # ensure that both `data` and `expected` are lists while working on them
    data_items = data if isinstance(data, list) else [data]
    expected_items = expected if isinstance(expected, list) else [expected]

    for item in data_items:
        # Sort the included and errors lists of the response.data if present
        sort_data_lists(item, 'included', included_sorter)
        sort_data_lists(item, 'errors', errors_sorter)

    for item in expected_items:
        # Sort the lists of the expected results if present
        sort_data_lists(item, 'included', included_sorter)
        sort_data_lists(item, 'errors', errors_sorter)

    assert data == expected def wrong_dump(data):
    """
    Give a wrong encoding(urlencode - like) to the given dictionary
    """
    return reduce(lambda x, y: "{}&{}".format(x, y), [
        "{}={}".format(k, v) for k, v in zip(data.keys(), data.values())])
import json
import pytest
from http.client import (BAD_REQUEST, CREATED, NO_CONTENT, NOT_FOUND, OK,
                         UNAUTHORIZED)
from uuid import uuid4
from models import Item, Order, OrderItem, WrongQuantity
from tests.test_case import TestCase
from tests.test_utils import (RESULTS, add_address, add_admin_user, add_user,
                              count_order_items, format_jsonapi_request,
                              open_with_auth, assert_valid_response,
                              wrong_dump)
                    'type': 'address',
                    'id': '284ac7f6-40c2-4da6-b722-5d8cd248b1cc'
                },
                'user': {
                    'type': 'user',
                    'id': '90c3e1c1-b51c-4224-b69d-17f84f6a8dfc'
                }
            }
        }
        post_data = format_jsonapi_request('order', post_data)
        path = 'orders/{}'.format(order.uuid)
        resp = open_with_auth(self.app, API_ENDPOINT.format(path), 'PATCH',
                              user.email, TEST_USER_PSW, 'application/json',
                              json.dumps(post_data))

        expected_result = EXPECTED_RESULTS['update_order__success']

        assert resp.status_code == OK
        assert_valid_response(resp.data, expected_result)

    def test_update_order__non_existing_items(self):
        item1 = Item.create(
            uuid='429994bf-784e-47cc-a823-e0c394b823e8',
            name='mario',
            price=20.20,
            description='svariati mariii',
            availability=2,
            category='scarpe',
        )
        item2 = Item.create(
            uuid='577ad826-a79d-41e9-a5b2-7955bcf03499',
            name='GINO',
            price=30.20,
            description='svariati GINIIIII',
            availability=1,
            category='accessori',
        )

        user = add_user('12345@email.com', TEST_USER_PSW)
        addr = add_address(user=user)
        add_address(user=user, id='429994bf-784e-47cc-a823-e0c394b823e8')

        order1 = Order.create(delivery_address=addr, user=user)
        order1.add_item(item1, 2).add_item(item2)
        order_item_before = [o.json() for o in OrderItem.select()]
        # order_uuid = str(order1.uuid)

        order = {
            'relationships': {
                'items': [
                    {'id': '577ad826-a79d-41e9-a5b2-7955bcf00000',
                     'type': 'item', 'quantity': 1},
                    {'id': '577ad826-a79d-41e9-a5b2-7955bcf2222',
                     'type': 'item', 'quantity': 1},
                    {'id': '577ad826-a79d-41e9-a5b2-7955bcf9999',
                     'type': 'item', 'quantity': 2}
                ],
                'delivery_address': {
                    'type': 'address',
                    'id': '429994bf-784e-47cc-a823-e0c394b823e8'
                },
                'user': {
                    'type': 'user',
                    'id': '86ba7e70-b3c0-4c9c-8d26-a14f49360e47'
                }
            }
        }
        data = format_jsonapi_request('order', order)
        path = 'orders/{}'.format(order1.uuid)
        resp = open_with_auth(self.app, API_ENDPOINT.format(path), 'PATCH',
                              user.email, TEST_USER_PSW, 'application/json',
                              json.dumps(data))
        order_item_after = [o.json() for o in OrderItem.select()]
        assert resp.status_code == BAD_REQUEST
        assert order_item_before == order_item_after

    def test_update_order__non_existing_address(self):
        item1 = Item.create(
            uuid='429994bf-784e-47cc-a823-e0c394b823e8',
            name='mario',
            price=20.20,
            description='svariati mariii',
            availability=2,
            category='scarpe',
        )
        item2 = Item.create(
            uuid='577ad826-a79d-41e9-a5b2-7955bcf03499',
            name='GINO',
            price=30.20,
            description='svariati GINIIIII',
            availability=1,
            category='accessori',
        )

        user = add_user('12345@email.com', TEST_USER_PSW)
        addr_A = add_address(user=user)

        order1 = Order.create(
            delivery_address=addr_A, user=user
        ).add_item(item1, 2).add_item(item2)
        order_item_before = order1.order_items

        order = {
            'relationships': {
                'items': [
                    {'id': '577ad826-a79d-41e9-a5b2-7955bcf00000',
                     'type': 'item', 'quantity': 1},
                    {'id': '577ad826-a79d-41e9-a5b2-7955bcf2222',
                     'type': 'item', 'quantity': 1},
                ],
                'delivery_address': {
                    'type': 'address',
                    'id': '817c8747-dfb7-4c2d-8a24-82dae23d250b',
                },
                'user': {
                    'type': 'user',
                    'id': str(user.uuid),
                }
            }
        }

        data = format_jsonapi_request('order', order)