219 lines
6.7 KiB
Python
219 lines
6.7 KiB
Python
"""接口 3 收件契约。内存仓库,不连 Oracle。按对方 fid 幂等。"""
|
|
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 MemoryPurchaseContractRepo
|
|
from app.inbound.repo import RepoUnavailable
|
|
from app.main import app, get_contract_repo
|
|
|
|
TOKEN = "test-oms-token"
|
|
PATH = "/api/oms/v1/purchase-contracts"
|
|
|
|
|
|
def _item(**overrides):
|
|
row = {
|
|
"fid": 200001,
|
|
"omsId": 20260928000001,
|
|
"supplierId": "SUP001",
|
|
"supplierName": "示例供应商",
|
|
"contractSignDate": "2026-09-28",
|
|
"contractStartDate": "2026-10-01",
|
|
"contractEndDate": "2027-09-30",
|
|
"paymentTerm": 90,
|
|
"goodsId": "G000123456789",
|
|
"goodsName": "阿莫西林胶囊",
|
|
"regNo": "国药准字H20201234",
|
|
"spec": "0.25g*24粒",
|
|
"unit": "盒",
|
|
"qty": "10.0000",
|
|
"purchaseTaxRate": "0.13",
|
|
"unitPrice": "100.0000",
|
|
"goodsSourceId": "GS001",
|
|
"purchaserId": "P001",
|
|
"purchaserName": "张三",
|
|
"plannerId": "PL001",
|
|
"plannerName": "李四",
|
|
"contractEntryStatus": None,
|
|
"contractNo": None,
|
|
"orgCode": "901",
|
|
"customerType": 1,
|
|
"goodsMdmCode": "QX0100167583",
|
|
"supplierMdmCode": "DW0100046361",
|
|
}
|
|
row.update(overrides)
|
|
return row
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
repo = MemoryPurchaseContractRepo()
|
|
app.dependency_overrides[get_contract_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
|
|
assert resp.json()["results"] == []
|
|
|
|
|
|
def test_missing_data_returns_422(client):
|
|
test_client, repo = client
|
|
resp = _post(test_client, {})
|
|
assert resp.status_code == 422
|
|
assert repo.count() == 0
|
|
|
|
|
|
def test_over_100_rows_returns_422(client):
|
|
test_client, repo = client
|
|
rows = [_item(fid=i, 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"] == [
|
|
{
|
|
"fid": 200001,
|
|
"resultCode": "ACCEPTED",
|
|
"message": "采购合同数据已接收,等待处理",
|
|
}
|
|
]
|
|
stored = repo.get(200001)
|
|
assert stored["process_status"] == 0
|
|
assert stored["oms_id"] == 20260928000001
|
|
assert stored["supplier_id"] == "SUP001"
|
|
assert stored["goods_mdm_code"] == "QX0100167583"
|
|
assert stored["contract_entry_status"] is None
|
|
assert stored["contract_no"] is None
|
|
|
|
|
|
def test_existing_fid_is_duplicate_and_not_updated(client):
|
|
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 result["message"] == "序号已存在,未重复入库"
|
|
assert repo.get(200001)["goods_name"] == "阿莫西林胶囊"
|
|
|
|
|
|
def test_duplicate_fid_in_same_batch_is_conflict(client):
|
|
test_client, repo = client
|
|
resp = _post(
|
|
test_client,
|
|
{"data": [_item(), _item(omsId=20260928000002)]},
|
|
)
|
|
assert resp.status_code == 200
|
|
results = resp.json()["results"]
|
|
assert results[0]["resultCode"] == "ACCEPTED"
|
|
assert results[1]["resultCode"] == "CONFLICT"
|
|
assert results[1]["fid"] == 200001
|
|
assert repo.count() == 1
|
|
|
|
|
|
def test_retry_protocol_without_contract_no_is_rejected(client):
|
|
test_client, repo = client
|
|
resp = _post(test_client, {"data": [_item(contractEntryStatus=0, contractNo=None)]})
|
|
assert resp.status_code == 200
|
|
result = resp.json()["results"][0]
|
|
assert result["resultCode"] == "REJECTED"
|
|
assert "contractNo" in result["message"]
|
|
assert repo.count() == 0
|
|
|
|
|
|
def test_retry_protocol_with_contract_no_is_accepted(client):
|
|
test_client, repo = client
|
|
resp = _post(
|
|
test_client,
|
|
{"data": [_item(fid=200002, contractEntryStatus=0, contractNo="HT20260928002")]},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["results"][0]["resultCode"] == "ACCEPTED"
|
|
stored = repo.get(200002)
|
|
assert stored["contract_entry_status"] == 0
|
|
assert stored["contract_no"] == "HT20260928002"
|
|
|
|
|
|
def test_end_date_before_start_is_rejected(client):
|
|
test_client, repo = client
|
|
resp = _post(
|
|
test_client,
|
|
{"data": [_item(contractStartDate="2027-09-30", contractEndDate="2026-10-01")]},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["results"][0]["resultCode"] == "REJECTED"
|
|
assert repo.count() == 0
|
|
|
|
|
|
def test_string_oms_id_is_rejected(client):
|
|
test_client, repo = client
|
|
resp = _post(test_client, {"data": [_item(omsId="20260928000001")]})
|
|
assert resp.status_code == 200
|
|
assert resp.json()["results"][0]["resultCode"] == "REJECTED"
|
|
assert repo.count() == 0
|
|
|
|
|
|
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(fid=1, omsId=1)]})
|
|
resp = _post(
|
|
test_client,
|
|
{
|
|
"data": [
|
|
_item(fid=1, omsId=1),
|
|
_item(fid=2, omsId=2),
|
|
_item(fid=3, 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_repo_unavailable_returns_503(client):
|
|
test_client, _ = client
|
|
|
|
class Boom:
|
|
def insert(self, record):
|
|
raise RepoUnavailable("数据库不可用")
|
|
|
|
app.dependency_overrides[get_contract_repo] = lambda: Boom()
|
|
resp = _post(test_client, {"data": [_item()]})
|
|
assert resp.status_code == 503
|
|
assert resp.json()["code"] == 503
|
|
assert resp.json()["results"] == []
|