Browse Source

added more domain specific stuff

funding-tag
Holger Frey 5 years ago
parent
commit
0b37e80065
  1. 6
      Makefile
  2. 89
      ordr3/adapters.py
  3. 94
      ordr3/models.py
  4. 53
      ordr3/repo.py
  5. 34
      ordr3/services.py
  6. 1
      pyproject.toml
  7. 19
      tests/conftest.py
  8. 151
      tests/test_adapters.py
  9. 85
      tests/test_models.py
  10. 147
      tests/test_repo.py
  11. 99
      tests/test_services.py

6
Makefile

@ -56,10 +56,10 @@ lint: ## reformat with black and check style with flake8 @@ -56,10 +56,10 @@ lint: ## reformat with black and check style with flake8
black ordr3 tests
flake8 ordr3 tests
test: ## run tests quickly with the default Python
pytest tests -x --disable-warnings -k "not app"
test: lint ## run tests quickly with the default Python
pytest tests -x --disable-warnings -m "not app"
coverage: ## full test suite, check code coverage and open coverage report
coverage: lint ## full test suite, check code coverage and open coverage report
pytest tests --cov=ordr3
coverage html
$(BROWSER) htmlcov/index.html

89
ordr3/adapters.py

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
""" sqlalchemy metadata configuration """
from datetime import datetime
from sqlalchemy import (
Enum,
Text,
Float,
Table,
Column,
Integer,
DateTime,
ForeignKey,
)
from sqlalchemy.orm import mapper, relationship
from sqlalchemy.schema import MetaData
from . import models
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html
NAMING_CONVENTION = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
metadata = MetaData(naming_convention=NAMING_CONVENTION)
order_table = Table(
"orders",
metadata,
Column("id", Integer, primary_key=True),
Column("cas_description", Text, nullable=False),
Column("catalog_nr", Text, nullable=False),
Column("vendor", Text, nullable=False),
Column("category", Enum(models.OrderCategory), nullable=False),
Column("package_size", Text, nullable=False),
Column("unit_price", Float, nullable=False),
Column("currency", Text, nullable=False, default="EUR"),
Column("amount", Integer, nullable=False),
Column("account", Text, nullable=False, default=""),
Column("comment", Text, nullable=False, default=""),
# redundant properties, could be determined from orders log
Column("created_on", DateTime, nullable=False, default=datetime.utcnow),
Column("created_by", Text, nullable=True, index=True),
Column("status", Enum(models.OrderStatus), nullable=True),
)
log_table = Table(
"logs",
metadata,
Column("order_id", Integer, ForeignKey("orders.id"), primary_key=True),
Column("status", Enum(models.OrderStatus), primary_key=True),
Column("date", DateTime, primary_key=True, default=datetime.utcnow),
Column("by", Text, nullable=False),
Column("user_id", Integer, nullable=False, index=True),
)
user_table = Table(
"users",
metadata,
Column("id", Integer, primary_key=True),
Column("username", Text, nullable=False, index=True),
Column("first_name", Text, nullable=False),
Column("last_name", Text, nullable=False),
Column("email", Text, nullable=False, index=True),
Column("password", Text, nullable=False),
Column("role", Enum(models.UserRole)),
)
def start_mappers():
mapper(
models.OrderItem,
order_table,
properties={
"log": relationship(
models.LogItem, backref="order", order_by=log_table.c.date
)
},
)
mapper(models.LogItem, log_table)
mapper(models.User, user_table)

94
ordr3/models.py

