Make all tests pass again.

This commit is contained in:
Alexis MÃtaireau 2020-04-08 17:55:52 +02:00
parent b7b31c23d7
commit a1cd995d9d
8 changed files with 144 additions and 171 deletions

View file

@ -119,21 +119,24 @@ class Groups(PersistedBase):
__lock__ = threading.Lock() __lock__ = threading.Lock()
groups: Dict[str, Group] groups: Dict[str, Group]
@classmethod
def get_path(cls):
return cls.get_root() / "groups.yml"
@classmethod @classmethod
def load(cls): def load(cls):
path = cls.get_root() / "groups.yml" path = cls.get_path()
if path.exists(): if path.exists():
data = yaml.safe_load(path.read_text()) data = yaml.safe_load(path.read_text())
data = {k: v for k, v in data.items() if k in cls.__dataclass_fields__} data = {k: v for k, v in data.items() if k in cls.__dataclass_fields__}
else: else:
data = {"groups": {}} data = {"groups": {}}
groups = cls(**data) groups = cls(**data)
groups.path = path
return groups return groups
def persist(self): def persist(self):
with self.__lock__: with self.__lock__:
self.path.write_text(self.dump()) self.get_path().write_text(self.dump())
def add_group(self, group): def add_group(self, group):
assert group.id not in self.groups, "Un groupe avec ce nom existe déjà." assert group.id not in self.groups, "Un groupe avec ce nom existe déjà."

View file

@ -4,5 +4,5 @@ pytest-asyncio
usine usine
pyquery pyquery
pytest-watch pytest-watch
python-emails #python-emails
Weasyprint Weasyprint

View file

