def test_realocates_batch_if_nessesary_when_available_quantity_reduces( messagebus, ): history = [ commands.CreateBatch(reference="batch1", sku="INDIFFERENT-TABLE", qty=100), commands.CreateBatch( reference="batch2", sku="INDIFFERENT-TABLE", qty=100, eta=date.today(), ), commands.Allocate(orderid="order1", sku="INDIFFERENT-TABLE", qty=50), commands.Allocate(orderid="order2", sku="INDIFFERENT-TABLE", qty=50), ] for message in history: messagebus.handle(message) [batch1, batch2] = messagebus.uow.products.get("INDIFFERENT-TABLE").batches assert batch1.available_quantity == 0 assert batch2.available_quantity == 100 message = commands.ChangeBatchQuantity(reference="batch1", qty=55) messagebus.handle(message) assert batch1.available_quantity == 5 assert batch2.available_quantity == 50
def test_allocations_view(sqlite_bus): # uow = unit_of_work.SqlAlchemyUnitOfWork(sqlite_session_factory) sqlite_bus.handle(commands.CreateBatch('sku1batch', 'sku1', 50, None)) sqlite_bus.handle(commands.CreateBatch('sku2batch', 'sku2', 50, None)) sqlite_bus.handle(commands.Allocate( 'order1', 'sku1', 20, )) sqlite_bus.handle(commands.Allocate( 'order1', 'sku2', 20, )) sqlite_bus.handle( commands.CreateBatch('sku1batch-later', 'sku1', 50, today)) sqlite_bus.handle(commands.Allocate('other-order', 'sku1', 30)) sqlite_bus.handle(commands.Allocate('other-order', 'sku2', 10)) assert views.allocations('order1', sqlite_bus.uow) == [ { 'sku': 'sku1', 'batchref': 'sku1batch' }, { 'sku': 'sku2', 'batchref': 'sku2batch' }, ]
def test_sends_email_on_out_of_stock_error(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch("b1", "POPULAR-CURTAINS", 9, None)) bus.handle(commands.Allocate("o1", "POPULAR-CURTAINS", 10)) with mock.patch( "allocation.adapters.notifications.EmailNotifications.send" ) as mock_send_mail: bus.handle(commands.Allocate("o1", "POPULAR-CURTAINS", 10)) assert mock_send_mail.call_args == mock.call( ['*****@*****.**'], "缺货啦,货号POPULAR-CURTAINS")
def test_allocations_view(sqlite_bus): sqlite_bus.handle(commands.CreateBatch("sku1batch", "sku1", 50, None)) sqlite_bus.handle(commands.CreateBatch("sku2batch", "sku2", 50, today)) sqlite_bus.handle(commands.Allocate("order1", "sku1", 20)) sqlite_bus.handle(commands.Allocate("order1", "sku2", 20)) # add a spurious batch and order to make sure we're getting the right ones sqlite_bus.handle(commands.CreateBatch("sku1batch-later", "sku1", 50, today)) sqlite_bus.handle(commands.Allocate("otherorder", "sku1", 30)) sqlite_bus.handle(commands.Allocate("otherorder", "sku2", 10)) assert views.allocations("order1", sqlite_bus.uow) == [ {"sku": "sku1", "batchref": "sku1batch"}, {"sku": "sku2", "batchref": "sku2batch"}, ]
def test_allocations_view(sqlite_session_factory): uow = unit_of_work.SqlAlchemyUnitOfWork(sqlite_session_factory) messagebus.handle(commands.CreateBatch("sku1batch", "sku1", 50, None), uow) messagebus.handle(commands.CreateBatch("sku2batch", "sku2", 50, today), uow) messagebus.handle(commands.Allocate("order1", "sku1", 20), uow) messagebus.handle(commands.Allocate("order1", "sku2", 20), uow) # add a spurious batch and order to make sure we're getting the right ones messagebus.handle(commands.CreateBatch("sku1batch-later", "sku1", 50, today), uow) messagebus.handle(commands.Allocate("otherorder", "sku1", 30), uow) messagebus.handle(commands.Allocate("otherorder", "sku2", 10), uow) assert views.allocations("order1", uow) == [ {"sku": "sku1", "batchref": "sku1batch"}, {"sku": "sku2", "batchref": "sku2batch"}, ]
def test_allocates(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch('b1', '黑桌子', 20, None)) bus.handle(commands.Allocate('o1', '黑桌子', 10)) [batch] = bus.uow.products.get('黑桌子').batches assert batch.available_quantity == 10
def test_commits(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch("batch01", "OMINOUS-MIRROR", 100, None)) bus.handle(commands.Allocate("o1", "OMINOUS-MIRROR", 10)) assert bus.uow.committed
def test_allocates(self): uow = FakeUnitOfWork() messagebus.handle( commands.CreateBatch("batch1", "COMPLICATED-LAMP", 100, None), uow) messagebus.handle(commands.Allocate("o1", "COMPLICATED-LAMP", 10), uow) [batch] = uow.products.get("COMPLICATED-LAMP").batches assert batch.available_quantity == 90
def test_allocates(self): bus = bootstrap_test_app() bus.handle( commands.CreateBatch("batch1", "COMPLICATED-LAMP", 100, None)) bus.handle(commands.Allocate("o1", "COMPLICATED-LAMP", 10)) [batch] = bus.uow.products.get("COMPLICATED-LAMP").batches assert batch.available_quantity == 90
def test_deallocation(sqlite_bus): sqlite_bus.handle(commands.CreateBatch('b1', 'sku1', 50, None)) sqlite_bus.handle(commands.CreateBatch('b2', 'sku1', 50, today)) sqlite_bus.handle(commands.Allocate('o1', 'sku1', 40)) sqlite_bus.handle(commands.ChangeBatchQuantity('b1', 10)) assert views.allocations('o1') == [{'sku': 'sku1', 'batchref': 'b2'}]
def test_errors_for_invalid_sku(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch("b1", "AREALSKU", 100, None)) with pytest.raises(handlers.InvalidSku, match="Invalid sku NONEXISTENTSKU"): bus.handle(commands.Allocate("o1", "NONEXISTENTSKU", 10))
def reallocate( event: events.Deallocated, uow: unit_of_work.AbstractUnitOfWork ): with uow: product = uow.products.get(sku=event.sku) product.events.append(commands.Allocate(**asdict(event))) uow.commit()
def test_errors_for_invalid_sku(self): uow = FakeUnitOfWork() messagebus.handle(commands.CreateBatch("b1", "AREALSKU", 100, None), uow) with pytest.raises(handlers.InvalidSku, match="Invalid sku NONEXISTENTSKU"): messagebus.handle( commands.Allocate("o1", "NONEXISTENTSKU", 10), uow )
def test_out_of_stock_email(bus): sku = random_sku() bus.handle(commands.CreateBatch('batch1', sku, 9, None)) bus.handle(commands.Allocate('order1', sku, 10)) email = get_email_from_mailhog(sku) assert email['Raw']['From'] == '*****@*****.**' assert email['Raw']['To'] == ['*****@*****.**'] assert f'Out of stock for {sku}' in email['Raw']['Data']
def test_commits(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch('b1', 'OMINOUS-MIRROR', 100, None)) bus.handle(commands.Allocate('o1', 'OMINOUS-MIRROR', 10)) assert bus.uow.committed is True
def test_out_of_stock_email(bus): sku = random_sku() bus.handle(commands.CreateBatch("batch1", sku, 9, None)) bus.handle(commands.Allocate("order1", sku, 10)) email = get_email_from_mailhog(sku) assert email["Raw"]["From"] == "*****@*****.**" assert email["Raw"]["To"] == ["*****@*****.**"] assert f"Out of stock for {sku}" in email["Raw"]["Data"]
def create_and_allocate_batch(uow: unit_of_work.AbstractUnitOfWork) -> tuple: sku = helpers.random_sku() orderid = helpers.random_orderid() batchref = helpers.random_batchref() message_bus.handle(commands.CreateBatch(batchref, sku, 100, None), uow) message_bus.handle(commands.Allocate(orderid, sku, 30), uow) return batchref, orderid, sku
def test_allocations_view(sqlite_bus): sqlite_bus.handle(commands.CreateBatch('sku1batch', 'sku1', 50, None)) sqlite_bus.handle(commands.CreateBatch('sku2batch', 'sku2', 50, today)) sqlite_bus.handle(commands.Allocate('order1', 'sku1', 20)) sqlite_bus.handle(commands.Allocate('order1', 'sku2', 20)) # add s spurious batch and order to make sure we're getting the right ones sqlite_bus.handle( commands.CreateBatch('sku1batch-later', 'sku1', 50, today)) sqlite_bus.handle(commands.Allocate('otherorder', 'sku1', 30)) sqlite_bus.handle(commands.Allocate('otherorder', 'sku2', 10)) assert views.allocations('order1') == [{ 'sku': 'sku1', 'batchref': 'sku1batch' }, { 'sku': 'sku2', 'batchref': 'sku2batch' }]
def allocate_endpoint(): try: command = commands.Allocate(request.json['orderid'], request.json['sku'], request.json['qty']) bus.handle(command) except handlers.InvalidSku as e: return jsonify({'message': str(e)}), 400 return 'OK', 202
def test_returns_allocation(self): bus = bootstrap_test_app() bus.handle(commands.CreateBatch('b1', 'COMPLICATED-LAMP', 100, None)) bus.handle(commands.Allocate('o1', 'COMPLICATED-LAMP', 10)) [batch] = bus.uow.products.get('COMPLICATED-LAMP').batches assert batch.available_quantity == 90
def allocate_endpoint(): try: cmd = commands.Allocate(request.json["orderid"], request.json["sku"], request.json["qty"]) bus.handle(cmd) except InvalidSku as e: return {"message": str(e)}, 400 return "OK", 202
def test_sends_email_on_out_of_stock_error(self): uow = FakeUnitOfWork() messagebus.handle( commands.CreateBatch("b1", "POPULAR-CURTAINS", 9, None), uow) with mock.patch("allocation.adapters.email.send") as mock_send_mail: messagebus.handle(commands.Allocate("o1", "POPULAR-CURTAINS", 10), uow) assert mock_send_mail.call_args == mock.call( "*****@*****.**", f"Out of stock for POPULAR-CURTAINS")
def test_allocations_view(sqlite_session_factory): uow = unit_of_work.SqlAlchemyUnitOfWork(sqlite_session_factory) messagebus.handle(commands.CreateBatch('sku1batch', 'sku1', 50, None), uow) messagebus.handle(commands.CreateBatch('sku2batch', 'sku2', 50, today), uow) messagebus.handle(commands.Allocate('order1', 'sku1', 20), uow) messagebus.handle(commands.Allocate('order1', 'sku2', 20), uow) assert views.allocations('order1', uow) == [ { 'sku': 'sku1', 'batchref': 'sku1batch' }, { 'sku': 'sku2', 'batchref': 'sku2batch' }, ]
def test_commits(self): uow = FakeUnitOfWork() messagebus.handle( commands.CreateBatch("b1", "OMINOUS-MIRROR", 100, None), uow ) messagebus.handle( commands.Allocate("o1", "OMINOUS-MIRROR", 10), uow ) assert uow.committed
def test_deallocation(sqlite_bus): sqlite_bus.handle(commands.CreateBatch("b1", "sku1", 50, None)) sqlite_bus.handle(commands.CreateBatch("b2", "sku1", 50, today)) sqlite_bus.handle(commands.Allocate("o1", "sku1", 40)) sqlite_bus.handle(commands.ChangeBatchQuantity("b1", 10)) assert views.allocations("o1", sqlite_bus.uow) == [ {"sku": "sku1", "batchref": "b2"}, ]
def test_allocate_errors_for_invalid_sku(messagebus): message = commands.CreateBatch(reference="batch1", sku="AREALSKU", qty=100) messagebus.handle(message) with pytest.raises(handlers.InvalidSku, match="Invalid sku NON-EXISTENTSKU"): message = commands.Allocate(orderid="order1", sku="NON-EXISTENTSKU", qty=10) messagebus.handle(message)
def test_reallocates_if_necessary(self): bus = bootstrap_test_app() event_history = [ commands.CreateBatch('批次1', '桌子', 50), commands.CreateBatch('批次2', '桌子', 50, date.today()), commands.Allocate('订单1', '桌子', 20), commands.Allocate('订单2', '桌子', 20), ] for e in event_history: bus.handle(e) [batch1, batch2] = bus.uow.products.get(sku='桌子').batches assert batch1.available_quantity == 10 # 下了两单 现在库存10 assert batch2.available_quantity == 50 # 这个不变 # bus.handle( # commands.ChangeBatchQuantity('批次1', 25), uow) bus.handle(commands.ChangeBatchQuantity('批次1', 25)) assert batch1.available_quantity == 5 # 批次1泡水了 库存25 所以要退下的单子给批次2 assert batch2.available_quantity == 30 # 批次2 减少20
def test_deallocation(sqlite_session_factory): uow = unit_of_work.SqlAlchemyUnitOfWork(sqlite_session_factory) messagebus.handle(commands.CreateBatch("b1", "sku1", 50, None), uow) messagebus.handle(commands.CreateBatch("b2", "sku1", 50, today), uow) messagebus.handle(commands.Allocate("o1", "sku1", 40), uow) messagebus.handle(commands.ChangeBatchQuantity("b1", 10), uow) assert views.allocations("o1", uow) == [ {"sku": "sku1", "batchref": "b2"}, ]
def test_reallocates_if_necessary(self): uow = FakeUnitOfWork() history = [ commands.CreateBatch("batch1", "INDIFFERENT-TABLE", 50, None), commands.CreateBatch("batch2", "INDIFFERENT-TABLE", 50, date.today()), commands.Allocate("order1", "INDIFFERENT-TABLE", 20), commands.Allocate("order2", "INDIFFERENT-TABLE", 20), ] for msg in history: messagebus.handle(msg, uow) [batch1, batch2] = uow.products.get(sku="INDIFFERENT-TABLE").batches assert batch1.available_quantity == 10 assert batch2.available_quantity == 50 messagebus.handle(commands.ChangeBatchQuantity("batch1", 25), uow) # order1 or order2 will be deallocated, so we'll have 25 - 20 assert batch1.available_quantity == 5 # and 20 will be reallocated to the next batch assert batch2.available_quantity == 30
def test_allocations_view(session_factory): uow = unit_of_work.SqlAlchemyUnitOfWork(session_factory) messagebus.handle(commands.CreateBatch('sku1batch', 'sku1', 50, None), uow) messagebus.handle( commands.CreateBatch('sku2batch', 'sku2', 50, date.today()), uow) messagebus.handle(commands.Allocate('vieworder1', 'sku1', 20), uow) messagebus.handle(commands.Allocate('vieworder1', 'sku2', 20), uow) # add a spurious batch and order to make sure we're getting the right ones messagebus.handle( commands.CreateBatch('sku1batch-later', 'sku1', 50, date.today()), uow) messagebus.handle(commands.Allocate('otherorder', 'sku1', 30), uow) messagebus.handle(commands.Allocate('otherorder', 'sku2', 10), uow) assert views.allocations('vieworder1', uow) == [{ 'sku': 'sku1', 'batchref': 'sku1batch' }, { 'sku': 'sku2', 'batchref': 'sku2batch' }]