class SyncOrdersTests(unittest.TestCase):
    """All methods that associated with placing and cancelling orders in cycle."""
    def setUp(self) -> None:
        self.exchange_mock = Mock()
        self.exchange_mock.get_open_orders_ws.return_value = []
        self.exchange_mock.get_position_size_ws.return_value = 0
        self.supervisor = Supervisor(interface=self.exchange_mock)

    def tearDown(self) -> None:
        self.supervisor.exit_cycle()

    def test_cancel_needless_order(self):
        order1 = Order()
        self.exchange_mock.get_open_orders_ws.return_value = [order1]
        expected_orders = [order1]
        self.supervisor.cancel_needless_orders()

        # assert that Supervisor try to cancel needless order
        self.exchange_mock.bulk_cancel_orders.assert_called_once_with(
            expected_orders)

    def test_move_changed_order(self):
        """Test that Supervisor will amend order, not cancel it."""

        order1 = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order1_copy = Order(order_type='Limit',
                            qty=228,
                            price=1000,
                            side='Buy')
        self.supervisor.add_order(order1)

        self.exchange_mock.get_open_orders_ws.return_value = [order1_copy]
        order1.move(to=1001)

        self.supervisor.cancel_needless_orders()

        self.exchange_mock.bulk_cancel_orders.assert_not_called()
        self.exchange_mock.move_order.assert_called_once_with(order=order1)

    def test_cancel_several_needless_orders(self):
        order1 = Order()
        order2 = Order()
        order3 = Order()
        self.exchange_mock.get_open_orders_ws.return_value = [
            order1, order2, order3
        ]
        expected_orders = [order1, order2, order3]
        self.supervisor.cancel_needless_orders()

        # assert that Supervisor try to cancel needless order
        self.exchange_mock.bulk_cancel_orders.assert_called_once_with(
            expected_orders)

    def test_cancel_duplicates(self):
        order1 = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order2 = Order(order_type='Limit', qty=229, price=1001, side='Buy')
        self.exchange_mock.get_open_orders_ws.return_value = [
            order1, order2, order2
        ]
        self.supervisor.add_order(order1)
        self.supervisor.add_order(order2)
        expected_orders = [order2]
        self.supervisor.cancel_needless_orders()

        # assert that Supervisor try to cancel needless order
        self.exchange_mock.bulk_cancel_orders.assert_called_once_with(
            expected_orders)

    def test_cancel_two_same_needed_orders(self):
        order1 = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order2 = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order3 = Order(order_type='Limit', qty=229, price=1001, side='Buy')
        self.exchange_mock.get_open_orders_ws.return_value = [order1, order2]
        self.supervisor.add_order(order1)
        self.supervisor.add_order(order2)
        self.supervisor.add_order(order3)
        self.supervisor.cancel_needless_orders()
        self.exchange_mock.bulk_cancel_orders.assert_not_called()

    def test_check_unplaced_order(self):
        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        self.exchange_mock.place_order.assert_called_once_with(order)

    def test_check_several_unplaced_orders(self):
        order1 = Order(order_type='Limit', qty=228, price=1001, side='Buy')
        order2 = Order(order_type='Limit', qty=229, price=1002, side='Buy')
        order3 = Order(order_type='Limit', qty=2210, price=1003, side='Buy')
        self.supervisor.add_order(order1)
        self.supervisor.add_order(order2)
        self.supervisor.add_order(order3)
        self.supervisor.check_needed_orders()

        self.exchange_mock.bulk_place_orders.assert_called_once_with(
            [order1, order2, order3])

    def test_check_rejected_order(self):
        def order_status_mock(_order):
            if _order == order:
                return 'Rejected'

        on_reject_mock = Mock()

        self.exchange_mock.get_order_status_ws.side_effect = order_status_mock

        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order.order_id = '1234'
        order._on_reject = on_reject_mock
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that we didn`t place this order
        self.exchange_mock.place_order.not_called(order)
        # assert that Supervisor forget this order
        self.assertNotIn(order, self.supervisor.orders)
        # assert that Supervisor call matching callback
        on_reject_mock.assert_called_once()

    def test_check_filled_order(self):
        def order_status_mock(_order):
            if _order == order:
                return 'Filled'

        on_filled_mock = Mock()

        self.exchange_mock.get_order_status_ws.side_effect = order_status_mock

        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order.order_id = '1234'
        order._on_fill = on_filled_mock
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that we didn`t place this order
        self.exchange_mock.place_order.not_called(order)
        # assert that Supervisor forget this order
        self.assertNotIn(order, self.supervisor.orders)
        # assert that Supervisor call matching callback
        on_filled_mock.assert_called_once()

    def test_check_filled_stop_order(self):
        def order_status_mock(_order):
            if _order == order:
                return 'Triggered'

        on_filled_mock = Mock()

        self.exchange_mock.get_order_status_ws.side_effect = order_status_mock

        order = Order(order_type='Stop', qty=228, stop_px=1000, side='Buy')
        order.order_id = '1234'
        order._on_fill = on_filled_mock
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that we didn`t place this order
        self.exchange_mock.place_order.not_called(order)
        # assert that Supervisor forget this order
        self.assertNotIn(order, self.supervisor.orders)
        # assert that Supervisor call matching callback
        on_filled_mock.assert_called_once()

    def test_validation_error_while_placing_order(self):
        validation_error = requests.HTTPError()
        validation_error.response = Mock()
        validation_error.response.text = 'Order price is above the liquidation price of current'
        self.exchange_mock.place_order.side_effect = validation_error

        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that method has been called
        self.exchange_mock.place_order.assert_called_once_with(order)
        # assert that we catch the exception and forget the order
        self.assertNotIn(order, self.supervisor.orders)