mirror of
https://github.com/Blockstream/satellite-api.git
synced 2025-03-13 11:35:20 +01:00
Use the tx_confirmations/rx_confirmations order field populated automatically (lazily) by the model.
1159 lines
46 KiB
Python
1159 lines
46 KiB
Python
import io
|
|
import os
|
|
import pytest
|
|
from http import HTTPStatus
|
|
from unittest.mock import patch
|
|
from uuid import uuid4
|
|
|
|
from constants import InvoiceStatus, OrderStatus
|
|
from database import db
|
|
from error import assert_error, get_http_error_resp
|
|
from models import Invoice, Order, RxConfirmation
|
|
from order_helpers import adjust_bids, _paid_invoices_total,\
|
|
_unpaid_invoices_total
|
|
from regions import Regions, SATELLITE_REGIONS
|
|
from utils import hmac_sha256_digest
|
|
import bidding
|
|
import constants
|
|
import server
|
|
|
|
from common import check_invoice, pay_invoice, check_upload, new_invoice,\
|
|
place_order, generate_test_order, rnd_string, upload_test_file
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mockredis):
|
|
app = server.create_app(from_test=True)
|
|
app.app_context().push()
|
|
with app.test_client() as client:
|
|
yield client
|
|
server.teardown_app(app)
|
|
|
|
|
|
def check_received_message(order_uuid, received_message):
|
|
path = os.path.join(constants.MSG_STORE_PATH, order_uuid)
|
|
assert os.path.exists(path)
|
|
with open(path, 'rb') as fd:
|
|
sent_message = fd.read()
|
|
assert sent_message == received_message
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_file_upload(mock_new_invoice, client):
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
|
|
rv = upload_test_file(client, msg, bid)
|
|
assert rv.status_code == HTTPStatus.OK
|
|
check_upload(rv.get_json()['uuid'], msg)
|
|
check_invoice(rv.get_json()['lightning_invoice'], rv.get_json()['uuid'])
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_text_msg_upload(mock_new_invoice, client):
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
rv = client.post('/order', data={'bid': bid, 'message': msg.encode()})
|
|
assert rv.status_code == HTTPStatus.OK
|
|
check_upload(rv.get_json()['uuid'], msg)
|
|
check_invoice(rv.get_json()['lightning_invoice'], rv.get_json()['uuid'])
|
|
|
|
|
|
def test_uploaded_file_too_small(client):
|
|
n_bytes = 0
|
|
rv = place_order(client, n_bytes)
|
|
assert_error(rv.get_json(), 'MESSAGE_FILE_TOO_SMALL')
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
# Patch the max sizes on each channel to run the test faster
|
|
@patch(
|
|
'constants.CHANNEL_INFO', {
|
|
constants.USER_CHANNEL:
|
|
constants.ChannelInfo('transmissions', ['get', 'post', 'delete'], 1000,
|
|
1000),
|
|
constants.BTC_SRC_CHANNEL:
|
|
constants.ChannelInfo('btc-src', ['get'], 500, 1500),
|
|
})
|
|
def test_uploaded_file_too_large(client):
|
|
|
|
max_size_user_chan = 1000
|
|
max_size_btc_src_chan = 1500
|
|
n_bytes = max_size_user_chan + 1
|
|
rv = place_order(client, n_bytes)
|
|
assert_error(rv.get_json(), 'MESSAGE_FILE_TOO_LARGE')
|
|
assert rv.status_code == HTTPStatus.REQUEST_ENTITY_TOO_LARGE
|
|
|
|
# The limit is different per channel. For instance, the above size should
|
|
# work on the btc-src channel.
|
|
rv = place_order(client,
|
|
n_bytes,
|
|
channel=constants.BTC_SRC_CHANNEL,
|
|
admin=True)
|
|
assert rv.status_code == HTTPStatus.OK
|
|
|
|
n_bytes = max_size_btc_src_chan + 1
|
|
rv = place_order(client, n_bytes)
|
|
assert_error(rv.get_json(), 'MESSAGE_FILE_TOO_LARGE')
|
|
assert rv.status_code == HTTPStatus.REQUEST_ENTITY_TOO_LARGE
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_uploaded_file_max_size(mock_new_invoice, client):
|
|
n_bytes = constants.DEFAULT_MAX_MESSAGE_SIZE
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bidding.get_min_bid(n_bytes)))
|
|
rv = place_order(client, n_bytes)
|
|
assert rv.status_code == HTTPStatus.OK
|
|
|
|
|
|
def test_uploaded_text_msg_too_large(client):
|
|
n_bytes = constants.MAX_TEXT_MSG_LEN + 1
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
rv = client.post('/order', data={'bid': bid, 'message': msg.encode()})
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
def test_post_order_invalid_channel(client):
|
|
n_bytes = 10
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'channel': 10
|
|
})
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
assert 'channel' in rv.get_json()
|
|
assert "Must be one of" in rv.get_json()['channel'][0]
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_uploaded_text_msg_max_size(mock_new_invoice, client):
|
|
n_bytes = constants.MAX_TEXT_MSG_LEN
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
rv = client.post('/order', data={'bid': bid, 'message': msg.encode()})
|
|
assert rv.status_code == HTTPStatus.OK
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_both_msg_and_file_provided(mock_new_invoice, client):
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'file': (io.BytesIO(msg.encode()), 'testfile')
|
|
})
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
def test_negative_bid(client):
|
|
n_bytes = 500
|
|
bid = -1
|
|
msg = rnd_string(n_bytes)
|
|
rv = upload_test_file(client, msg, bid)
|
|
assert 'bid' in rv.get_json()
|
|
assert rv.get_json()['bid'][0] == 'Must be greater than or equal to 0.'
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
def test_bid_too_low(client):
|
|
n_bytes = 1000
|
|
bid = 1051
|
|
msg = rnd_string(n_bytes)
|
|
rv = upload_test_file(client, msg, bid)
|
|
assert_error(rv.get_json(), 'BID_TOO_SMALL')
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
def test_order_without_message(client):
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
rv = client.post('/order', data={'bid': bid})
|
|
assert_error(rv.get_json(), 'MESSAGE_MISSING')
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_invoice_generation_failure(mock_new_invoice, client):
|
|
mock_new_invoice.return_value =\
|
|
(False,
|
|
get_http_error_resp('LIGHTNING_CHARGE_INVOICE_ERROR'))
|
|
n_bytes = 500
|
|
rv = place_order(client, n_bytes)
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
mock_new_invoice.return_value =\
|
|
(False,
|
|
get_http_error_resp(
|
|
'LIGHTNING_CHARGE_WEBHOOK_REGISTRATION_ERROR'
|
|
))
|
|
rv = place_order(client, n_bytes)
|
|
assert rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_order(mock_new_invoice, client):
|
|
json_response = generate_test_order(mock_new_invoice, client)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
assert get_json_resp['uuid'] == uuid
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_order_auth_token_as_form_param(mock_new_invoice, client):
|
|
json_response = generate_test_order(mock_new_invoice, client)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
get_rv = client.get(f'/order/{uuid}', data={'auth_token': auth_token})
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
assert get_json_resp['uuid'] == uuid
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_order_auth_token_as_query_param(mock_new_invoice, client):
|
|
json_response = generate_test_order(mock_new_invoice, client)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
get_rv = client.get(f'/order/{uuid}?auth_token={auth_token}')
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
assert get_json_resp['uuid'] == uuid
|
|
|
|
|
|
def test_get_nonexistent_order(client):
|
|
uuid = str(uuid4())
|
|
rv = client.get(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': 'test-auth-token'})
|
|
assert_error(rv.get_json(), 'ORDER_NOT_FOUND')
|
|
assert rv.status_code == HTTPStatus.NOT_FOUND
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_order_wrong_auth_token(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client)['uuid']
|
|
|
|
get_rv = client.get(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': 'wrong-auth-token'})
|
|
assert_error(get_rv.get_json(), 'INVALID_AUTH_TOKEN')
|
|
assert get_rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_order_missing_auth_token(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client)['uuid']
|
|
|
|
get_rv = client.get(f'/order/{uuid}')
|
|
assert_error(get_rv.get_json(), 'INVALID_AUTH_TOKEN')
|
|
assert get_rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_admin_order(mock_new_invoice, client):
|
|
# Post order on a channel that forbids users from fetching messages
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
channel=constants.AUTH_CHANNEL,
|
|
admin=True)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
# Getting through the normal route should fail
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
assert get_rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
assert_error(get_rv.get_json(), 'ORDER_CHANNEL_UNAUTHORIZED_OP')
|
|
|
|
# Getting through the admin route should work
|
|
get_rv = client.get(f'/admin/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
assert get_json_resp['uuid'] == uuid
|
|
|
|
|
|
def test_adjust_bids(client):
|
|
# if the values like bid, unpaid_bid, bid_per_byte are wrong or become
|
|
# obsolete due to changes in the invoices, the adjust_bids function should
|
|
# be able to fix them all
|
|
n_bytes = 1000
|
|
paid_bid = 1000
|
|
unpaid_bid = 10000
|
|
order = Order(id=1,
|
|
uuid='a-b-c',
|
|
bid=123,
|
|
message_size=n_bytes,
|
|
bid_per_byte=56.0,
|
|
message_digest='abcd',
|
|
status=OrderStatus.pending.value,
|
|
unpaid_bid=2345,
|
|
invoices=[
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
int(0.4 * unpaid_bid)),
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
int(0.6 * unpaid_bid)),
|
|
new_invoice(1, InvoiceStatus.paid, int(0.3 * paid_bid)),
|
|
new_invoice(1, InvoiceStatus.paid, int(0.7 * paid_bid))
|
|
])
|
|
assert _paid_invoices_total(order) == paid_bid
|
|
assert _unpaid_invoices_total(order) == unpaid_bid
|
|
adjust_bids(order)
|
|
assert order.bid == paid_bid
|
|
assert order.unpaid_bid == unpaid_bid
|
|
expected_bid_per_byte = paid_bid / (n_bytes + 52) # w/ 52 overhead bytes
|
|
assert order.bid_per_byte == expected_bid_per_byte
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_negative_bid_increase_error(mock_new_invoice, client):
|
|
initial_bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
bid=initial_bid)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
# Bump the bid with a negative value
|
|
bump_rv = client.post(f'/order/{uuid}/bump',
|
|
data={
|
|
'bid_increase': -1,
|
|
},
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert bump_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_bump_order(mock_new_invoice, client):
|
|
initial_bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
bid=initial_bid)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_json_resp['unpaid_bid'] == initial_bid
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert len(db_order.invoices) == 1
|
|
|
|
# Bump the bid on the existing order
|
|
bid_increase = 2500
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid_increase))
|
|
bump_rv = client.post(f'/order/{uuid}/bump',
|
|
data={
|
|
'bid_increase': bid_increase,
|
|
},
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert bump_rv.status_code == HTTPStatus.OK
|
|
|
|
# Get the order and check if the bid was bumped successfully
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_json_resp['unpaid_bid'] == initial_bid + bid_increase
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert len(db_order.invoices) == 2
|
|
|
|
# If a bump request fails, the order should stay untouched
|
|
second_bid_increase = 3000
|
|
mock_new_invoice.return_value = \
|
|
(False,
|
|
get_http_error_resp('LIGHTNING_CHARGE_INVOICE_ERROR'))
|
|
bump_rv = client.post(f'/order/{uuid}/bump',
|
|
data={
|
|
'bid_increase': second_bid_increase,
|
|
},
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert bump_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
# Get the order and verify the second bid increase (which failed) was
|
|
# completely ignored
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_json_resp['unpaid_bid'] == initial_bid + bid_increase
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert len(db_order.invoices) == 2
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_bump_transmitted_order(mock_new_invoice, client):
|
|
initial_bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
bid=initial_bid)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
pay_invoice(db_order.invoices[0], client)
|
|
db.session.commit()
|
|
|
|
get_rv = client.get(f'/order/{uuid}', headers={'X-Auth-Token': auth_token})
|
|
assert get_rv.status_code == HTTPStatus.OK
|
|
get_json_resp = get_rv.get_json()
|
|
assert get_json_resp['unpaid_bid'] == 0
|
|
assert get_json_resp['bid'] == initial_bid
|
|
assert get_json_resp['status'] == OrderStatus.transmitting.name
|
|
|
|
# Since the order is already in transmitting state, a bump request should
|
|
# return error
|
|
bid_increase = 2500
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid_increase))
|
|
bump_rv = client.post(f'/order/{uuid}/bump',
|
|
data={
|
|
'bid_increase': bid_increase,
|
|
},
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert bump_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
assert_error(bump_rv.get_json(), 'ORDER_BUMP_ERROR')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_bump_non_paid_order(mock_new_invoice, client):
|
|
# Send order over the gossip channel, which is not paid.
|
|
initial_bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
bid=initial_bid,
|
|
channel=constants.GOSSIP_CHANNEL,
|
|
admin=True)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
# Since the order is not paid, a bump request should not be authorized
|
|
bid_increase = 2500
|
|
bump_rv = client.post(f'/order/{uuid}/bump',
|
|
data={
|
|
'bid_increase': bid_increase,
|
|
},
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert bump_rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
assert_error(bump_rv.get_json(), 'ORDER_CHANNEL_UNAUTHORIZED_OP')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_cancel_order(mock_new_invoice, client):
|
|
bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice, client, bid=bid)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.pending.value
|
|
assert db_order.cancelled_at is None
|
|
|
|
# Only pending and paid orders can be cancelled
|
|
for status in OrderStatus:
|
|
db_order.status = status.value
|
|
db.session.commit()
|
|
if status in [OrderStatus.paid, OrderStatus.pending]:
|
|
delete_rv = client.delete(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert delete_rv.status_code == HTTPStatus.OK
|
|
else:
|
|
delete_rv = client.delete(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert delete_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
assert_error(delete_rv.get_json(), 'ORDER_CANCELLATION_ERROR')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_cancel_order_twice(mock_new_invoice, client):
|
|
bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice, client, bid=bid)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.pending.value
|
|
assert db_order.cancelled_at is None
|
|
message_path = os.path.join(constants.MSG_STORE_PATH, uuid)
|
|
assert os.path.exists(message_path)
|
|
|
|
# Cancel the order
|
|
delete_rv = client.delete(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert delete_rv.status_code == HTTPStatus.OK
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.cancelled.value
|
|
assert db_order.cancelled_at is not None
|
|
assert not os.path.exists(message_path)
|
|
|
|
# Try to cancel the order again
|
|
delete_rv = client.delete(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert delete_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
assert_error(delete_rv.get_json(), 'ORDER_CANCELLATION_ERROR')
|
|
|
|
|
|
def test_cancel_non_existing_order(client):
|
|
delete_rv = client.delete('/order/13245',
|
|
headers={'X-Auth-Token': 'token'})
|
|
assert delete_rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(delete_rv.get_json(), 'ORDER_NOT_FOUND')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_cancel_order_unauthorized_channel_op(mock_new_invoice, client):
|
|
# Place order on a channel that forbids uses from deleting orders.
|
|
bid = 1000
|
|
json_response = generate_test_order(mock_new_invoice,
|
|
client,
|
|
bid=bid,
|
|
channel=constants.GOSSIP_CHANNEL,
|
|
admin=True)
|
|
uuid = json_response['uuid']
|
|
auth_token = json_response['auth_token']
|
|
|
|
# Deleting through the regular user endpoint should fail
|
|
delete_rv = client.delete(f'/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
assert delete_rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
assert_error(delete_rv.get_json(), 'ORDER_CHANNEL_UNAUTHORIZED_OP')
|
|
|
|
# Deleting through the admin endpoint should work (i.e., won't return
|
|
# unauthorized operation)
|
|
delete_rv = client.delete(f'/admin/order/{uuid}',
|
|
headers={'X-Auth-Token': auth_token})
|
|
# In this case, it hits a cancellation error because the order is already
|
|
# in transmitting state, given that the gossip channel is auto-paid.
|
|
assert delete_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
assert_error(delete_rv.get_json(), 'ORDER_CANCELLATION_ERROR')
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.transmitting.value
|
|
assert db_order.cancelled_at is None
|
|
|
|
|
|
def test_get_sent_message_for_nonexisting_uuid(client):
|
|
# Try to get message for a non existing uuid
|
|
rv = client.get('/order/some-uuid/sent_message')
|
|
assert rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(rv.get_json(), 'ORDER_NOT_FOUND')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_sent_message_for_pending_order(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client)['uuid']
|
|
# The test order has pending status. Hence, fetching via the
|
|
# /order/{uuid}/sent_message endpoint should fail. Only "sent" and
|
|
# "transmitting" orders can be requested.
|
|
rv = client.get(f'/order/{uuid}/sent_message')
|
|
assert rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(rv.get_json(), 'ORDER_NOT_FOUND')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize("status",
|
|
[OrderStatus.sent, OrderStatus.transmitting])
|
|
def test_get_sent_message_from_uuid(mock_new_invoice, client, status):
|
|
uuid = generate_test_order(mock_new_invoice, client,
|
|
order_status=status)['uuid']
|
|
rv = client.get(f'/order/{uuid}/sent_message')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
received_message = rv.data
|
|
check_received_message(uuid, received_message)
|
|
|
|
|
|
def test_get_sent_message_for_nonexisting_seq_number(client):
|
|
# Try to get message for a non existing seq number
|
|
rv = client.get('/message/1')
|
|
assert rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(rv.get_json(), 'SEQUENCE_NUMBER_NOT_FOUND')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_sent_message_by_seq_number_for_paid_order(mock_new_invoice,
|
|
client):
|
|
uuid = generate_test_order(mock_new_invoice, client)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
pay_invoice(db_order.invoices[0], client)
|
|
db.session.commit()
|
|
|
|
# Because the invoice was paid, the order should go immediately into
|
|
# transmitting state and be assigned with sequence number 1.
|
|
rv = client.get('/message/1')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
received_message = rv.data
|
|
check_received_message(uuid, received_message)
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize(
|
|
"status",
|
|
[OrderStatus.sent, OrderStatus.received, OrderStatus.transmitting])
|
|
def test_get_sent_message_by_seq_number(mock_new_invoice, client, status):
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
order_status=status,
|
|
tx_seq_num=1)['uuid']
|
|
|
|
# Get order's sent message by seq number
|
|
rv = client.get('/message/1')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
received_message = rv.data
|
|
check_received_message(uuid, received_message)
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_get_sent_message_by_seq_number_unauthorized_channel_op(
|
|
mock_new_invoice, client):
|
|
# Create an order on the auth channel, which forbids GET requests from
|
|
# users. Make sure those requests fail. And use the /admin/order endpoint
|
|
# when POSTing the order.
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
order_status=OrderStatus.sent,
|
|
tx_seq_num=1,
|
|
channel=constants.AUTH_CHANNEL,
|
|
admin=True)['uuid']
|
|
|
|
# Reading by sequence number via the regular route should fail
|
|
rv = client.get('/message/1')
|
|
assert rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
assert_error(rv.get_json(), 'ORDER_CHANNEL_UNAUTHORIZED_OP')
|
|
|
|
# Reading by sequence number via the admin route should fail
|
|
rv = client.get('/admin/message/1')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
received_message = rv.data
|
|
check_received_message(uuid, received_message)
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize("channel", constants.CHANNELS)
|
|
def test_get_sent_message_admin(mock_new_invoice, client, channel):
|
|
# the admin should be able to get messages from any channel
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
order_status=OrderStatus.sent,
|
|
tx_seq_num=1,
|
|
channel=channel,
|
|
admin=True)['uuid']
|
|
|
|
rv = client.get(f'/admin/message/1?channel={channel}')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
received_message = rv.data
|
|
check_received_message(uuid, received_message)
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize("channel", [
|
|
constants.GOSSIP_CHANNEL, constants.BTC_SRC_CHANNEL, constants.AUTH_CHANNEL
|
|
])
|
|
def test_post_order_unauthorized_channel(mock_new_invoice, client, channel):
|
|
# users are not authorized to post to some channels
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'channel': channel
|
|
})
|
|
assert rv.status_code == HTTPStatus.UNAUTHORIZED
|
|
assert_error(rv.get_json(), 'ORDER_CHANNEL_UNAUTHORIZED_OP')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize("channel", constants.CHANNELS)
|
|
def test_post_order_admin(mock_new_invoice, client, channel):
|
|
# the admin should be allowed to POST messages to any channel
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
rv = client.post('/admin/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'channel': channel
|
|
})
|
|
assert rv.status_code == HTTPStatus.OK
|
|
check_upload(rv.get_json()['uuid'], msg)
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_tx_missing_or_invalid_param(mock_new_invoice, client):
|
|
post_rv = client.post('/order/tx/1')
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order/tx/1', data={'regions': 'a'})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order/tx/1', data={'regions': 1})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
# Confirm tx of a non existing sequence number
|
|
post_rv = client.post('/order/tx/2', data={"regions": [[1]]})
|
|
assert post_rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(post_rv.get_json(), 'SEQUENCE_NUMBER_NOT_FOUND')
|
|
# Create a test order but confirm Tx for an invalid region
|
|
generate_test_order(mock_new_invoice, client, tx_seq_num=1)
|
|
post_rv = client.post('/order/tx/1', data={'regions': [[20, 1]]})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_tx(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=1)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Confirm tx for a single region
|
|
post_rv = client.post('/order/tx/1',
|
|
data={'regions': [[Regions.t11n_afr.value]]})
|
|
assert len(db_order.tx_confirmations) == 1
|
|
assert db_order.tx_confirmations[0].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_afr]['id']
|
|
|
|
# Confirm tx for multiple regions
|
|
post_rv = client.post(
|
|
'/order/tx/1',
|
|
data={
|
|
'regions':
|
|
[[Regions.g18.value, Regions.e113.value, Regions.t11n_afr.value]]
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order.tx_confirmations) == 3
|
|
assert db_order.tx_confirmations[0].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_afr]['id']
|
|
assert db_order.tx_confirmations[1].region_id == SATELLITE_REGIONS[
|
|
Regions.g18]['id']
|
|
assert db_order.tx_confirmations[2].region_id == SATELLITE_REGIONS[
|
|
Regions.e113]['id']
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_rx_missing_or_invalid_param(mock_new_invoice, client):
|
|
post_rv = client.post('/order/rx/1')
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order/rx/1', data={'region': 'a'})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order/rx/1', data={'regions': [[1, 2]]})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
# Confirm rx of a non existing sequence number
|
|
post_rv = client.post('/order/rx/2', data={"region": 1})
|
|
assert post_rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(post_rv.get_json(), 'SEQUENCE_NUMBER_NOT_FOUND')
|
|
# Create a test order but confirm Rx for an invalid region
|
|
generate_test_order(mock_new_invoice, client, tx_seq_num=1)
|
|
post_rv = client.post('/order/rx/1', data={'region': 20})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_rx(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=1)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
post_rv = client.post('/order/rx/1', data={'region': Regions.g18.value})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order.rx_confirmations) == 1
|
|
assert db_order.rx_confirmations[0].region_id == SATELLITE_REGIONS[
|
|
Regions.g18]['id']
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize(
|
|
"tx_regions",
|
|
[[Regions.g18.value], [Regions.g18.value, Regions.e113.value],
|
|
[
|
|
Regions.g18.value, Regions.e113.value, Regions.t11n_afr.value,
|
|
Regions.t11n_eu.value, Regions.t18v_c.value
|
|
],
|
|
[
|
|
Regions.g18.value, Regions.e113.value, Regions.t11n_afr.value,
|
|
Regions.t11n_eu.value, Regions.t18v_c.value, Regions.t18v_c.value
|
|
]])
|
|
@pytest.mark.parametrize(
|
|
"rx_regions",
|
|
[[Regions.g18.value], [Regions.g18.value, Regions.e113.value],
|
|
[Regions.g18.value, Regions.e113.value, Regions.t18v_c.value],
|
|
[
|
|
Regions.g18.value, Regions.e113.value, Regions.t18v_c.value,
|
|
Regions.t18v_c.value
|
|
]])
|
|
def test_sent_or_received_criteria_met_inadequate_regions(
|
|
mock_new_invoice, client, tx_regions, rx_regions):
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
order_status=OrderStatus.transmitting,
|
|
tx_seq_num=1)['uuid']
|
|
|
|
# Confirm tx
|
|
post_rv = client.post('/order/tx/1', data={'regions': [tx_regions]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# Confirm rx
|
|
for region in rx_regions:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# The order status should not change to sent nor received because not
|
|
# enough regions have confirmed tx/rx
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.confirming.value
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_sent_or_received_criteria_met_for_unsent_order(
|
|
mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=1)['uuid']
|
|
# Confirm tx for all 6 regions
|
|
post_rv = client.post('/order/tx/1',
|
|
data={'regions': [[e.value for e in Regions]]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# Confirm rx for all regions except africa and europe
|
|
for region in [
|
|
Regions.g18.value, Regions.e113.value, Regions.t18v_c.value,
|
|
Regions.t18v_ku.value
|
|
]:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# Although all the required Tx/Rx confirmations are available, the order
|
|
# still cannot change to "received" state, as that requires the order to be
|
|
# in "sent" state before. The test order is still in "pending" state.
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.pending.value
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_sent_or_received_criteria_met_successfully(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
order_status=OrderStatus.transmitting,
|
|
tx_seq_num=1)['uuid']
|
|
|
|
# Confirm tx for all 6 regions
|
|
post_rv = client.post('/order/tx/1',
|
|
data={'regions': [[e.value for e in Regions]]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
# Order's status should change to sent
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.sent.value
|
|
|
|
# Confirm rx for all regions except africa and europe
|
|
for region in [
|
|
Regions.g18.value, Regions.e113.value, Regions.t18v_c.value,
|
|
Regions.t18v_ku.value
|
|
]:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# Order's status should change to received
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.received.value
|
|
|
|
# Synthesized rx_confirmations for africa and europe should be created
|
|
for region in [Regions.t11n_afr, Regions.t11n_eu]:
|
|
db_rx_confirmation = RxConfirmation.query.filter_by(
|
|
order_id=db_order.id).filter_by(
|
|
region_id=SATELLITE_REGIONS[region]['id']).all()
|
|
assert len(db_rx_confirmation) == 1
|
|
assert db_rx_confirmation[0].presumed
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_tx_repeated_regions(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=1)['uuid']
|
|
db_order1 = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=2)['uuid']
|
|
db_order2 = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Confirm tx for a single region
|
|
post_rv = client.post('/order/tx/1',
|
|
data={'regions': [[Regions.t11n_afr.value]]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order1.tx_confirmations) == 1
|
|
|
|
# Re-Confirm tx for the same region
|
|
post_rv = client.post('/order/tx/1',
|
|
data={'regions': [[Regions.t11n_afr.value]]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order1.tx_confirmations) == 1
|
|
|
|
# Confirm tx for multiple regions, including t11n_afr again
|
|
post_rv = client.post(
|
|
'/order/tx/1',
|
|
data={
|
|
'regions':
|
|
[[Regions.g18.value, Regions.t18v_c.value, Regions.t11n_afr.value]]
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order1.tx_confirmations) == 3
|
|
|
|
# Confirm tx for multiple regions, different order_id
|
|
post_rv = client.post('/order/tx/2',
|
|
data={
|
|
'regions': [[
|
|
Regions.t11n_eu.value, Regions.t18v_ku.value,
|
|
Regions.t18v_ku.value
|
|
]]
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order2.tx_confirmations) == 2
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_confirm_rx_repeated_regions(mock_new_invoice, client):
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=1)['uuid']
|
|
db_order1 = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
uuid = generate_test_order(mock_new_invoice, client, tx_seq_num=2)['uuid']
|
|
db_order2 = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Confirm rx for a region
|
|
post_rv = client.post('/order/rx/1', data={'region': Regions.g18.value})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order1.rx_confirmations) == 1
|
|
|
|
# Re-Confirm rx for the same region
|
|
post_rv = client.post('/order/rx/1', data={'region': Regions.g18.value})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order1.rx_confirmations) == 1
|
|
|
|
# Confirm rx for the same region, different order_id
|
|
post_rv = client.post('/order/rx/2', data={'region': Regions.g18.value})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order2.rx_confirmations) == 1
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
@pytest.mark.parametrize("status", [
|
|
OrderStatus.transmitting, OrderStatus.sent, OrderStatus.received,
|
|
OrderStatus.cancelled, OrderStatus.expired
|
|
])
|
|
def test_try_to_pay_a_non_pending_order(mock_new_invoice, client, status):
|
|
n_bytes = 500
|
|
invoice = new_invoice(1, InvoiceStatus.pending,
|
|
bidding.get_min_bid(n_bytes))
|
|
mock_new_invoice.return_value = (True, invoice)
|
|
post_rv = place_order(client, n_bytes)
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
uuid_order = post_rv.get_json()['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid_order).first()
|
|
db_order.status = status.value
|
|
db.session.commit()
|
|
|
|
charged_auth_token = hmac_sha256_digest(constants.LIGHTNING_WEBHOOK_KEY,
|
|
invoice.lid)
|
|
rv = client.post(f'/callback/{invoice.lid}/{charged_auth_token}')
|
|
assert rv.status_code == HTTPStatus.OK
|
|
|
|
# Refetch the order and the invoice from the database.
|
|
# The expectation is that invoice changes its status to paid because
|
|
# it had the pending status, but order keeps its current status.
|
|
db_invoice = Invoice.query.filter_by(lid=invoice.lid).first()
|
|
db_order = Order.query.filter_by(uuid=uuid_order).first()
|
|
assert db_invoice.status == InvoiceStatus.paid.value
|
|
assert db_order.status == status.value
|
|
assert db_invoice.paid_at is not None
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_bump_non_existing_order(mock_new_invoice, client):
|
|
bid_increase = 2500
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid_increase))
|
|
bump_rv = client.post('/order/12345/bump',
|
|
data={
|
|
'bid_increase': bid_increase,
|
|
},
|
|
headers={'X-Auth-Token': "non-existing-token"})
|
|
assert bump_rv.status_code == HTTPStatus.NOT_FOUND
|
|
assert_error(bump_rv.get_json(), 'ORDER_NOT_FOUND')
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_create_order_with_invalid_region(mock_new_invoice, client):
|
|
n_bytes = 500
|
|
bid = bidding.get_min_bid(n_bytes)
|
|
msg = rnd_string(n_bytes)
|
|
mock_new_invoice.return_value = (True,
|
|
new_invoice(1, InvoiceStatus.pending,
|
|
bid))
|
|
post_rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'regions': 'a'
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'regions': 1
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
post_rv = client.post('/order',
|
|
data={
|
|
'bid': bid,
|
|
'message': msg.encode(),
|
|
'regions': [[6, 1]]
|
|
})
|
|
assert post_rv.status_code == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_sent_or_received_criteria_met_successfully_for_subset_of_regions(
|
|
mock_new_invoice, client):
|
|
selected_regions = [
|
|
Regions.g18.value, Regions.e113.value, Regions.t11n_afr.value
|
|
]
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
tx_seq_num=1,
|
|
order_status=OrderStatus.transmitting,
|
|
regions=selected_regions)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Only the selected regions are required to confirm Tx in order for the
|
|
# order to change into "sent" state.
|
|
post_rv = client.post('/order/tx/1', data={'regions': [selected_regions]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
assert len(db_order.tx_confirmations) == 3
|
|
assert db_order.tx_confirmations[0].region_id == SATELLITE_REGIONS[
|
|
Regions.g18]['id']
|
|
assert db_order.tx_confirmations[1].region_id == SATELLITE_REGIONS[
|
|
Regions.e113]['id']
|
|
assert db_order.tx_confirmations[2].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_afr]['id']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.sent.value
|
|
|
|
# Confirm rx only for the monitored regions in the order request (i.e.,
|
|
# excluding t11n_afr)
|
|
for region in [Regions.g18.value, Regions.e113.value]:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# The order's status should change to received
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.received.value
|
|
|
|
# A synthesized Rx confirmation should be created for t11n_afr
|
|
synth_rx_confirmations = RxConfirmation.query.filter_by(
|
|
order_id=db_order.id).filter_by(presumed=True).all()
|
|
assert len(synth_rx_confirmations) == 1
|
|
assert synth_rx_confirmations[0].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_afr]['id']
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_sent_or_received_criteria_met_for_subset_of_presumed_rx_regions(
|
|
mock_new_invoice, client):
|
|
selected_regions = [Regions.t11n_afr.value, Regions.t11n_eu.value]
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
tx_seq_num=1,
|
|
order_status=OrderStatus.transmitting,
|
|
regions=selected_regions)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Confirm tx only for the two selected regions
|
|
post_rv = client.post('/order/tx/1', data={'regions': [selected_regions]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# The order status should change to received and NOT sent. The two regions
|
|
# in the order request were t11n_afr and t11n_eu. None of these regions
|
|
# send Rx confirmations. As a result, the order should automatically move
|
|
# from sent to received without any Rx confirmations.
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.received.value
|
|
|
|
# The synthesized Rx confirmations for Africa and Europe should be created
|
|
db_rx_confirmation = RxConfirmation.query.filter_by(
|
|
order_id=db_order.id).filter_by(presumed=True).all()
|
|
assert len(db_rx_confirmation) == 2
|
|
assert db_rx_confirmation[0].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_afr]['id']
|
|
assert db_rx_confirmation[1].region_id == SATELLITE_REGIONS[
|
|
Regions.t11n_eu]['id']
|
|
|
|
|
|
@patch('orders.new_invoice')
|
|
def test_sent_or_received_criteria_met_invalid_tx_subset(
|
|
mock_new_invoice, client):
|
|
selected_regions = [Regions.g18.value, Regions.e113.value]
|
|
other_regions = [
|
|
Regions.t11n_afr.value, Regions.t11n_eu.value, Regions.t18v_c.value,
|
|
Regions.t18v_ku.value
|
|
]
|
|
uuid = generate_test_order(mock_new_invoice,
|
|
client,
|
|
tx_seq_num=1,
|
|
order_status=OrderStatus.transmitting,
|
|
regions=selected_regions)['uuid']
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
|
|
# Confirm tx over regions other than those explicitly requested
|
|
# by the order
|
|
post_rv = client.post('/order/tx/1', data={'regions': [other_regions]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
|
|
# The order status should change to "confirming"
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.confirming.value
|
|
|
|
# Confirm tx
|
|
post_rv = client.post('/order/tx/1', data={'regions': [selected_regions]})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
# Now the status should get updated to sent
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.sent.value
|
|
|
|
# Confirm rx
|
|
for region in other_regions:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
# The status should stay as sent so long as rx is not confirmed
|
|
# by the two regions in the request (g18, e113)
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.sent.value
|
|
|
|
for region in [Regions.g18.value, Regions.e113.value]:
|
|
post_rv = client.post('/order/rx/1', data={'region': region})
|
|
assert post_rv.status_code == HTTPStatus.OK
|
|
# Now the status should change to received
|
|
db_order = Order.query.filter_by(uuid=uuid).first()
|
|
assert db_order.status == OrderStatus.received.value
|