# 接口 2 Oracle 取数:SQL 按 oms_id 整组截断;一次 UPDATE 回写。不连真库。 from __future__ import annotations import pytest from app.inbound.repo import RepoUnavailable from app.outbound.oracle_repo import _LIST_UNPUSHED_SQL, OracleCartSettlementRepo from tests.fake_oracle import FakeDb, ora_error # 构造一条表 2 未推行。 def _cart(**overrides) -> dict: row = { "fid": 1, "operate_type": 1, "oms_id": 1, "platform_type": 48, "supplier_name": "示例供应商", "bar_code": "06901234567890", "purchase_price": 128.5, "purchase_qty": 10, "purchase_total": 1285, "status": 1, "fail_reason": None, "pay_order_no": None, "pay_order_dtl_id": None, "org_code": "901", "push_status": 0, } row.update(overrides) return row # 取数 SQL 只捞 push_status=0,按 oms_id 分组,并用 :limit 在库侧截断。 def test_list_sql_filters_unpushed_and_partitions_by_oms_id(): sql = " ".join(_LIST_UNPUSHED_SQL.split()).lower() assert "rpa_cart_settlement_goods_data" in sql assert "push_status = 0" in sql assert "partition by" in sql assert "oms_id" in sql assert ":limit" in sql assert "running_start" in sql assert "order by" in sql and "oms_id" in sql # 假连接返回的列名转小写后能对上组装层字段。 def test_list_unpushed_maps_columns(): db = FakeDb() db.carts[100001] = _cart( fid=100001, oms_id=20260928000001, bar_code="06901234567890", ) repo = OracleCartSettlementRepo(db.connection()) rows = repo.list_unpushed(limit=500) assert rows[0]["fid"] == 100001 assert rows[0]["operate_type"] == 1 assert rows[0]["bar_code"] == "06901234567890" assert rows[0]["push_status"] == 0 assert db.closes == 0 # limit=3 时同一 omsId 三行整组进批,多出的另一组留下。 def test_list_unpushed_keeps_whole_oms_group_when_limit_is_3(): db = FakeDb() db.carts[1] = _cart(fid=1, oms_id=1) db.carts[2] = _cart(fid=2, oms_id=1) db.carts[3] = _cart(fid=3, oms_id=1) db.carts[4] = _cart(fid=4, oms_id=2) repo = OracleCartSettlementRepo(db.connection()) rows = repo.list_unpushed(limit=3) assert [row["fid"] for row in rows] == [1, 2, 3] sql, params = _last_execute(db) assert "partition by" in sql.lower() assert params["limit"] == 3 # 库故障对外固定「数据库不可用」,不含 ORA 原文。 def test_list_unpushed_hides_ora_text(): db = FakeDb() db.raise_execute = ora_error(12541, "ORA-12541: TNS:no listener") repo = OracleCartSettlementRepo(db.connection()) with pytest.raises(RepoUnavailable) as caught: repo.list_unpushed(limit=500) assert caught.value.message == "数据库不可用" assert "ORA-" not in str(caught.value) # 回写 push_status 只发一条 UPDATE ... WHERE fid IN (...)。 def test_mark_pushed_updates_in_one_statement(): db = FakeDb() db.carts[1] = _cart(fid=1) db.carts[2] = _cart(fid=2) repo = OracleCartSettlementRepo(db.connection()) repo.mark_pushed([1, 2], status=1, reason="ok") updates = [ call for call in db.calls if call[0] == "execute" and "UPDATE" in call[1].upper() ] assert len(updates) == 1 sql = updates[0][1].upper() assert "RPA_CART_SETTLEMENT_GOODS_DATA" in sql assert "FID IN" in sql assert db.carts[1]["push_status"] == 1 assert db.carts[2]["push_status"] == 1 assert db.carts[1]["push_result"] == "ok" assert db.commits == 1 # 空 fid 列表不发 SQL、不 commit。 def test_mark_pushed_empty_fids_does_not_touch_db(): db = FakeDb() repo = OracleCartSettlementRepo(db.connection()) repo.mark_pushed([], status=1, reason=None) assert db.calls == [] assert db.commits == 0 # 回写失败对外固定「数据库不可用」。 def test_mark_pushed_hides_ora_text(): db = FakeDb() db.carts[1] = _cart(fid=1) db.raise_execute = ora_error(3113, "ORA-03113: end-of-file on communication channel") repo = OracleCartSettlementRepo(db.connection()) with pytest.raises(RepoUnavailable) as caught: repo.mark_pushed([1], status=2, reason="fail") assert caught.value.message == "数据库不可用" assert "ORA-" not in str(caught.value) # 取出最近一次 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 {}