191 lines
5.6 KiB
Python
191 lines
5.6 KiB
Python
"""接口 1 收件契约。内存仓库,不连 Oracle。"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
|
||
import pytest
|
||
from fastapi.testclient import TestClient
|
||
|
||
os.environ["OMS_INBOUND_TOKEN"] = "test-oms-token"
|
||
|
||
from app.inbound.memory_repo import MemorySalesGoodsRepo
|
||
from app.inbound.repo import RepoUnavailable
|
||
from app.main import app, get_repo
|
||
|
||
TOKEN = "test-oms-token"
|
||
PATH = "/api/oms/v1/sales-goods"
|
||
|
||
|
||
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": "901",
|
||
"goodsMdmCode": "QX0100167583",
|
||
"customerMdmCode": "DW0100079727",
|
||
}
|
||
row.update(overrides)
|
||
return row
|
||
|
||
|
||
@pytest.fixture
|
||
def client():
|
||
repo = MemorySalesGoodsRepo()
|
||
app.dependency_overrides[get_repo] = lambda: repo
|
||
with TestClient(app) as test_client:
|
||
yield test_client, repo
|
||
app.dependency_overrides.clear()
|
||
|
||
|
||
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)
|
||
|
||
|
||
def test_missing_token_returns_401(client):
|
||
test_client, _ = client
|
||
resp = _post(test_client, {"data": [_item()]}, token=None)
|
||
assert resp.status_code == 401
|
||
body = resp.json()
|
||
assert body["code"] == 401
|
||
assert body["results"] == []
|
||
|
||
|
||
def test_wrong_token_returns_401(client):
|
||
test_client, _ = client
|
||
resp = _post(test_client, {"data": [_item()]}, token="bad")
|
||
assert resp.status_code == 401
|
||
assert resp.json()["code"] == 401
|
||
|
||
|
||
def test_missing_data_returns_422(client):
|
||
test_client, _ = client
|
||
resp = _post(test_client, {})
|
||
assert resp.status_code == 422
|
||
assert resp.json()["code"] == 422
|
||
assert resp.json()["results"] == []
|
||
|
||
|
||
def test_empty_data_returns_422(client):
|
||
test_client, _ = client
|
||
resp = _post(test_client, {"data": []})
|
||
assert resp.status_code == 422
|
||
assert resp.json()["code"] == 422
|
||
|
||
|
||
def test_over_100_rows_returns_422(client):
|
||
test_client, repo = client
|
||
rows = [_item(omsId=i) for i in range(1, 102)]
|
||
resp = _post(test_client, {"data": rows})
|
||
assert resp.status_code == 422
|
||
assert repo.count() == 0
|
||
|
||
|
||
def test_accepts_valid_row(client):
|
||
test_client, repo = 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 = repo.get(2026092801)
|
||
assert stored["deal_flag"] == 0
|
||
assert stored["bar_code"] == "06901234567890"
|
||
assert stored["goods_mdm_code"] == "QX0100167583"
|
||
|
||
|
||
def test_existing_oms_id_is_duplicate_and_not_updated(client):
|
||
# 跨请求同 omsId:DUPLICATE,库里品名不改。
|
||
test_client, repo = 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 repo.get(2026092801)["goods_name"] == "阿莫西林胶囊"
|
||
|
||
|
||
def test_duplicate_oms_id_in_same_batch_is_conflict(client):
|
||
# 同一 POST 里两条相同 omsId:后一条 CONFLICT,只落第一条。
|
||
test_client, repo = 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 repo.count() == 1
|
||
|
||
|
||
def test_missing_mdm_is_rejected_and_not_stored(client):
|
||
test_client, repo = 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 repo.count() == 0
|
||
|
||
|
||
def test_mixed_batch_still_http_200(client):
|
||
test_client, repo = 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 repo.count() == 2
|
||
|
||
|
||
def test_string_oms_id_is_rejected(client):
|
||
# JSON 字符串 "2026092801" 不转整数。
|
||
test_client, repo = client
|
||
resp = _post(test_client, {"data": [_item(omsId="2026092801")]})
|
||
assert resp.status_code == 200
|
||
assert resp.json()["results"][0]["resultCode"] == "REJECTED"
|
||
assert repo.count() == 0
|
||
|
||
|
||
def test_repo_unavailable_returns_503(client):
|
||
test_client, _ = client
|
||
|
||
class Boom:
|
||
def insert(self, record):
|
||
raise RepoUnavailable("数据库不可用")
|
||
|
||
app.dependency_overrides[get_repo] = lambda: Boom()
|
||
resp = _post(test_client, {"data": [_item()]})
|
||
assert resp.status_code == 503
|
||
assert resp.json()["code"] == 503
|
||
assert resp.json()["results"] == []
|