Skip to content

Commit b4ffd27

Browse files
committed
start adding tests for consistency on version_number [data_integrity_test]
1 parent 5ba88f3 commit b4ffd27

9 files changed

Lines changed: 81 additions & 19 deletions

File tree

src/allocation/adapters/orm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
products = Table(
2121
'products', metadata,
2222
Column('sku', String(255), primary_key=True),
23-
# Column('version_number', Integer, nullable=False, default=0),
23+
Column('version_number', Integer, nullable=False, server_default='0'),
2424
)
2525

2626
batches = Table(

src/allocation/adapters/repository.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ def add(self, product):
2222
self.session.add(product)
2323

2424
def get(self, sku):
25+
print(sku, type(sku))
2526
return self.session.query(model.Product).filter_by(sku=sku).first()

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,16 @@ def postgres_db():
5959
metadata.create_all(engine)
6060
return engine
6161

62-
6362
@pytest.fixture
64-
def postgres_session(postgres_db):
63+
def postgres_session_factory(postgres_db):
6564
start_mappers()
66-
yield sessionmaker(bind=postgres_db)()
65+
yield sessionmaker(bind=postgres_db)
6766
clear_mappers()
6867

68+
@pytest.fixture
69+
def postgres_session(postgres_session_factory):
70+
return postgres_session_factory()
71+
6972

7073
@pytest.fixture
7174
def restart_api():

tests/e2e/__init__.py

Whitespace-only changes.

tests/e2e/test_api.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,7 @@
33
import requests
44

55
from allocation import config
6-
7-
def random_suffix():
8-
return uuid.uuid4().hex[:6]
9-
10-
def random_sku(name=''):
11-
return f'sku-{name}-{random_suffix()}'
12-
13-
def random_batchref(name=''):
14-
return f'batch-{name}-{random_suffix()}'
15-
16-
def random_orderid(name=''):
17-
return f'order-{name}-{random_suffix()}'
6+
from ..random_refs import random_sku, random_batchref, random_orderid
187

198

209
def post_to_add_batch(ref, sku, qty, eta):

tests/integration/__init__.py

Whitespace-only changes.

tests/integration/test_uow.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
# pylint: disable=broad-except
2+
import threading
3+
import time
4+
import traceback
5+
from typing import List
16
import pytest
27
from allocation.domain import model
38
from allocation.service_layer import unit_of_work
9+
from ..random_refs import random_sku, random_batchref, random_orderid
410

511

6-
def insert_batch(session, ref, sku, qty, eta):
12+
def insert_batch(session, ref, sku, qty, eta, product_version=1):
713
session.execute(
8-
'INSERT INTO products (sku) VALUES (:sku)',
9-
dict(sku=sku),
14+
'INSERT INTO products (sku, version_number) VALUES (:sku, :version)',
15+
dict(sku=sku, version=product_version),
1016
)
1117
session.execute(
1218
'INSERT INTO batches (reference, sku, _purchased_quantity, eta)'
@@ -67,3 +73,53 @@ class MyException(Exception):
6773
new_session = session_factory()
6874
rows = list(new_session.execute('SELECT * FROM "batches"'))
6975
assert rows == []
76+
77+
78+
def try_to_allocate(orderid, sku, exceptions):
79+
line = model.OrderLine(orderid, sku, 10)
80+
try:
81+
with unit_of_work.SqlAlchemyUnitOfWork() as uow:
82+
product = uow.products.get(sku=sku)
83+
product.allocate(line)
84+
time.sleep(0.2)
85+
uow.commit()
86+
except Exception as e:
87+
print(traceback.format_exc())
88+
exceptions.append(e)
89+
90+
91+
def test_concurrent_updates_to_version_are_not_allowed(postgres_session_factory):
92+
sku, batch = random_sku(), random_batchref()
93+
session = postgres_session_factory()
94+
insert_batch(session, batch, sku, 100, eta=None, product_version=1)
95+
session.commit()
96+
97+
order1, order2 = random_orderid(1), random_orderid(2)
98+
exceptions = [] # type: List[Exception]
99+
try_to_allocate_order1 = lambda: try_to_allocate(order1, sku, exceptions)
100+
try_to_allocate_order2 = lambda: try_to_allocate(order2, sku, exceptions)
101+
thread1 = threading.Thread(target=try_to_allocate_order1)
102+
thread2 = threading.Thread(target=try_to_allocate_order2)
103+
thread1.start()
104+
thread2.start()
105+
thread1.join()
106+
thread2.join()
107+
108+
[[version]] = session.execute(
109+
"SELECT version_number FROM products WHERE sku=:sku",
110+
dict(sku=sku),
111+
)
112+
assert version == 2
113+
[exception] = exceptions
114+
assert 'could not serialize access due to concurrent update' in str(exception)
115+
116+
orders = list(session.execute(
117+
"SELECT orderid FROM allocations"
118+
" JOIN batches ON allocations.batch_id = batches.id"
119+
" JOIN order_lines ON allocations.orderline_id = order_lines.id"
120+
" WHERE order_lines.sku=:sku",
121+
dict(sku=sku),
122+
))
123+
assert len(orders) == 1
124+
with unit_of_work.SqlAlchemyUnitOfWork() as uow:
125+
uow.session.execute('select 1')

tests/random_refs.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import uuid
2+
3+
def random_suffix():
4+
return uuid.uuid4().hex[:6]
5+
6+
def random_sku(name=''):
7+
return f'sku-{name}-{random_suffix()}'
8+
9+
def random_batchref(name=''):
10+
return f'batch-{name}-{random_suffix()}'
11+
12+
def random_orderid(name=''):
13+
return f'order-{name}-{random_suffix()}'

0 commit comments

Comments
 (0)