AutoRpa/rpa_oms/tests/test_cart_settlement_outbou...

238 lines
7.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 接口 2:从表 2 取 push_status=0,组装文档报文。假连接录 SQL,不连真库,不发 HTTP。
from __future__ import annotations
from decimal import Decimal
from app.outbound.cart_settlement import (
MAX_BATCH,
batch_limit,
build_cart_settlement_envelope,
load_cart_settlement_envelope,
)
from app.outbound.oracle_repo import OracleCartSettlementRepo
from tests.fake_oracle import FakeDb
# 构造一条表 2 行,供组装层单测用。
def _row(**overrides):
row = {
"fid": 100001,
"operate_type": 1,
"oms_id": 20260928000001,
"platform_type": 48,
"supplier_name": "示例供应商",
"bar_code": "06901234567890",
"purchase_price": Decimal("128.5000"),
"purchase_qty": 10,
"purchase_total": Decimal("1285.0000"),
"status": 1,
"fail_reason": None,
"pay_order_no": None,
"pay_order_dtl_id": None,
"org_code": "901",
"push_status": 0,
"multi_platform_flag": 0,
}
row.update(overrides)
return row
# 把表 2 行放进假库。list_unpushed 走真 SQL,调用记在 db.calls。
def _open(rows: list[dict]) -> tuple[OracleCartSettlementRepo, FakeDb]:
db = FakeDb()
for row in rows:
db.carts[row["fid"]] = row
return OracleCartSettlementRepo(db.connection()), db
# 取出最近一次 execute 的 SQL 和绑定。
def _last_execute(db: FakeDb) -> tuple[str, dict]:
execute_calls = [call for call in db.calls if call[0] == "execute"]
assert execute_calls
_, sql, params = execute_calls[-1]
return sql, params or {}
# 未推行按文档信封字段组装,金额四位小数字符串。
def test_assembles_unpushed_rows_as_document_envelope():
repo, db = _open(
[
_row(),
_row(
fid=100002,
operate_type=2,
platform_type=66,
status=None,
pay_order_no="PAY20260928000001",
pay_order_dtl_id="1233",
),
]
)
envelope = load_cart_settlement_envelope(repo)
assert envelope == {
"data": [
{
"fid": 100001,
"operateType": 1,
"omsId": 20260928000001,
"platformType": 48,
"supplierName": "示例供应商",
"barCode": "06901234567890",
"purchasePrice": "128.5000",
"purchaseQty": 10,
"purchaseTotal": "1285.0000",
"status": 1,
"failReason": None,
"payOrderNo": None,
"payOrderDtlId": None,
"orgCode": "901",
},
{
"fid": 100002,
"operateType": 2,
"omsId": 20260928000001,
"platformType": 66,
"supplierName": "示例供应商",
"barCode": "06901234567890",
"purchasePrice": "128.5000",
"purchaseQty": 10,
"purchaseTotal": "1285.0000",
"status": None,
"failReason": None,
"payOrderNo": "PAY20260928000001",
"payOrderDtlId": "1233",
"orgCode": "901",
},
]
}
sql, params = _last_execute(db)
assert "rpa_cart_settlement_goods_data" in sql.lower()
assert "push_status = 0" in sql.lower()
assert params["limit"] == 500
# 失败行金额为 NULL 时该行置空,同批其它行照常组装。
def test_null_amounts_do_not_fail_the_batch():
repo, _db = _open(
[
_row(
fid=1,
status=2,
purchase_price=None,
purchase_total=None,
fail_reason="库存不足",
),
_row(fid=2, oms_id=20260928000002),
]
)
envelope = load_cart_settlement_envelope(repo)
failed, ok = envelope["data"]
assert failed["status"] == 2
assert failed["purchasePrice"] is None
assert failed["purchaseTotal"] is None
assert failed["failReason"] == "库存不足"
assert ok["purchasePrice"] == "128.5000"
assert ok["purchaseTotal"] == "1285.0000"
# push_status=1 的行不进报文。
def test_skips_already_pushed_rows():
repo, db = _open(
[
_row(fid=1, push_status=1),
_row(fid=2, oms_id=20260928000002, push_status=0),
]
)
envelope = load_cart_settlement_envelope(repo)
assert [item["fid"] for item in envelope["data"]] == [2]
sql, _params = _last_execute(db)
assert "push_status = 0" in sql.lower()
# 同一 omsId 的行必须落在同一批 data。
def test_keeps_same_oms_id_in_one_envelope():
rows = [
_row(fid=1, oms_id=1, platform_type=48, multi_platform_flag=1),
_row(fid=2, oms_id=1, platform_type=66, multi_platform_flag=1),
_row(fid=3, oms_id=2, platform_type=48),
]
envelope = build_cart_settlement_envelope(rows, limit=2)
fids = [item["fid"] for item in envelope["data"]]
assert fids == [1, 2]
# 批次已有行时,装不下的 omsId 整组留下。
def test_does_not_split_oms_id_group_across_batch_limit():
rows = [
_row(fid=1, oms_id=1, platform_type=48),
_row(fid=2, oms_id=2, platform_type=48, multi_platform_flag=1),
_row(fid=3, oms_id=2, platform_type=66, multi_platform_flag=1),
]
envelope = build_cart_settlement_envelope(rows, limit=2)
assert [item["fid"] for item in envelope["data"]] == [1]
# 默认上限 500;超过则按组截到 500。
def test_batch_cap_is_500():
assert MAX_BATCH == 500
rows = [_row(fid=i, oms_id=i) for i in range(1, 502)]
envelope = build_cart_settlement_envelope(rows)
assert len(envelope["data"]) == 500
assert envelope["data"][0]["fid"] == 1
assert envelope["data"][-1]["fid"] == 500
# 报文不含 push_status 等表内列。
def test_payload_omits_table_only_columns():
envelope = build_cart_settlement_envelope([_row()])
item = envelope["data"][0]
assert "pushStatus" not in item
assert "push_status" not in item
assert "multiPlatformFlag" not in item
assert "createTime" not in item
# 组装入口把 limit 传给仓库,让 SQL 侧截组。
def test_load_passes_limit_to_list_unpushed():
seen: dict = {}
# 只记 list_unpushed 收到的 limit。
class SpyRepo:
# 记下上限并返回空列表。
def list_unpushed(self, limit: int = 500) -> list[dict]:
seen["limit"] = limit
return []
load_cart_settlement_envelope(SpyRepo(), limit=3)
assert seen["limit"] == 3
# OMS_OUTBOUND_BATCH_LIMIT 覆盖默认 500。
def test_batch_limit_reads_env(monkeypatch):
monkeypatch.setenv("OMS_OUTBOUND_BATCH_LIMIT", "3")
assert batch_limit() == 3
# 非法或非正数上限回退 500。
def test_batch_limit_invalid_falls_back_to_500(monkeypatch):
monkeypatch.setenv("OMS_OUTBOUND_BATCH_LIMIT", "abc")
assert batch_limit() == 500
monkeypatch.setenv("OMS_OUTBOUND_BATCH_LIMIT", "0")
assert batch_limit() == 500
# 未显式传 limit 时按环境变量截组。
def test_load_uses_env_batch_limit(monkeypatch):
monkeypatch.setenv("OMS_OUTBOUND_BATCH_LIMIT", "1")
repo, db = _open(
[
_row(fid=1, oms_id=1),
_row(fid=2, oms_id=2),
]
)
envelope = load_cart_settlement_envelope(repo)
assert [item["fid"] for item in envelope["data"]] == [1]
_sql, params = _last_execute(db)
assert params["limit"] == 1