AutoRpa/rpa_oms/tests/test_sales_goods_receive.py

250 lines
8.0 KiB
Python
Raw 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.

# 接口 1 收件契约。假连接录 SQL,不连 Oracle。
from __future__ import annotations
import os
import pytest
from fastapi.testclient import TestClient
os.environ["OMS_INBOUND_TOKEN"] = "test-oms-token"
from app.main import app, get_pool
from tests.fake_oracle import FakePool, ora_error
TOKEN = "test-oms-token"
PATH = "/api/oms/v1/sales-goods"
# 构造一条合法的接口 1 明细,overrides 覆盖字段。
def _item(**overrides):
row = {
"omsId": 2026092801,
"barCode": "06901234567890",
"orderDate": "2026-09-28",
"salePrice": "128.5000",
"saleQty": 10,
"planPurchaseQty": 10,
"goodsId": "G000123456789",
"goodsName": "阿莫西林胶囊",
"goodsSpec": "0.25g*24粒",
"manufacturer": "示例制药有限公司",
"approvalNo": "国药准字H20201234",
"unit": "盒",
"customerId": "C00010001",
"customerName": "示例医院",
"orgCode": "907",
"goodsMdmCode": "QX0100167583",
"customerMdmCode": "DW0100079727",
}
row.update(overrides)
return row
# 注入 FakePool,整次请求走同一块假库。
@pytest.fixture
def client():
pool = FakePool()
app.dependency_overrides[get_pool] = lambda: pool
with TestClient(app) as test_client:
yield test_client, pool
app.dependency_overrides.clear()
# 带或不带 X-Oms-Token 发 POST。
def _post(client, body, token=TOKEN):
headers = {"X-Oms-Token": token} if token is not None else {}
return client.post(PATH, json=body, headers=headers)
# 缺 X-Oms-Token 时接口返回 401。
def test_missing_token_returns_401(client):
test_client, pool = client
resp = _post(test_client, {"data": [_item()]}, token=None)
assert resp.status_code == 401
body = resp.json()
assert body["code"] == 401
assert body["msg"] == "身份认证失败"
assert body["results"] == []
assert pool.db.audits
# 错误 token 时接口返回 401。
def test_wrong_token_returns_401(client):
test_client, _pool = client
resp = _post(test_client, {"data": [_item()]}, token="bad")
assert resp.status_code == 401
assert resp.json()["code"] == 401
# 缺 data 节点时整包 422,一条都不落。
def test_missing_data_returns_422(client):
test_client, pool = client
resp = _post(test_client, {})
assert resp.status_code == 422
assert resp.json()["code"] == 422
assert resp.json()["results"] == []
assert pool.db.sales == {}
# data 空数组时整包 422。
def test_empty_data_returns_422(client):
test_client, _pool = client
resp = _post(test_client, {"data": []})
assert resp.status_code == 422
assert resp.json()["code"] == 422
# 超过 100 条时整包 422,假库仍空。
def test_over_100_rows_returns_422(client):
test_client, pool = client
rows = [_item(omsId=i) for i in range(1, 102)]
resp = _post(test_client, {"data": rows})
assert resp.status_code == 422
assert pool.db.sales == {}
# 合法一行 ACCEPTED,deal_flag=0,条码和 MDM 按 snake_case 落库。
def test_accepts_valid_row(client):
test_client, pool = client
resp = _post(test_client, {"data": [_item()]})
assert resp.status_code == 200
body = resp.json()
assert body["code"] == 200
assert body["results"] == [
{
"omsId": 2026092801,
"resultCode": "ACCEPTED",
"message": "接收成功,等待RPA处理",
}
]
stored = pool.db.sales[2026092801]
assert stored["deal_flag"] == 0
assert stored["bar_code"] == "06901234567890"
assert stored["goods_mdm_code"] == "QX0100167583"
# 跨请求同 omsId:DUPLICATE,库里品名不改。
def test_existing_oms_id_is_duplicate_and_not_updated(client):
test_client, pool = client
_post(test_client, {"data": [_item()]})
resp = _post(test_client, {"data": [_item(goodsName="不应覆盖")]})
assert resp.status_code == 200
result = resp.json()["results"][0]
assert result["resultCode"] == "DUPLICATE"
assert pool.db.sales[2026092801]["goods_name"] == "阿莫西林胶囊"
# 同一 POST 里两条相同 omsId:后一条 CONFLICT,只落第一条。
def test_duplicate_oms_id_in_same_batch_is_conflict(client):
test_client, pool = client
resp = _post(
test_client,
{"data": [_item(), _item(barCode="06901234567891")]},
)
assert resp.status_code == 200
results = resp.json()["results"]
assert results[0]["resultCode"] == "ACCEPTED"
assert results[1]["resultCode"] == "CONFLICT"
assert results[1]["omsId"] == 2026092801
assert len(pool.db.sales) == 1
# 缺 MDM 码 REJECTED,不入库。
def test_missing_mdm_is_rejected_and_not_stored(client):
test_client, pool = client
resp = _post(test_client, {"data": [_item(goodsMdmCode="")]})
assert resp.status_code == 200
result = resp.json()["results"][0]
assert result["resultCode"] == "REJECTED"
assert "goodsMdmCode" in result["message"]
assert pool.db.sales == {}
# 混批 DUPLICATE + ACCEPTED + REJECTED 时 HTTP 仍 200。
def test_mixed_batch_still_http_200(client):
test_client, pool = client
_post(test_client, {"data": [_item(omsId=1)]})
resp = _post(
test_client,
{
"data": [
_item(omsId=1),
_item(omsId=2),
_item(omsId=3, orgCode=""),
]
},
)
assert resp.status_code == 200
codes = [r["resultCode"] for r in resp.json()["results"]]
assert codes == ["DUPLICATE", "ACCEPTED", "REJECTED"]
assert set(pool.db.sales) == {1, 2}
# JSON 字符串 omsId 不转整数,标 REJECTED。
def test_string_oms_id_is_rejected(client):
test_client, pool = client
resp = _post(test_client, {"data": [_item(omsId="2026092801")]})
assert resp.status_code == 200
assert resp.json()["results"][0]["resultCode"] == "REJECTED"
assert pool.db.sales == {}
# 库故障时 HTTP 503,body 固定「数据库不可用」,不含 ORA 和表名。
def test_repo_unavailable_returns_503_without_ora(client):
test_client, pool = client
pool.db.raise_execute = ora_error(
12170,
"ORA-12170: TNS:Connect timeout occurred at rpa_sales_goods_mapping_data",
)
resp = _post(test_client, {"data": [_item()]})
assert resp.status_code == 503
body = resp.json()
assert body["code"] == 503
assert body["msg"] == "数据库不可用"
assert body["results"] == []
text = resp.text
assert "ORA-" not in text
assert "rpa_sales_goods_mapping_data" not in text
# 一批两条合法明细只从池里借一次连接。
def test_batch_two_rows_acquires_once(client):
test_client, pool = client
resp = _post(
test_client,
{"data": [_item(omsId=1), _item(omsId=2)]},
)
assert resp.status_code == 200
assert [r["resultCode"] for r in resp.json()["results"]] == ["ACCEPTED", "ACCEPTED"]
assert pool.acquires == 1
many = [c for c in pool.db.calls if c[0] == "executemany"]
assert len(many) == 1
# 并发撞上未知唯一约束时不回 DUPLICATE,整包 503。
def test_unknown_unique_constraint_is_not_duplicate(client):
test_client, pool = client
pool.db.collide_sales.add(2026092801)
pool.db.collide_constraint = "UK_SOMETHING_ELSE"
resp = _post(test_client, {"data": [_item()]})
assert resp.status_code == 503
assert resp.json()["msg"] == "数据库不可用"
assert "DUPLICATE" not in resp.text
# 成功收件写审计一行,入参出参 JSON 里没有 token 明文。
def test_success_audit_masks_token(client):
test_client, pool = client
resp = _post(test_client, {"data": [_item()]})
assert resp.status_code == 200
assert len(pool.db.audits) == 1
audit = pool.db.audits[0]
blob = " ".join(str(v) for v in audit.values())
assert TOKEN not in blob
assert audit["token_masked"] == "***"
assert audit["api_no"] == 1
assert audit["http_code"] == 200
audit_sql = [c for c in pool.db.calls if c[0] == "execute" and "RPA_OMS_INBOUND_AUDIT" in " ".join(c[1].split()).upper()]
assert audit_sql