feat(t213): complete live SKU price verification
This commit is contained in:
+12
@@ -202,6 +202,18 @@ class PinduoduoPageClassifierTest {
|
||||
assertNull(snapshot.safetyStopReason)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `modern detail footer accepts short customer service label`() {
|
||||
val snapshot = classify(
|
||||
element(contentDescription = "返回", clickable = true),
|
||||
element(contentDescription = "客服", clickable = true),
|
||||
element(contentDescription = "收藏", clickable = true)
|
||||
)
|
||||
|
||||
assertEquals(PinduoduoPage.PRODUCT_DETAIL, snapshot.page)
|
||||
assertNull(snapshot.safetyStopReason)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `specification panel requires color size and options`() {
|
||||
val snapshot = classify(
|
||||
|
||||
+25
@@ -106,6 +106,31 @@ class PinduoduoSpecificationParserTest {
|
||||
assertFalse(evidence.priceAmbiguous)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reads allowlisted first unit price and ignores comparison price`() {
|
||||
val evidence = PinduoduoSpecificationParser.parse(
|
||||
listOf(
|
||||
element("首件¥16.7", top = 20),
|
||||
element("大促价¥20.7", top = 30),
|
||||
element("已选择:XL码/裙长90胸围89 红色", top = 40),
|
||||
element("颜色分类", top = 100),
|
||||
element("红色", top = 140, clickable = true, selected = true),
|
||||
element("尺码", top = 240),
|
||||
element(
|
||||
"XL码/裙长90胸围89",
|
||||
top = 280,
|
||||
clickable = true,
|
||||
selected = true
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
requireNotNull(evidence)
|
||||
assertEquals(1670L, evidence.price?.cents)
|
||||
assertEquals("首件¥16.7", evidence.price?.rawText)
|
||||
assertFalse(evidence.priceAmbiguous)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple explicit prices are ambiguous`() {
|
||||
val evidence = PinduoduoSpecificationParser.parse(
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.roubao.autopilot.procurement
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ClaimReconciliationPolicyTest {
|
||||
@Test
|
||||
fun `restores only when local and server task identities match`() {
|
||||
assertEquals(
|
||||
ClaimReconciliationDecision.RESTORE,
|
||||
reconcileClaim("task-1", "task-1")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `discards local task when backend has ended or replaced it`() {
|
||||
assertEquals(
|
||||
ClaimReconciliationDecision.DISCARD_LOCAL,
|
||||
reconcileClaim("task-1", null)
|
||||
)
|
||||
assertEquals(
|
||||
ClaimReconciliationDecision.DISCARD_LOCAL,
|
||||
reconcileClaim("task-1", "task-2")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `continues claim or replay when no local task is materialized`() {
|
||||
assertEquals(
|
||||
ClaimReconciliationDecision.CLAIM_OR_REPLAY,
|
||||
reconcileClaim(null, null)
|
||||
)
|
||||
assertEquals(
|
||||
ClaimReconciliationDecision.CLAIM_OR_REPLAY,
|
||||
reconcileClaim(null, "task-1")
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user