@ -19,7 +19,27 @@ class OrderCategory(enum.Enum): @@ -19,7 +19,27 @@ class OrderCategory(enum.Enum):
BIOLAB = 4
class OrderItem:
@enum.unique
class UserRole(enum.Enum):
NEW = 1
USER = 2
PURCHASER = 3
ADMIN = 4
INACTIVE = 5
class Model:
def __hash__(self):
items = sorted(self.__dict__.items())
no_log = ((k, v) for k, v in items if not k == "log")
content = ((k, v) for k, v in no_log if not k.startswith("_"))
return hash(tuple(content))
def __eq__(self, other):
return hash(self) == hash(other)
class OrderItem(Model):
""" an ordered item """
# properties
@ -35,9 +55,6 @@ class OrderItem: @@ -35,9 +55,6 @@ class OrderItem:
account = None
comment = None
# logging status changes of the order
status_log = None
# redundant properties, could be determined from orders log
created_on = None
created_by = None
@ -56,6 +73,9 @@ class OrderItem: @@ -56,6 +73,9 @@ class OrderItem:
currency="",
account="",
comment="",
created_on=None,
created_by=None,
status=None,
):
self.id = id
self.cas_description = cas_description
@ -68,6 +88,11 @@ class OrderItem: @@ -68,6 +88,11 @@ class OrderItem:
self.currency = currency
self.account = account
self.comment = comment
self.created_on = created_on
self.created_by = created_by
self.status = status
self.log = []
def __repr__(self):
return f"<ordr3.models.OrderItem id={self.id}>"
@ -78,27 +103,28 @@ class OrderItem: @@ -78,27 +103,28 @@ class OrderItem:
def add_to_log(self, log_item):
""" adds a log item to the status log """
if not self.status_log:
self.status_log = []
if len(self.log) == 0:
self.created_by = log_item.by
self.created_on = log_item.date
self.status_log.append(log_item)
self.status = log_item.status
self.log.append(log_item)
class LogItem:
class LogItem(Model):
""" an entry in the order log """
order_id = None
status = None
date = None
by = None
user_id = None
date = None
def __init__(self, order, status, by, date=None):
self.order_id = order.id
def __init__(self, order_id, status, by, user_id, date=None):
self.order_id = order_id
self.status = status
self.by = by
self.date = date or datetime.now()
self.user_id = user_id
self.date = date or datetime.utcnow()
class ProposedConsumable:
@ -107,3 +133,47 @@ class ProposedConsumable: @@ -107,3 +133,47 @@ class ProposedConsumable:
def __init__(self, order):
self.order = order
self.times = 0
class User(Model):
id = None
username = None
first_name = None
last_name = None
email = None
password = None
role = None
def __init__(
self, id, username, first_name, last_name, email, password, role
):
self.id = id
self.username = username
self.first_name = first_name
self.last_name = last_name
self.email = email
self.password = password
self.role = role
@property
def principal(self):
return f"user:{self.id}"
@property
def principals(self):
tmp = [self.principal]
if self.role in {UserRole.PURCHASER, UserRole.ADMIN}:
tmp.append("role:user")
if self.role == UserRole.ADMIN:
tmp.append("role:purchaser")
tmp.append("role:" + self.role.name.lower())
return tmp
def __hash__(self):
items = sorted(self.__dict__.items())
content = ((k, v) for k, v in items if not k.startswith("_"))
return hash(tuple(content))
def __repr__(self):
return f"<ordr3.models.User id={self.id}, username={self.username}>"

53
ordr3/repo.py

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
""" Classes for acessing a datastore """
import abc
from datetime import datetime
from . import models
@ -9,10 +8,8 @@ from . import models @@ -9,10 +8,8 @@ from . import models
class AbstractOrderRepository(abc.ABC):
""" Abstract base class for a datastore """
consumable_stati = {
models.OrderStatus.ORDERED,
models.OrderStatus.COMPLETED,
}
def __init__(self, session):
self.session = session
@abc.abstractmethod
def add(self, order):
@ -23,41 +20,25 @@ class AbstractOrderRepository(abc.ABC): @@ -23,41 +20,25 @@ class AbstractOrderRepository(abc.ABC):
raise NotImplementedError
@abc.abstractmethod
def find_consumables(self, repeat=3, days=365 * 2):
def list(self):
raise NotImplementedError
class FakeOrderRepository(AbstractOrderRepository):
""" Repository implementation for testing """
def __init__(self):
self._orders = []
class SqlAlchemyRepository(AbstractOrderRepository):
""" Repository implementation for SQLAlchemy """
def add(self, order):
""" add an order to the datastore """
self._orders.append(order)
self.session.add(order)
self.session.flush()
def get(self, reference):
""" retrieve an order from the datastore """
return next(o for o in self._orders if o.id == reference)
def find_consumables(self, repeat=3, days=365 * 2):
""" search for orders that are requested often """
unsorted = self._find_consumables(repeat=3, days=365 * 2)
return sorted(unsorted, key=lambda x: x.cas_description)
def _find_consumables(self, repeat=3, days=365 * 2):
""" helper function for find_consumables() implementation """
now = datetime.now()
by_date = (o for o in self._orders if (now - o.created_on).days < days)
relevant = (o for o in by_date if o.status in self.consumable_stati)
counter = {}
for order in sorted(
relevant, reverse=True, key=lambda x: x.created_on
):
item = counter.setdefault(
order.catalog_nr, models.ProposedConsumable(order)
)
item.times = item.times + 1
if item.times == repeat:
yield item.order
return (
self.session.query(models.OrderItem).filter_by(id=reference).one()
)
def list(self):
return (
self.session.query(models.OrderItem)
.order_by(models.OrderItem.created_on.desc())
.all()
)

