feat(t224): add incremental freight sync
This commit is contained in:
@@ -21,6 +21,20 @@ class FakeClient:
|
||||
assert order_number == "SOURCE-12"
|
||||
return raw_result()
|
||||
|
||||
def get_freight_details_by_created_range(
|
||||
self,
|
||||
created_from: str,
|
||||
created_to: str,
|
||||
) -> dict:
|
||||
assert (created_from, created_to) == ("2026-07-22", "2026-07-28")
|
||||
result = raw_result()
|
||||
result["query"] = {
|
||||
"mode": "CREATED_RANGE",
|
||||
"createdFrom": created_from,
|
||||
"createdTo": created_to,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def test_query_requires_configured_service_key(monkeypatch) -> None:
|
||||
monkeypatch.delenv("SHUNYUNBAO_SERVICE_API_KEY", raising=False)
|
||||
@@ -56,3 +70,42 @@ def test_query_returns_only_normalized_fields(monkeypatch) -> None:
|
||||
assert "receiverTel" not in encoded
|
||||
assert "receiverAddr" not in encoded
|
||||
assert "PRIVATE-QUERY" not in encoded
|
||||
|
||||
|
||||
def test_created_range_query_returns_allowlisted_range(monkeypatch) -> None:
|
||||
monkeypatch.setenv("SHUNYUNBAO_SERVICE_API_KEY", VALID_KEY)
|
||||
monkeypatch.setattr(api, "get_client", lambda: FakeClient())
|
||||
|
||||
response = api.query_freight(
|
||||
api.FreightQueryRequest(
|
||||
mode="CREATED_RANGE",
|
||||
created_from="2026-07-22",
|
||||
created_to="2026-07-28",
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
assert response["query"] == {
|
||||
"mode": "CREATED_RANGE",
|
||||
"created_from": "2026-07-22",
|
||||
"created_to": "2026-07-28",
|
||||
}
|
||||
|
||||
|
||||
def test_created_range_rejects_mixed_mode_parameters(monkeypatch) -> None:
|
||||
monkeypatch.setenv("SHUNYUNBAO_SERVICE_API_KEY", VALID_KEY)
|
||||
monkeypatch.setattr(api, "get_client", lambda: FakeClient())
|
||||
|
||||
with pytest.raises(HTTPException) as raised:
|
||||
api.query_freight(
|
||||
api.FreightQueryRequest(
|
||||
mode="CREATED_RANGE",
|
||||
order_number="SOURCE-12",
|
||||
created_from="2026-07-22",
|
||||
created_to="2026-07-28",
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
assert raised.value.status_code == 422
|
||||
assert raised.value.detail == "ERP_QUERY_INVALID"
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
from datetime import date
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import pytest
|
||||
@@ -153,6 +154,62 @@ def test_query_and_detail_reproduce_har_contract() -> None:
|
||||
assert session.calls[2]["json"] == {"ids": [99001122]}
|
||||
|
||||
|
||||
def test_created_range_query_reproduces_har_contract_and_deduplicates() -> None:
|
||||
stock = {"id": 99001122, "code": "FREIGHT-001"}
|
||||
detail = {"id": 99001122, "details": []}
|
||||
session = FakeSession(
|
||||
[
|
||||
FakeResponse(envelope(2)),
|
||||
FakeResponse(envelope({"total": 2, "list": [stock, stock]})),
|
||||
FakeResponse(envelope({"total": 1, "list": [detail]})),
|
||||
]
|
||||
)
|
||||
client = ERPClient(session=session)
|
||||
|
||||
result = client.get_freight_details_by_created_range(
|
||||
date(2026, 7, 22),
|
||||
date(2026, 7, 28),
|
||||
)
|
||||
|
||||
assert result["count"] == 1
|
||||
assert result["query"] == {
|
||||
"mode": "CREATED_RANGE",
|
||||
"createdFrom": "2026-07-22",
|
||||
"createdTo": "2026-07-28",
|
||||
}
|
||||
assert session.calls[0]["json"]["queries"] == [
|
||||
{
|
||||
"dvalue": "2026-07-22,2026-07-28",
|
||||
"tableName": "t_stock",
|
||||
"colName": "created",
|
||||
"op": 0,
|
||||
"type": 3,
|
||||
"tableAlias": "t",
|
||||
"optType": 0,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_created_range_rejects_more_than_seven_inclusive_days() -> None:
|
||||
client = ERPClient(session=FakeSession([]))
|
||||
|
||||
with pytest.raises(ValueError, match="7 天"):
|
||||
client.query_stock_by_created_range("2026-07-21", "2026-07-28")
|
||||
|
||||
|
||||
def test_empty_created_range_does_not_request_details() -> None:
|
||||
session = FakeSession([FakeResponse(envelope(0))])
|
||||
client = ERPClient(session=session)
|
||||
|
||||
result = client.get_freight_details_by_created_range(
|
||||
"2026-07-28",
|
||||
"2026-07-28",
|
||||
)
|
||||
|
||||
assert result["records"] == []
|
||||
assert len(session.calls) == 1
|
||||
|
||||
|
||||
def test_not_found_stops_before_detail_request() -> None:
|
||||
session = FakeSession([FakeResponse(envelope(0))])
|
||||
client = ERPClient(session=session)
|
||||
|
||||
@@ -110,6 +110,25 @@ def test_invalid_procurement_fields_remain_reviewable() -> None:
|
||||
assert result["product_thumb_ref"] is None
|
||||
|
||||
|
||||
def test_normalizes_created_range_without_exposing_raw_query() -> None:
|
||||
source = raw_result()
|
||||
source["query"] = {
|
||||
"mode": "CREATED_RANGE",
|
||||
"createdFrom": "2026-07-22",
|
||||
"createdTo": "2026-07-28",
|
||||
"private": "must-not-leak",
|
||||
}
|
||||
|
||||
normalized = normalize_freight_result(source)
|
||||
|
||||
assert normalized["query"] == {
|
||||
"mode": "CREATED_RANGE",
|
||||
"created_from": "2026-07-22",
|
||||
"created_to": "2026-07-28",
|
||||
}
|
||||
assert "must-not-leak" not in json.dumps(normalized)
|
||||
|
||||
|
||||
def test_conflicting_duplicate_item_is_rejected() -> None:
|
||||
source = raw_result()
|
||||
duplicate = dict(source["records"][0]["detail"]["details"][0])
|
||||
|
||||
Reference in New Issue
Block a user