@ -8,7 +8,7 @@ from roll.testing import Client as BaseClient
from copanier import app as copanier_app from copanier import app as copanier_app
from copanier import config as kconfig from copanier import config as kconfig
from copanier.utils import create_token from copanier.utils import create_token
from copanier.models import Delivery, Person, Product from copanier.models import Delivery, Person, Product, Producer, Groups, Group
def pytest_configure(config): def pytest_configure(config):
@ -25,7 +25,7 @@ def pytest_runtest_setup(item):
class Client(BaseClient): class Client(BaseClient):
content_type = 'application/x-www-form-urlencoded; charset=utf-8' content_type = "application/x-www-form-urlencoded; charset=utf-8"
headers = {} headers = {}
async def request( async def request(
@ -67,12 +67,44 @@ def app(): # Requested by Roll testing utilities.
@pytest.fixture @pytest.fixture
def delivery(): def delivery():
return Delivery( return Delivery(
name="Andines", name="CRAC d'automne",
contact="mister@me.me", contact="mister@me.me",
from_date=datetime.now() + timedelta(days=10), from_date=datetime.now() + timedelta(days=10),
to_date=datetime.now() + timedelta(days=10), to_date=datetime.now() + timedelta(days=10),
order_before=datetime.now() + timedelta(days=7), order_before=datetime.now() + timedelta(days=7),
products=[Product(name="Lait", ref="123", price=1.5)], products=[
Product(name="Lait", producer="ferme-du-coin", ref="lait", price=1.5,)
],
producers={"ferme-du-coin": Producer(name="Ferme du coin", id="ferme-du-coin")},
)
@pytest.fixture
def groups():
fractal_brocolis = Group(
id="fractal-brocolis", name="The Fractal Brocolis", members=["foo@bar.org"]
)
groups = Groups({"fractal-brocolis": fractal_brocolis})
groups.persist()
return groups
@pytest.fixture
def yaourt():
return Product(
ref="yaourt",
unit="pot 125ml",
name="Yaourt",
price="3.5",
packing=4,
producer="ferme-du-coin",
)
@pytest.fixture
def fromage():
return Product(
ref="fromage", name="Fromage", price="9.2", producer="ferme-du-coin",
) )

View file

@ -1,56 +0,0 @@
from io import BytesIO
import pytest
from openpyxl import Workbook
from copanier import imports
from copanier.models import Product, Delivery
@pytest.fixture
def workbook():
def _(rows, headers=["ref", "name", "price"]):
wb = Workbook()
ws = wb.active
ws.append(headers)
for row in rows:
ws.append(row)
return wb
return _
def test_mandatory_headers_with_xlsx(delivery, workbook):
with pytest.raises(ValueError):
imports.products_and_producers_from_xlsx(
delivery,
workbook([("123", "Chocolat", "2.3")], headers=["ref", "nom", "prix"]),
)
def test_bad_xlsx_file(delivery, workbook):
with pytest.raises(ValueError):
imports.products_and_producers_from_xlsx(delivery, BytesIO(b"pouet"))
def test_simple_xlsx_import(delivery, workbook):
delivery.persist()
assert delivery.products == [Product(ref="123", name="Lait", price=1.5)]
imports.products_and_producers_from_xlsx(
delivery, workbook([("123", "Lait cru", 1.3)])
)
assert Delivery.load(delivery.id).products == [
Product(ref="123", name="Lait cru", price=1.3)
]
def test_simple_xlsx_import_invalid_price(delivery, workbook):
delivery.persist()
assert delivery.products == [Product(ref="123", name="Lait", price=1.5)]
with pytest.raises(ValueError):
imports.products_and_producers_from_xlsx(
delivery, workbook([("123", "Lait cru", "invalid")])
)
assert Delivery.load(delivery.id).products == [
Product(ref="123", name="Lait", price=1.5)
]

View file

@ -3,7 +3,15 @@ from datetime import datetime, timedelta
import pytest import pytest
from copanier import config from copanier import config
from copanier.models import Delivery, Product, Person, Order, ProductOrder, Groups, Group from copanier.models import (
Delivery,
Product,
Person,
Order,
ProductOrder,
Groups,
Group,
)
now = datetime.now now = datetime.now
@ -108,11 +116,11 @@ def test_order_has_adjustments():
def test_order_total(delivery): def test_order_total(delivery):
delivery.products = [Product(name="Lait", ref="123", price=1.5)] delivery.products = [Product(name="Lait", ref="123", price=1.5)]
order = Order() order = Order()
assert order.total(delivery.products) == 0 assert order.total(delivery.products, delivery) == 0
order.products["123"] = ProductOrder(wanted=2) order.products["123"] = ProductOrder(wanted=2)
assert order.total(delivery.products) == 3 assert order.total(delivery.products, delivery) == 3
order.products["unknown"] = ProductOrder(wanted=2) order.products["unknown"] = ProductOrder(wanted=2)
assert order.total(delivery.products) == 3 assert order.total(delivery.products, delivery) == 3
def test_can_persist_delivery(delivery): def test_can_persist_delivery(delivery):
@ -176,22 +184,22 @@ def test_archive_delivery(delivery):
def test_group_management(): def test_group_management():
ndp = Group(id='nid-de-poules', name='Nid de poules', members=['someone@domain.tld']) ndp = Group(
assert ndp.id == 'nid-de-poules' id="nid-de-poules", name="Nid de poules", members=["someone@domain.tld"]
assert ndp.name == 'Nid de poules' )
assert ndp.id == "nid-de-poules"
assert ndp.name == "Nid de poules"
assert len(ndp.members) == 1 assert len(ndp.members) == 1
groups = Groups.load() groups = Groups.load()
groups.persist() groups.persist()
groups.add_group(ndp) groups.add_group(ndp)
groups.add_user('simon@tld', ndp.id) groups.add_user("simon@tld", ndp.id)
assert 'simon@tld' in groups.groups[ndp.id].members assert "simon@tld" in groups.groups[ndp.id].members
ladouce = Group(id='la-douce', name='La douce', members=[]) ladouce = Group(id="la-douce", name="La douce", members=[])
groups.add_group(ladouce) groups.add_group(ladouce)
groups.add_user('simon@tld', ladouce.id) groups.add_user("simon@tld", ladouce.id)
assert 'simon@tld' in groups.groups[ladouce.id].members assert "simon@tld" in groups.groups[ladouce.id].members
assert 'simon@tld' not in groups.groups[ndp.id].members assert "simon@tld" not in groups.groups[ndp.id].members

View file

@ -6,20 +6,18 @@ from copanier import reports
from copanier.models import Order, Product, ProductOrder from copanier.models import Order, Product, ProductOrder
def test_summary_report(delivery): def test_summary_report(delivery, yaourt, fromage):
delivery.products[0].packing = 6 delivery.products[0].packing = 6
delivery.products.append( delivery.products.append(yaourt)
Product(ref="456", name="yaourt", price="3.5", packing=4, unit="pot 125ml") delivery.products.append(fromage)
) delivery.orders["fractals-brocoli"] = Order(
delivery.products.append(Product(ref="789", name="fromage", price="9.2")) products={"lait": ProductOrder(wanted=1), "yaourt": ProductOrder(wanted=4)}
delivery.orders["foo@bar.org"] = Order(
products={"123": ProductOrder(wanted=1), "456": ProductOrder(wanted=4)}
) )
delivery.persist() delivery.persist()
wb = load_workbook(filename=BytesIO(reports.summary(delivery))) wb = load_workbook(filename=BytesIO(reports.summary(delivery)))
assert list(wb.active.values) == [ assert list(wb.active.values) == [
("ref", "produit", "prix unitaire", "quantité commandée", "unité", "total"), ("ref", "produit", "prix unitaire", "quantité commandée", "unité", "total"),
("123", "Lait", 1.5, 1, None, 1.5), ("lait", "Lait", 1.5, 1, None, 1.5),
("456", "yaourt (pot 125ml)", 3.5, 4, "pot 125ml", 14), ("yaourt", "Yaourt", 3.5, 4, "pot 125ml", 14),
(None, None, None, None, "Total", 15.5), (None, None, None, None, "Total", 15.5),
] ]

View file

@ -10,12 +10,21 @@ from copanier.models import Delivery, Order, ProductOrder, Product
pytestmark = pytest.mark.asyncio pytestmark = pytest.mark.asyncio
async def test_empty_home(client): async def test_home_redirects_to_group_if_needed(client):
client.login(email="new@example.org")
resp = await client.get("/")
assert resp.status == 302
assert resp.headers["Location"] == "/groupes"
async def test_empty_home(client, delivery, groups):
groups.persist()
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 200 assert resp.status == 200
async def test_home_should_list_active_delivery(client, delivery): async def test_home_should_list_active_delivery(client, delivery, groups):
groups.persist()
delivery.persist() delivery.persist()
resp = await client.get("/") resp = await client.get("/")
assert resp.status == 200 assert resp.status == 200
@ -55,12 +64,12 @@ async def test_create_delivery(client):
async def test_place_order_with_session(client, delivery): async def test_place_order_with_session(client, delivery):
delivery.persist() delivery.persist()
body = {"wanted:123": "3"} body = {"wanted:lait": "3"}
resp = await client.post(f"/distribution/{delivery.id}/commander", body=body) resp = await client.post(f"/distribution/{delivery.id}/commander", body=body)
assert resp.status == 302 assert resp.status == 302
delivery = Delivery.load(id=delivery.id) delivery = Delivery.load(id=delivery.id)
assert delivery.orders["foo@bar.org"] assert "fractal-brocolis" in delivery.orders.keys()
assert delivery.orders["foo@bar.org"].products["123"].wanted == 3 assert delivery.orders["fractal-brocolis"].products["lait"].wanted == 3
async def test_place_empty_order(client, delivery): async def test_place_empty_order(client, delivery):
@ -72,7 +81,9 @@ async def test_place_empty_order(client, delivery):
async def test_place_empty_order_should_delete_previous(client, delivery): async def test_place_empty_order_should_delete_previous(client, delivery):
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=1)}) delivery.orders["fractal-brocolis"] = Order(
products={"lait": ProductOrder(wanted=1)}
)
delivery.persist() delivery.persist()
resp = await client.post(f"/distribution/{delivery.id}/commander", body={}) resp = await client.post(f"/distribution/{delivery.id}/commander", body={})
assert resp.status == 302 assert resp.status == 302
@ -82,89 +93,66 @@ async def test_place_empty_order_should_delete_previous(client, delivery):
async def test_place_order_with_empty_string(client, delivery): async def test_place_order_with_empty_string(client, delivery):
delivery.persist() delivery.persist()
body = {"wanted:123": ""} # User deleted the field value. body = {"wanted:lait": ""} # User deleted the field value.
resp = await client.post(f"/distribution/{delivery.id}/commander", body=body) resp = await client.post(f"/distribution/{delivery.id}/commander", body=body)
assert resp.status == 302 assert resp.status == 302
delivery = Delivery.load(id=delivery.id) delivery = Delivery.load(id=delivery.id)
assert not delivery.orders assert not delivery.orders
async def test_get_place_order_with_closed_delivery(client, delivery, monkeypatch): async def test_get_place_order_if_not_adjustable(client, delivery, monkeypatch):
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"]) monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=1)}) delivery.orders["fractal-brocolis"] = Order(
products={"lait": ProductOrder(wanted=1)}
)
delivery.persist() delivery.persist()
assert delivery.status == delivery.CLOSED assert delivery.status == delivery.CLOSED
resp = await client.get(f"/distribution/{delivery.id}/commander") resp = await client.get(f"/distribution/{delivery.id}/commander")
doc = pq(resp.body) doc = pq(resp.body)
assert doc('[name="wanted:123"]').attr("readonly") assert doc('[name="wanted:lait"]').attr("readonly")
assert not doc('[name="adjustment:123"]') assert not doc('[name="adjustment:lait"]')
assert not doc('input[type="submit"]') assert not doc('input[type="submit"]')
async def test_get_place_order_with_adjustment_status(client, delivery): async def test_get_place_order_with_adjustment_status(client, delivery, yaourt, fromage):
resp = await client.get(f"/distribution/{delivery.id}/commander") resp = await client.get(f"/distribution/{delivery.id}/commander")
doc = pq(resp.body) doc = pq(resp.body)
assert not doc('[name="wanted:123"]').attr("readonly") assert not doc('[name="wanted:lait"]').attr("readonly")
assert not doc('[name="adjustment:123"]') assert not doc('[name="adjustment:lait"]')
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.products[0].packing = 6 delivery.products[0].packing = 6
delivery.products.append(Product(ref="456", name="yaourt", price="3.5", packing=4)) delivery.products.append(yaourt)
delivery.products.append(Product(ref="789", name="fromage", price="9.2")) delivery.products.append(fromage)
delivery.orders["foo@bar.org"] = Order( delivery.orders["fractal-brocolis"] = Order(
products={"123": ProductOrder(wanted=1), "456": ProductOrder(wanted=4)} products={
"lait": ProductOrder(wanted=1),
"yaourt": ProductOrder(wanted=4)
}
) )
delivery.persist() delivery.persist()
assert delivery.status == delivery.ADJUSTMENT assert delivery.status == delivery.ADJUSTMENT
resp = await client.get(f"/distribution/{delivery.id}/commander") resp = await client.get(f"/distribution/{delivery.id}/commander")
doc = pq(resp.body) doc = pq(resp.body)
assert doc('[name="wanted:123"]').attr("readonly") assert doc('[name="wanted:lait"]').attr("readonly")
assert doc('[name="adjustment:123"]') assert doc('[name="adjustment:lait"]')
assert not doc('[name="adjustment:123"]').attr("readonly") assert not doc('[name="adjustment:lait"]').attr("readonly")
assert doc('[name="adjustment:123"]').attr("min") == "-1" assert doc('[name="adjustment:lait"]').attr("min") == "-1"
assert doc('[name="wanted:456"]').attr("readonly") assert doc('[name="wanted:yaourt"]').attr("readonly")
assert doc('[name="adjustment:456"]') assert doc('[name="adjustment:yaourt"]')
# Already adjusted. # Already adjusted.
assert doc('[name="adjustment:456"]').attr("readonly") assert doc('[name="adjustment:yaourt"]').attr("readonly")
assert doc('[name="adjustment:789"]') assert doc('[name="adjustment:fromage"]')
# Needs no adjustment. # Needs no adjustment.
assert doc('[name="adjustment:789"]').attr("readonly") assert doc('[name="adjustment:fromage"]').attr("readonly")
assert doc('input[type="submit"]') assert doc('input[type="submit"]')
async def test_get_place_order_with_closed_delivery_but_adjustments(client, delivery):
delivery.order_before = datetime.now() - timedelta(days=1)
delivery.orders["foo@bar.org"] = Order(
products={"123": ProductOrder(wanted=1, adjustment=1)}
)
delivery.persist()
assert delivery.status == delivery.CLOSED
resp = await client.get(f"/distribution/{delivery.id}/commander")
doc = pq(resp.body)
assert doc('[name="wanted:123"]').attr("readonly")
assert doc('[name="adjustment:123"]')
async def test_get_place_order_with_closed_delivery_but_force(client, delivery):
delivery.order_before = datetime.now() - timedelta(days=1)
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=1)})
delivery.persist()
assert delivery.status == delivery.CLOSED
resp = await client.get(f"/distribution/{delivery.id}/commander")
doc = pq(resp.body)
assert doc('[name="wanted:123"]').attr("readonly") is not None
assert not doc('[name="adjustment:123"]')
resp = await client.get(f"/distribution/{delivery.id}/commander?adjust")
doc = pq(resp.body)
assert doc('[name="wanted:123"]').attr("readonly") is not None
assert doc('[name="adjustment:123"]')
async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypatch): async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypatch):
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"]) monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.persist() delivery.persist()
body = {"wanted:123": "3"} body = {"wanted:lait": "3"}
resp = await client.post(f"/distribution/{delivery.id}/commander", body=body) resp = await client.post(f"/distribution/{delivery.id}/commander", body=body)
assert resp.status == 302 assert resp.status == 302
delivery = Delivery.load(id=delivery.id) delivery = Delivery.load(id=delivery.id)
@ -174,45 +162,45 @@ async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypat
async def test_get_adjust_product(client, delivery): async def test_get_adjust_product(client, delivery):
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.products[0].packing = 6 delivery.products[0].packing = 6
delivery.orders["foo@bar.org"] = Order( delivery.orders["fractal-brocolis"] = Order(
products={"123": ProductOrder(wanted=2, adjustment=1)} products={"lait": ProductOrder(wanted=2, adjustment=1)}
) )
delivery.persist() delivery.persist()
assert delivery.status == delivery.ADJUSTMENT assert delivery.status == delivery.ADJUSTMENT
resp = await client.get(f"/distribution/{delivery.id}/ajuster/123") resp = await client.get(f"/distribution/{delivery.id}/ajuster/lait")
doc = pq(resp.body) doc = pq(resp.body)
assert doc('[name="foo@bar.org"]') assert doc('[name="fractal-brocolis"]')
assert doc('[name="foo@bar.org"]').attr("value") == "1" assert doc('[name="fractal-brocolis"]').attr("value") == "1"
async def test_post_adjust_product(client, delivery): async def test_post_adjust_product(client, delivery):
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.products[0].packing = 6 delivery.products[0].packing = 6
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=2)}) delivery.orders["fractal-brocolis"] = Order(products={"lait": ProductOrder(wanted=2)})
delivery.persist() delivery.persist()
assert delivery.status == delivery.ADJUSTMENT assert delivery.status == delivery.ADJUSTMENT
body = {"foo@bar.org": "1"} body = {"fractal-brocolis": "1"}
resp = await client.post(f"/distribution/{delivery.id}/ajuster/123", body=body) resp = await client.post(f"/distribution/{delivery.id}/ajuster/lait", body=body)
assert resp.status == 302 assert resp.status == 302
delivery = Delivery.load(id=delivery.id) delivery = Delivery.load(id=delivery.id)
assert delivery.orders["foo@bar.org"].products["123"].wanted == 2 assert delivery.orders["fractal-brocolis"].products["lait"].wanted == 2
assert delivery.orders["foo@bar.org"].products["123"].adjustment == 1 assert delivery.orders["fractal-brocolis"].products["lait"].adjustment == 1
async def test_only_staff_can_adjust_product(client, delivery, monkeypatch): async def test_only_staff_can_adjust_product(client, delivery, monkeypatch):
delivery.order_before = datetime.now() - timedelta(days=1) delivery.order_before = datetime.now() - timedelta(days=1)
delivery.products[0].packing = 6 delivery.products[0].packing = 6
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=2)}) delivery.orders["fractal-brocolis"] = Order(products={"lait": ProductOrder(wanted=2)})
delivery.persist() delivery.persist()
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"]) monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
resp = await client.get(f"/distribution/{delivery.id}/ajuster/123") resp = await client.get(f"/distribution/{delivery.id}/ajuster/lait")
assert resp.status == 302 assert resp.status == 302
body = {"foo@bar.org": "1"} body = {"fractal-brocolis": "1"}
resp = await client.post(f"/distribution/{delivery.id}/ajuster/123", body=body) resp = await client.post(f"/distribution/{delivery.id}/ajuster/lait", body=body)
assert resp.status == 302 assert resp.status == 302
delivery = Delivery.load(id=delivery.id) delivery = Delivery.load(id=delivery.id)
assert delivery.orders["foo@bar.org"].products["123"].wanted == 2 assert delivery.orders["fractal-brocolis"].products["lait"].wanted == 2
assert delivery.orders["foo@bar.org"].products["123"].adjustment == 0 assert delivery.orders["fractal-brocolis"].products["lait"].adjustment == 0
async def test_export_products(client, delivery): async def test_export_products(client, delivery):
@ -221,15 +209,15 @@ async def test_export_products(client, delivery):
wb = load_workbook(filename=BytesIO(resp.body)) wb = load_workbook(filename=BytesIO(resp.body))
assert list(wb.active.values) == [ assert list(wb.active.values) == [
( (
"name", 'name',
"ref", 'ref',
"price", 'price',
"unit", 'last_update',
"description", 'unit',
"url", 'description',
"img", 'packing',
"packing", 'producer',
"producer", 'rupture'
), ),
("Lait", "123", 1.5, None, None, None, None, None, None), ("Lait", "lait", 1.5, delivery.products[0].last_update, None, None, None, "ferme-du-coin", None),
] ]