34
ordr3/services.py

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
from datetime import datetime
from . import models
CONSUMABLE_STATI = {
models.OrderStatus.ORDERED,
models.OrderStatus.COMPLETED,
}
def find_consumables(repo, repeat=3, days=365 * 2):
""" search for orders that are requested often """
unsorted = _find_consumables(repo, repeat, days)
return sorted(unsorted, key=lambda x: x.cas_description)
def _find_consumables(repo, repeat=3, days=365 * 2):
""" helper function for find_consumables() implementation """
now = datetime.now()
by_date = (o for o in repo.list() if (now - o.created_on).days < days)
relevant = (o for o in by_date if o.status in CONSUMABLE_STATI)
counter = {}
for order in relevant:
item = counter.setdefault(
order.catalog_nr, models.ProposedConsumable(order)
)
item.times = item.times + 1
if item.times == repeat:
yield item.order
def create_log_entry(order, status, user):
log_entry = models.LogItem(order.id, status, user.username, user.id)
order.add_to_log(log_entry)

1
pyproject.toml

@ -26,6 +26,7 @@ classifiers = [ @@ -26,6 +26,7 @@ classifiers = [
requires = [
"pyramid >= 1.10",
"passlib[argon2] >= 1.7.2",
"sqlalchemy >= 1.3.15",
]
requires-python = ">=3.7"

19
tests/conftest.py

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, clear_mappers
from ordr3.adapters import metadata, start_mappers
@pytest.fixture
def in_memory_db():
engine = create_engine("sqlite:///:memory:")
metadata.create_all(engine)
return engine
@pytest.fixture
def session(in_memory_db):
start_mappers()
yield sessionmaker(bind=in_memory_db)()
clear_mappers()

151
tests/test_adapters.py

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
from datetime import datetime
import pytest
@pytest.fixture
def order_sql():
return """
INSERT INTO orders (
id, cas_description, catalog_nr, vendor, category, package_size,
unit_price, currency, amount, account, comment, created_on,
created_by, status
) VALUES (
1, "Ethanol", "1-23", "VWR", "SOLVENT", "5 l",
20, "EUR", 2, "DFG", "urgent", "2020-02-04 15:14:13.000000",
"me", "OPEN"
), (
2, "Gloves", "12-3", "Carl Roth", "DISPOSABLE", "100 St.",
40, "USD", 5, "BMBF", "no comment", "2020-02-03 15:14:13.000000",
"you", "APPROVAL"
)
"""
def test_order_mapper(session, order_sql):
from ordr3.models import OrderItem, OrderCategory, OrderStatus
session.execute(order_sql)
expected = [
OrderItem(
1,
"Ethanol",
"1-23",
"VWR",
OrderCategory.SOLVENT,
"5 l",
20.0,
2,
"EUR",
"DFG",
"urgent",
datetime(2020, 2, 4, 15, 14, 13),
"me",
OrderStatus.OPEN,
),
OrderItem(
2,
"Gloves",
"12-3",
"Carl Roth",
OrderCategory.DISPOSABLE,
"100 St.",
40.0,
5,
"USD",
"BMBF",
"no comment",
datetime(2020, 2, 3, 15, 14, 13),
"you",
OrderStatus.APPROVAL,
),
]
assert session.query(OrderItem).all() == expected
def test_log_mapper(session, order_sql):
from ordr3.models import LogItem, OrderItem, OrderStatus
session.execute(order_sql)
session.execute(
"""
INSERT INTO logs
(order_id, status, by, user_id, date)
VALUES
(1, "OPEN", "ME", 1, "2020-02-05 15:14:13.000000"),
(1, "COMPLETED", "YOU", 2, "2020-02-07 15:14:13.000000"),
(1, "ORDERED", "ME", 1, "2020-02-06 15:14:13.000000")
"""
)
expected = [
LogItem(
1, OrderStatus.OPEN, "ME", 1, datetime(2020, 2, 5, 15, 14, 13),
),
LogItem(
1,
OrderStatus.COMPLETED,
"YOU",
2,
datetime(2020, 2, 7, 15, 14, 13),
),
LogItem(
1, OrderStatus.ORDERED, "ME", 1, datetime(2020, 2, 6, 15, 14, 13)
),
]
assert session.query(LogItem).all() == expected
order = session.query(OrderItem).first()
assert order.log == sorted(expected, key=lambda x: x.date)
logitem = session.query(LogItem).first()
assert logitem.order.id == order.id
def test_user_mapper(session):
from ordr3.models import User, UserRole
session.execute(
"""
INSERT INTO users
(id, username, first_name, last_name, email, password, role)
VALUES
(1, "Me", "Jane", "Doe", "jane.doe", "1234", "USER"),
(2, "You", "Jim", "Smith", "jim.smith", "abcd", "ADMIN")
"""
)
expected = [
User(1, "Me", "Jane", "Doe", "jane.doe", "1234", UserRole.USER),
User(2, "You", "Jim", "Smith", "jim.smith", "abcd", UserRole.ADMIN,),
]
assert session.query(User).all() == expected
def test_adapter_behaviour(session):
from ordr3.models import OrderItem, OrderCategory, OrderStatus, LogItem
order = OrderItem(
None,
"Ethanol",
"1-23",
"VWR",
OrderCategory.SOLVENT,
"5 l",
20.0,
2,
"EUR",
)
session.add(order)
log_entry = LogItem(order.id, OrderStatus.OPEN, "ME", 123)
order.add_to_log(log_entry)
from_db = session.query(OrderItem).first()
assert len(from_db.log) == 1
assert from_db.created_by == "ME"
assert from_db.created_on == from_db.log[0].date
assert from_db.status == OrderStatus.OPEN

85
tests/test_models.py

@ -1,7 +1,10 @@ @@ -1,7 +1,10 @@
import pytest
def test_orderitem_init():
from ordr3.models import OrderItem
order = OrderItem(*list("ABCDEFGHIJK"))
order = OrderItem(*list("ABCDEFGHIJKLMN"))
assert order.id == "A"
assert order.cas_description == "B"
@ -14,6 +17,17 @@ def test_orderitem_init(): @@ -14,6 +17,17 @@ def test_orderitem_init():
assert order.currency == "I"
assert order.account == "J"
assert order.comment == "K"
assert order.created_on == "L"
assert order.created_by == "M"
assert order.status == "N"
def test_orderitem_repr():
from ordr3.models import OrderItem
order = OrderItem(*list("ABCDEFGHIJK"))
assert repr(order) == "<ordr3.models.OrderItem id=A>"
def test_orderitem_total_price():
@ -28,7 +42,7 @@ def test_orderitem_add_to_log_empty_log(): @@ -28,7 +42,7 @@ def test_orderitem_add_to_log_empty_log():
from ordr3.models import OrderItem, LogItem
order = OrderItem(*list("ABCDEFGHIJK"))
log_item = LogItem(order, "critical", "me", "yesterday")
log_item = LogItem(order, "critical", "me", "myid", "yesterday")
order.add_to_log(log_item)
assert order.created_on == log_item.date
@ -40,8 +54,8 @@ def test_orderitem_add_to_log_non_empty_log(): @@ -40,8 +54,8 @@ def test_orderitem_add_to_log_non_empty_log():
from ordr3.models import OrderItem, LogItem
order = OrderItem(*list("ABCDEFGHIJK"))
log_item_1 = LogItem(order, "critical", "me", "yesterday")
log_item_2 = LogItem(order, "normal", "you", "today")
log_item_1 = LogItem(order, "critical", "me", "myid", "yesterday")
log_item_2 = LogItem(order, "normal", "you", "yourid", "today")
order.add_to_log(log_item_1)
order.add_to_log(log_item_2)
@ -50,26 +64,61 @@ def test_orderitem_add_to_log_non_empty_log(): @@ -50,26 +64,61 @@ def test_orderitem_add_to_log_non_empty_log():
assert order.status == log_item_2.status
def test_logitem_init_with_date():
from ordr3.models import OrderItem, LogItem
def test_logitem_init():
from ordr3.models import LogItem
order = OrderItem(*list("ABCDEFGHIJK"))
log_item = LogItem(order, "critical", "me", "yesterday")
log_item = LogItem(1, "critical", "me", "myid", "yesterday")
assert log_item.order_id == order.id
assert log_item.order_id == 1
assert log_item.status == "critical"
assert log_item.by == "me"
assert log_item.user_id == "myid"
assert log_item.date == "yesterday"
def test_logitem_init_with_out_date():
from ordr3.models import OrderItem, LogItem
from datetime import datetime
def test_user_init():
from ordr3.models import User
order = OrderItem(*list("ABCDEFGHIJK"))
log_item = LogItem(order, "critical", "me")
user = User(*list("ABCDEFG"))
assert log_item.order_id == order.id
assert log_item.status == "critical"
assert log_item.by == "me"
assert isinstance(log_item.date, datetime)
assert user.id == "A"
assert user.username == "B"
assert user.first_name == "C"
assert user.last_name == "D"
assert user.email == "E"
assert user.password == "F"
assert user.role == "G"
def test_user_principal():
from ordr3.models import User
user = User(*list("ABCDEFG"))
assert user.principal == "user:A"
@pytest.mark.parametrize(
"role_str,roles",
[
("NEW", ["role:new"]),
("USER", ["role:user"]),
("PURCHASER", ["role:user", "role:purchaser"]),
("ADMIN", ["role:user", "role:purchaser", "role:admin"]),
("INACTIVE", ["role:inactive"]),
],
)
def test_user_principals(role_str, roles):
from ordr3.models import User, UserRole
user = User(*list("ABCDEF"), UserRole[role_str])
assert user.principals == ["user:A"] + roles
def test_user_repr():
from ordr3.models import User
user = User(*list("ABCDEFG"))
assert repr(user) == "<ordr3.models.User id=A, username=B>"

147
tests/test_repo.py

@ -1,75 +1,78 @@ @@ -1,75 +1,78 @@
import pytest
@pytest.fixture
def prefilled_repo():
from datetime import datetime, timedelta
from itertools import count
from ordr3.models import OrderItem, OrderStatus
from ordr3.repo import FakeOrderRepository
i = count()
catalog = {
1: "Ethanol",
2: "Aceton",
3: "NaOH",
4: "Coffee",
5: "Water",
}
def _create_order(item, date, status):
order = OrderItem(next(i), catalog[item], item, "", "", "", "", "")
order.created_on = date
order.status = status
return order
month = timedelta(days=30)
today = datetime.now()
repo = FakeOrderRepository()
# should be consumables
repo.add(_create_order(1, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(1, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(1, today - month * 3, OrderStatus.COMPLETED))
repo.add(_create_order(2, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(2, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(2, today - month * 3, OrderStatus.COMPLETED))
# no consumable, only two repeats
repo.add(_create_order(3, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(3, today - month * 2, OrderStatus.ORDERED))
# no consumable, only two repeats in the last two years
repo.add(_create_order(4, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(4, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(4, today - month * 50, OrderStatus.ORDERED))
# no consumable, one order on hold
repo.add(_create_order(5, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(5, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(5, today - month * 3, OrderStatus.HOLD))
return repo
def test_fakerepo_add():
from ordr3.repo import FakeOrderRepository
repo = FakeOrderRepository()
repo.add("Something")
assert len(repo._orders) == 1
assert repo._orders[0] == "Something"
def test_fakerepo_get(prefilled_repo):
reference = 8
result = prefilled_repo.get(reference)
assert result.id == reference
def test_fakerepo_find_consumables(prefilled_repo):
result = prefilled_repo.find_consumables()
assert len(result) == 2
assert result == [prefilled_repo._orders[3], prefilled_repo._orders[0]]
@pytest.fixture()
def example_orders():
from datetime import datetime
from ordr3.models import OrderItem, OrderCategory, OrderStatus
return [
OrderItem(
1,
"Ethanol",
"1-23",
"VWR",
OrderCategory.SOLVENT,
"5 l",
20.0,
2,
"EUR",
"DFG",
"urgent",
datetime(2020, 2, 3, 15, 14, 13),
"me",
OrderStatus.OPEN,
),
OrderItem(
2,
"Gloves",
"12-3",
"Carl Roth",
OrderCategory.DISPOSABLE,
"100 St.",
40.0,
5,
"USD",
"BMBF",
"no comment",
datetime(2020, 2, 4, 15, 14, 13),
"you",
OrderStatus.APPROVAL,
),
]
def test_sql_repo_add(session, example_orders):
from ordr3.repo import SqlAlchemyRepository
from ordr3.models import OrderItem
repo = SqlAlchemyRepository(session)
repo.add(example_orders[0])
session.flush()
order = session.query(OrderItem).first()
assert order == example_orders[0]
def test_sql_repo_get(session, example_orders):
from ordr3.repo import SqlAlchemyRepository
repo = SqlAlchemyRepository(session)
repo.add(example_orders[0])
repo.add(example_orders[1])
session.flush()
assert example_orders[1] == repo.get(2)
def test_sql_repo_list(session, example_orders):
from ordr3.repo import SqlAlchemyRepository
earlier, later = example_orders
repo = SqlAlchemyRepository(session)
repo.add(earlier)
repo.add(later)
session.flush()
assert repo.list() == [later, earlier]

99
tests/test_services.py

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
from datetime import datetime, timedelta
import pytest
from ordr3.repo import AbstractOrderRepository
class FakeOrderRepository(AbstractOrderRepository):
""" Repository implementation for testing """
def __init__(self, session):
self._orders = set()
def add(self, order):
""" add an order to the datastore """
self._orders.add(order)
def get(self, reference):
""" retrieve an order from the datastore """
return next(o for o in self._orders if o.id == reference)
def list(self):
return sorted(self._orders, reverse=True, key=lambda x: x.created_on)
@pytest.fixture
def prefilled_repo():
from itertools import count
from ordr3.models import OrderItem, OrderStatus
i = count()
catalog = {
1: "Ethanol",
2: "Aceton",
3: "NaOH",
4: "Coffee",
5: "Water",
}
def _create_order(item, date, status):
order = OrderItem(next(i), catalog[item], item, "", "", "", "", "")
order.created_on = date
order.status = status
return order
month = timedelta(days=30)
today = datetime.now()
repo = FakeOrderRepository(session=None)
# should be consumables
repo.add(_create_order(1, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(1, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(1, today - month * 3, OrderStatus.COMPLETED))
repo.add(_create_order(2, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(2, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(2, today - month * 3, OrderStatus.COMPLETED))
# no consumable, only two repeats
repo.add(_create_order(3, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(3, today - month * 2, OrderStatus.ORDERED))
# no consumable, only two repeats in the last two years
repo.add(_create_order(4, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(4, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(4, today - month * 50, OrderStatus.ORDERED))
# no consumable, one order on hold
repo.add(_create_order(5, today - month * 1, OrderStatus.ORDERED))
repo.add(_create_order(5, today - month * 2, OrderStatus.ORDERED))
repo.add(_create_order(5, today - month * 3, OrderStatus.HOLD))
return repo
def test_service_find_consumables(prefilled_repo):
from ordr3.services import find_consumables
result = find_consumables(prefilled_repo)
assert len(result) == 2
assert [o.id for o in result] == [3, 0]
def test_create_log_entry(prefilled_repo):
from ordr3.services import create_log_entry
from ordr3.models import OrderStatus, User
order = prefilled_repo.get(1)
user = User(*list("ABCDEFG"))
create_log_entry(order, OrderStatus.APPROVAL, user)
assert len(order.log) == 1
log_entry = order.log[0]
assert log_entry.order_id == order.id
assert log_entry.status == OrderStatus.APPROVAL
assert log_entry.by == "B"
assert log_entry.user_id == "A"
assert isinstance(log_entry.date, datetime)
assert order.status == log_entry.status
assert order.created_by == log_entry.by
assert order.created_on == log_entry.date
Loading…
Cancel
Save