feat: 安全应用采购规格解析结果 (#257)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""AdminGateway 边界和 Mock 契约测试。"""
|
||||
|
||||
import hashlib
|
||||
import unittest
|
||||
|
||||
from src.admin_gateway import (
|
||||
@@ -9,6 +10,8 @@ from src.admin_gateway import (
|
||||
AndroidDeviceInfo,
|
||||
ClaimCapabilities,
|
||||
ClientInfo,
|
||||
SpecResolutionMatch,
|
||||
SpecResolutionReceipt,
|
||||
)
|
||||
from src.mock_admin_gateway import MockAdminGateway
|
||||
from src.task_models import TaskType
|
||||
@@ -61,7 +64,7 @@ class MockAdminGatewayContractTest(unittest.TestCase):
|
||||
"reported_at": "2026-08-06T08:03:00Z",
|
||||
}
|
||||
|
||||
def test_gateway_has_only_four_business_methods(self):
|
||||
def test_gateway_has_only_expected_business_methods(self):
|
||||
self.assertEqual(
|
||||
AdminGateway.__abstractmethods__,
|
||||
{
|
||||
@@ -69,6 +72,7 @@ class MockAdminGatewayContractTest(unittest.TestCase):
|
||||
"claim_next",
|
||||
"submit_result",
|
||||
"submit_failure",
|
||||
"resolve_purchase_spec",
|
||||
},
|
||||
)
|
||||
for forbidden in ("get_status", "heartbeat", "renew_lease"):
|
||||
@@ -162,6 +166,92 @@ class MockAdminGatewayContractTest(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(unavailable_context.exception.retryable)
|
||||
|
||||
def test_spec_resolution_is_idempotent_and_returns_configured_match(self):
|
||||
task = AdminTask(
|
||||
task_id="PUR-SPEC",
|
||||
task_type=TaskType.PURCHASE,
|
||||
version=3,
|
||||
priority=1,
|
||||
payload={
|
||||
"goods_id": "737116531267",
|
||||
"options": {"color": "黑色", "size": "60公斤"},
|
||||
},
|
||||
)
|
||||
self.gateway.enqueue_task(task, self.client.client_id)
|
||||
self.gateway.claim_next(self.client, self.all_capabilities)
|
||||
frame = lambda value: f"{len(value.encode('utf-8'))}:{value}"
|
||||
snapshot_material = "".join(
|
||||
frame(value)
|
||||
for value in (
|
||||
"spec-resolution-v1",
|
||||
"737116531267",
|
||||
"黑色",
|
||||
"1",
|
||||
"c1",
|
||||
"120斤",
|
||||
"黑色",
|
||||
"120斤",
|
||||
)
|
||||
)
|
||||
snapshot_hash = hashlib.sha256(
|
||||
snapshot_material.encode("utf-8")
|
||||
).hexdigest()
|
||||
observation = {
|
||||
"schema_version": 1,
|
||||
"task_version": 3,
|
||||
"attempt_id": "attempt-001",
|
||||
"pdd_goods_id": "737116531267",
|
||||
"original_options": {"color": "黑色", "size": "60公斤"},
|
||||
"selected_color": "黑色",
|
||||
"target_size": "60公斤",
|
||||
"candidates": [
|
||||
{
|
||||
"candidate_id": "c1",
|
||||
"raw_text": "120斤",
|
||||
"options": {"color": "黑色", "size": "120斤"},
|
||||
}
|
||||
],
|
||||
"candidate_snapshot_hash": snapshot_hash,
|
||||
"observed_at": "2026-08-17T08:00:00Z",
|
||||
}
|
||||
material = "".join(
|
||||
frame(value)
|
||||
for value in (
|
||||
"PUR-SPEC",
|
||||
"attempt-001",
|
||||
snapshot_hash,
|
||||
"spec-resolution-v1",
|
||||
)
|
||||
)
|
||||
key = "spec-resolution-v1:" + hashlib.sha256(
|
||||
material.encode("utf-8")
|
||||
).hexdigest()
|
||||
configured = SpecResolutionReceipt(
|
||||
1,
|
||||
"psr-001",
|
||||
"matched",
|
||||
"rule",
|
||||
snapshot_hash,
|
||||
SpecResolutionMatch(
|
||||
"c1", "120斤", {"color": "黑色", "size": "120斤"}
|
||||
),
|
||||
10000,
|
||||
"唯一重量等价",
|
||||
"2026-08-17T08:00:01Z",
|
||||
)
|
||||
self.gateway.set_next_spec_resolution(configured)
|
||||
|
||||
first = self.gateway.resolve_purchase_spec(
|
||||
"PUR-SPEC", key, observation
|
||||
)
|
||||
second = self.gateway.resolve_purchase_spec(
|
||||
"PUR-SPEC", key, observation
|
||||
)
|
||||
|
||||
self.assertEqual(first, configured)
|
||||
self.assertEqual(second, configured)
|
||||
self.assertEqual(self.gateway.spec_resolution_count, 1)
|
||||
|
||||
def test_same_idempotency_key_and_content_reuses_receipt(self):
|
||||
self.gateway.enqueue_task(
|
||||
self._task("TASK-001", TaskType.COLLECT), "client-001"
|
||||
|
||||
Reference in New Issue
Block a user