Skip to content

Commit f59e9f7

Browse files
committed
start adding tests for consistency on version_number [data_integrity_test]
1 parent 48cb8b9 commit f59e9f7

File tree

9 files changed

+86
-23
lines changed

9 files changed

+86
-23
lines changed

src/allocation/adapters/orm.py

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

2525
batches = Table(

src/allocation/adapters/repository.py

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

2222
def get(self, sku):
23+
print(sku, type(sku))
2324
return self.session.query(model.Product).filter_by(sku=sku).first()

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@ def postgres_db():
6262

6363

6464
@pytest.fixture
65-
def postgres_session(postgres_db):
65+
def postgres_session_factory(postgres_db):
6666
start_mappers()
67-
yield sessionmaker(bind=postgres_db)()
67+
yield sessionmaker(bind=postgres_db)
6868
clear_mappers()
6969

7070

71+
@pytest.fixture
72+
def postgres_session(postgres_session_factory):
73+
return postgres_session_factory()
74+
75+
7176
@pytest.fixture
7277
def restart_api():
7378
(Path(__file__).parent / "../src/allocation/entrypoints/flask_app.py").touch()

tests/e2e/__init__.py

Whitespace-only changes.

tests/e2e/test_api.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,8 @@
1-
import uuid
21
import pytest
32
import requests
43

54
from allocation import config
6-
7-
8-
def random_suffix():
9-
return uuid.uuid4().hex[:6]
10-
11-
12-
def random_sku(name=""):
13-
return f"sku-{name}-{random_suffix()}"
14-
15-
16-
def random_batchref(name=""):
17-
return f"batch-{name}-{random_suffix()}"
18-
19-
20-
def random_orderid(name=""):
21-
return f"order-{name}-{random_suffix()}"
5+
from ..random_refs import random_sku, random_batchref, random_orderid
226

237

248
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 = 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 orders.rowcount == 1
124+
with unit_of_work.SqlAlchemyUnitOfWork() as uow:
125+
uow.session.execute("select 1")

tests/random_refs.py

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

0 commit comments

Comments
 (0)