dodil/erp-platform

templates/brick-example/tests/test_rules.py

"""The business rules, with no database — each test is one sentence a business person would agree to."""
from decimal import Decimal

import pytest

from brick_example.domain.quote import ACCEPTED, SENT, Quote, RuleBroken


def draft() -> Quote:
    return Quote(quote_id="Q-1", customer="Acme", owner="alice@shop.example")


def test_the_total_is_exact():
    q = draft()
    q.add_line("Banner", 3, Decimal("33.33"))
    q.add_line("Print", 1, Decimal("0.01"))
    assert q.total == Decimal("100.00")


def test_an_empty_quote_cannot_be_sent():
    with pytest.raises(RuleBroken, match="empty"):
        draft().send()


def test_a_sent_quote_cannot_change():
    q = draft()
    q.add_line("Banner", 1, Decimal("10"))
    q.send()
    with pytest.raises(RuleBroken, match="sent quote cannot be changed"):
        q.add_line("More", 1, Decimal("1"))


def test_only_a_sent_quote_can_be_accepted():
    with pytest.raises(RuleBroken, match="only a sent quote"):
        draft().accept()


def test_accepting_twice_changes_nothing():
    q = draft()
    q.add_line("Banner", 1, Decimal("10"))
    q.send()
    q.accept()
    q.accept()
    assert q.status == ACCEPTED


def test_line_numbers_are_never_reused():
    q = draft()
    q.add_line("A", 1, Decimal("1"))
    q.add_line("B", 1, Decimal("1"))
    q.remove_line(2)
    assert q.add_line("C", 1, Decimal("1")).line_no == 2 and q.status != SENT


def test_a_unique_race_lost_at_commit_is_409_and_any_other_integrity_error_is_not(tmp_path):
    """_on_engine: on DataK3 a racer's duplicate key can surface at COMMIT, after the use case returned. It is the
    retry-safe 409, never a 500; a NOT NULL or other integrity failure still raises as itself."""
    import pytest
    from sqlalchemy import create_engine, text
    from sqlalchemy.exc import IntegrityError
    from erp_platform.bricks import Refused
    from brick_example.application.use_cases import _on_engine

    e = create_engine(f"sqlite:///{tmp_path / 'race.db'}")
    with e.begin() as c:
        c.execute(text("CREATE TABLE claim (k TEXT PRIMARY KEY, v TEXT NOT NULL)"))
        c.execute(text("INSERT INTO claim VALUES ('a', 'x')"))
    with pytest.raises(Refused) as lost:
        with _on_engine(e) as repo:
            repo.conn.execute(text("INSERT INTO claim VALUES ('a', 'y')"))
    assert lost.value.status == 409
    with pytest.raises(IntegrityError):
        with _on_engine(e) as repo:
            repo.conn.execute(text("INSERT INTO claim VALUES ('b', NULL)"))