Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db9076d6f6 | ||
|
|
5f329d2311 |
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "cn.ilapage.goauto.agent"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 86
|
||||
versionName = "0.9.73"
|
||||
versionCode = 85
|
||||
versionName = "0.9.72"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
||||
+1
-3
@@ -797,7 +797,7 @@ class PurchaseLiveAutomation(
|
||||
val screenHeight = snapshot.nodes.maxOfOrNull { it.bounds.bottom }?.coerceAtLeast(1) ?: 1
|
||||
return snapshot.nodes.filter { node ->
|
||||
node.visible && node.enabled && node.scrollable &&
|
||||
node.bounds.height * 100 >= node.bounds.width * PANEL_SCROLL_MIN_HEIGHT_TO_WIDTH_PERCENT &&
|
||||
node.bounds.height >= node.bounds.width &&
|
||||
node.bounds.width * 100 >= screenWidth * 60 &&
|
||||
node.bounds.height * 100 >= screenHeight * 20
|
||||
}.distinctBy { it.path }
|
||||
@@ -1153,8 +1153,6 @@ class PurchaseLiveAutomation(
|
||||
const val SPEC_CONFIRMATION_MAX_SAMPLES = 20
|
||||
const val SPEC_CONFIRMATION_SAMPLE_INTERVAL_MS = 100L
|
||||
const val ADDRESS_ENTRY_STABLE_INTERVAL_MS = 200L
|
||||
// #390: near-square purchase lists can be shorter than their width; exclude flat strips.
|
||||
const val PANEL_SCROLL_MIN_HEIGHT_TO_WIDTH_PERCENT = 80
|
||||
const val ADDRESS_CARD_OVERLAP_PERCENT = 70
|
||||
const val ADDRESS_SEMANTIC_MIN_LENGTH = 4
|
||||
val ADDRESS_CARD_GENERIC_LABELS = setOf("收货地址", "修改", "默认地址", "默认")
|
||||
|
||||
@@ -1302,113 +1302,6 @@ class PurchaseLiveAutomationTest {
|
||||
assertEquals(0, driver.genericSwipes)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `near square purchase panel reveals collapsed address with one bounded swipe`() {
|
||||
assertClippedPanelAddressRevealed(NodeBounds(0, 400, 1080, 1478))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `purchase panel accepts exactly eighty percent height to width`() {
|
||||
assertClippedPanelAddressRevealed(NodeBounds(0, 400, 1080, 1264))
|
||||
}
|
||||
|
||||
private fun assertClippedPanelAddressRevealed(bounds: NodeBounds) {
|
||||
val driver = LiveDriver(addressClipped = true, clippedPanelBounds = bounds)
|
||||
val initial = driver.capture()
|
||||
val collapsedAddress = initial.nodes.single { it.path == "panel/collapsed-address" }
|
||||
assertEquals(0, collapsedAddress.bounds.height)
|
||||
assertFalse(collapsedAddress.visible)
|
||||
assertEquals(2376, initial.nodes.maxOf { it.bounds.bottom })
|
||||
|
||||
val proof = PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress("_cg390")
|
||||
|
||||
assertTrue(proof.expectedAddress.endsWith("_cg390"))
|
||||
assertEquals(listOf(Triple(bounds, SwipeDirection.DOWN, 350L)), driver.scopedSwipeRequests)
|
||||
assertEquals(1, driver.addressPathClicks)
|
||||
assertEquals(1, driver.inputCount)
|
||||
assertEquals(0, driver.genericSwipes + driver.submitClicks)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `purchase panel rejects height just below eighty percent and flat horizontal strip`() {
|
||||
for (height in listOf(863, 400)) {
|
||||
val driver = LiveDriver(addressClipped = true, clippedPanelBounds = NodeBounds(0, 400, 1080, 400 + height))
|
||||
|
||||
val error = runCatching { PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress("_cg390") }
|
||||
.exceptionOrNull() as PurchaseLiveException
|
||||
|
||||
assertEquals("PURCHASE_ADDRESS_ENTRY_AMBIGUOUS", error.code)
|
||||
assertEquals(0, driver.scopedSwipes + driver.genericSwipes + driver.inputCount + driver.submitClicks)
|
||||
assertTrue(driver.clicked.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `purchase panel still requires sixty percent screen width and twenty percent screen height`() {
|
||||
for (bounds in listOf(NodeBounds(0, 400, 647, 1478), NodeBounds(0, 400, 648, 999))) {
|
||||
val driver = LiveDriver(addressClipped = true, clippedPanelBounds = bounds, clippedScreenHeight = 3000)
|
||||
|
||||
val error = runCatching { PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress("_cg390") }
|
||||
.exceptionOrNull() as PurchaseLiveException
|
||||
|
||||
assertEquals("PURCHASE_ADDRESS_ENTRY_AMBIGUOUS", error.code)
|
||||
assertEquals(0, driver.scopedSwipes + driver.genericSwipes + driver.inputCount + driver.submitClicks)
|
||||
assertTrue(driver.clicked.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `two near square purchase panels remain ambiguous without swiping`() {
|
||||
val driver = LiveDriver(addressClipped = true, duplicatePanels = true, clippedPanelBounds = NodeBounds(0, 400, 1080, 1478))
|
||||
|
||||
val error = runCatching { PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress("_cg390") }
|
||||
.exceptionOrNull() as PurchaseLiveException
|
||||
|
||||
assertEquals("PURCHASE_ADDRESS_ENTRY_AMBIGUOUS", error.code)
|
||||
assertEquals(0, driver.scopedSwipes + driver.genericSwipes + driver.inputCount + driver.submitClicks)
|
||||
assertTrue(driver.clicked.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `near square purchase panel stops after unchanged viewport without address`() {
|
||||
assertClippedPanelTimeout(viewportChanges = false, expectedSwipes = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `near square purchase panel stops within five rounds without address`() {
|
||||
assertClippedPanelTimeout(viewportChanges = true, expectedSwipes = 4)
|
||||
}
|
||||
|
||||
private fun assertClippedPanelTimeout(viewportChanges: Boolean, expectedSwipes: Int) {
|
||||
val bounds = NodeBounds(0, 400, 1080, 1478)
|
||||
val driver = LiveDriver(
|
||||
addressClipped = true, clippedPanelBounds = bounds,
|
||||
keepAddressClipped = true, clippedViewportChanges = viewportChanges,
|
||||
)
|
||||
|
||||
val error = runCatching { PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress("_cg390") }
|
||||
.exceptionOrNull() as PurchaseLiveException
|
||||
|
||||
assertEquals("PURCHASE_ADDRESS_PANEL_TIMEOUT", error.code)
|
||||
assertEquals(List(expectedSwipes) { Triple(bounds, SwipeDirection.DOWN, 350L) }, driver.scopedSwipeRequests)
|
||||
assertEquals(0, driver.genericSwipes + driver.inputCount + driver.submitClicks)
|
||||
assertTrue(driver.clicked.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `saved address evidence is restored through near square purchase panel`() {
|
||||
val bounds = NodeBounds(0, 400, 1080, 1478)
|
||||
val driver = LiveDriver(saveReturnsToSpecPanel = true, clippedPanelBounds = bounds)
|
||||
val automation = PurchaseLiveAutomation(driver, pause = {})
|
||||
|
||||
val proof = automation.updateShippingAddress("_cg390")
|
||||
val final = automation.finalConfirmation(input().copy(addressSuffix = "_cg390"), proof)
|
||||
|
||||
assertEquals("_cg390", final.addressSuffix)
|
||||
assertEquals(listOf(Triple(bounds, SwipeDirection.DOWN, 350L)), driver.scopedSwipeRequests)
|
||||
assertEquals(0, driver.backCount + driver.genericSwipes + driver.submitClicks)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ambiguous purchase panels fail without swiping or clicking`() {
|
||||
val driver = LiveDriver(addressClipped = true, duplicatePanels = true)
|
||||
@@ -1555,10 +1448,6 @@ class PurchaseLiveAutomationTest {
|
||||
private class LiveDriver(
|
||||
private val addressClipped: Boolean = false,
|
||||
private val duplicatePanels: Boolean = false,
|
||||
private val clippedPanelBounds: NodeBounds? = null,
|
||||
private val clippedScreenHeight: Int = 2376,
|
||||
private val keepAddressClipped: Boolean = false,
|
||||
private val clippedViewportChanges: Boolean = false,
|
||||
private val chooserAfterSubmit: Boolean = false,
|
||||
private val trustedChooser: Boolean = true,
|
||||
private val splitConfirmationAddress: Boolean = false,
|
||||
@@ -1591,7 +1480,6 @@ class PurchaseLiveAutomationTest {
|
||||
val clicked = mutableListOf<String>()
|
||||
var submitClicks = 0
|
||||
var scopedSwipes = 0
|
||||
val scopedSwipeRequests = mutableListOf<Triple<NodeBounds, SwipeDirection, Long>>()
|
||||
var genericSwipes = 0
|
||||
var addressTaps = 0
|
||||
var addressPathClicks = 0
|
||||
@@ -1612,21 +1500,7 @@ class PurchaseLiveAutomationTest {
|
||||
postSubmitCaptureCount++
|
||||
if (pendingPostSubmitPages.isNotEmpty()) page = pendingPostSubmitPages.removeAt(0)
|
||||
}
|
||||
val snapshot = currentSnapshot().let { original ->
|
||||
if (clippedPanelBounds != null && ((page == "confirmation" && !addressVisible) || page == "post-save-spec")) {
|
||||
original.copy(nodes = original.nodes.map {
|
||||
when (it.path) {
|
||||
"root" -> it.copy(bounds = NodeBounds(0, 0, 1080, clippedScreenHeight))
|
||||
"panel", "panel2" -> it.copy(bounds = clippedPanelBounds)
|
||||
else -> it
|
||||
}
|
||||
} + node("panel/collapsed-address", "138****5678", parentPath = "panel",
|
||||
bounds = NodeBounds(20, 400, 400, 400)).copy(visible = false) +
|
||||
node("panel/viewport-marker", "", parentPath = "panel", bounds = NodeBounds(
|
||||
20, 800 + if (clippedViewportChanges) scopedSwipes else 0, 300, 850,
|
||||
)))
|
||||
} else original
|
||||
}
|
||||
val snapshot = currentSnapshot()
|
||||
if (page == "edit" && inputCount == 0) {
|
||||
initialEditCaptures++
|
||||
return initialEditFrame(snapshot, initialEditCaptures).also { lastInitialEditSnapshot = it }
|
||||
@@ -1817,8 +1691,7 @@ class PurchaseLiveAutomationTest {
|
||||
}
|
||||
override fun swipePurchaseIn(target: SnapshotNode, direction: SwipeDirection, durationMs: Long): Boolean {
|
||||
scopedSwipes++
|
||||
scopedSwipeRequests += Triple(target.bounds, direction, durationMs)
|
||||
if (target.path == "panel" && direction == SwipeDirection.DOWN && !keepAddressClipped) addressVisible = true
|
||||
if (target.path == "panel" && direction == SwipeDirection.DOWN) addressVisible = true
|
||||
if (page == "post-save-spec" && direction == SwipeDirection.DOWN && !savedSpecPanelRecoveryStuck) page = "confirmation"
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package sybimport
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// SYB returns numeric isCancel: 1 cancelled, 0 not cancelled. Do not infer
|
||||
// anything from status/orderStatus, missing fields or undocumented values.
|
||||
// SYB's numeric isCancel enum is 0 (正常), 1 (取消), 2 (已取消).
|
||||
// Both 1 and 2 mean cancelled; other nonzero values remain unknown. Do not
|
||||
// infer anything from status/orderStatus or missing fields.
|
||||
func parseSYBIsCancelled(raw map[string]any) *bool {
|
||||
var number float64
|
||||
switch value := raw["isCancel"].(type) {
|
||||
@@ -18,9 +19,9 @@ func parseSYBIsCancelled(raw map[string]any) *bool {
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if number != 0 && number != 1 {
|
||||
if number != 0 && number != 1 && number != 2 {
|
||||
return nil
|
||||
}
|
||||
cancelled := number == 1
|
||||
cancelled := number == 1 || number == 2
|
||||
return &cancelled
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ package sybimport
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -14,23 +18,42 @@ import (
|
||||
|
||||
func TestParseSYBIsCancelled(t *testing.T) {
|
||||
for _, tc := range []struct{ input, want string }{
|
||||
{`{"isCancel":1}`, "true"}, {`{"isCancel":0}`, "false"},
|
||||
{`{"isCancel":1}`, "true"}, {`{"isCancel":2}`, "true"}, {`{"isCancel":0}`, "false"},
|
||||
{`{"isCancel":0.0}`, "false"}, {`{"isCancel":1.0}`, "true"}, {`{"isCancel":2.0}`, "true"},
|
||||
{`{"isCancel":2e0}`, "true"},
|
||||
{`{}`, "null"}, {`{"isCancel":null}`, "null"},
|
||||
{`{"isCancel":2}`, "null"}, {`{"isCancel":-1}`, "null"},
|
||||
{`{"isCancel":3}`, "null"}, {`{"isCancel":-1}`, "null"},
|
||||
{`{"isCancel":0.5}`, "null"}, {`{"isCancel":"1"}`, "null"},
|
||||
{`{"isCancel":true}`, "null"}, {`{"status":79,"orderStatus":"已取消"}`, "null"},
|
||||
{`{"isCancel":1.5}`, "null"}, {`{"isCancel":2.5}`, "null"},
|
||||
{`{"isCancel":"2"}`, "null"}, {`{"isCancel":"0"}`, "null"}, {`{"isCancel":""}`, "null"},
|
||||
{`{"isCancel":true}`, "null"}, {`{"isCancel":false}`, "null"},
|
||||
{`{"status":79,"orderStatus":"已取消","platformorderStatus":"CANCELLED"}`, "null"},
|
||||
{`{"isCancel":0,"status":79,"orderStatus":"已取消","platformorderStatus":"CANCELLED"}`, "false"},
|
||||
{`{"isCancel":0,"orderStatus":"申请取消"}`, "false"},
|
||||
{`{"isCancel":1,"orderStatus":"已发货"}`, "true"},
|
||||
{`{"isCancel":2,"orderStatus":"已发货"}`, "true"},
|
||||
{`{"isCancel":2,"orderStatus":"运送中"}`, "true"},
|
||||
{`{"isCancel":2,"orderStatus":"待出货"}`, "true"},
|
||||
} {
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal([]byte(tc.input), &raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, _ := json.Marshal(parseSYBIsCancelled(raw))
|
||||
if string(got) != tc.want {
|
||||
t.Errorf("%s: %s, want %s", tc.input, got, tc.want)
|
||||
for _, useNumber := range []bool{false, true} {
|
||||
var raw map[string]any
|
||||
decoder := json.NewDecoder(strings.NewReader(tc.input))
|
||||
if useNumber {
|
||||
decoder.UseNumber()
|
||||
}
|
||||
if err := decoder.Decode(&raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, _ := json.Marshal(parseSYBIsCancelled(raw))
|
||||
if string(got) != tc.want {
|
||||
t.Errorf("UseNumber=%t %s: %s, want %s", useNumber, tc.input, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
if got := parseSYBIsCancelled(map[string]any{"isCancel": json.Number("1")}); got == nil || !*got {
|
||||
t.Fatal("json.Number")
|
||||
for _, value := range []any{json.Number("invalid"), json.Number("1e999"), json.Number("NaN"), math.NaN(), math.Inf(1)} {
|
||||
if got := parseSYBIsCancelled(map[string]any{"isCancel": value}); got != nil {
|
||||
t.Errorf("invalid numeric value %v must remain unknown", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +64,29 @@ func TestSyncCancellationDisplayAndListContract(t *testing.T) {
|
||||
detail := sybclient.StockDetail{ID: row.ID, Code: row.Code, Details: []sybclient.DetailItem{
|
||||
{ID: 711, ProductID: 701, ProductQty: 1, ProductSpec: "黑色,XL"},
|
||||
{ID: 712, ProductID: 702, ProductQty: 1, ProductSpec: "白色,L"},
|
||||
{ID: 713, ProductID: 703, ProductQty: 1, ProductSpec: "蓝色,M"},
|
||||
}}
|
||||
untouchedTime := time.Date(2026, 9, 30, 12, 0, 0, 0, time.UTC)
|
||||
unrelated := models.SYBProduct{OrderCode: "MOCK-UNRELATED", DetailID: 721, StockID: 72,
|
||||
Quantity: 1, ParseStatus: models.SYBParseStatusSuccess, RawJSON: `{}`,
|
||||
SYBIsCancelled: boolPointer(false), SYBStatusSyncedAt: &untouchedTime}
|
||||
if err := db.Create(&unrelated).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var previous *time.Time
|
||||
var existingTask models.PurchaseTask
|
||||
var firstDetailID uint64
|
||||
for _, tc := range []struct {
|
||||
raw string
|
||||
want *bool
|
||||
raw string
|
||||
want *bool
|
||||
valid bool
|
||||
}{
|
||||
{`{}`, nil}, {`{"isCancel":1}`, boolPointer(true)},
|
||||
{`{"isCancel":null}`, boolPointer(true)}, {`{"isCancel":7}`, boolPointer(true)},
|
||||
{`{"isCancel":0}`, boolPointer(false)}, {`{}`, boolPointer(false)},
|
||||
{`{}`, nil, false}, {`{"isCancel":0}`, boolPointer(false), true},
|
||||
{`{"isCancel":2,"orderStatus":"已发货"}`, boolPointer(true), true},
|
||||
{`{"isCancel":2}`, boolPointer(true), true},
|
||||
{`{"isCancel":null}`, boolPointer(true), false}, {`{"isCancel":7}`, boolPointer(true), false},
|
||||
{`{"isCancel":"2"}`, boolPointer(true), false}, {`{}`, boolPointer(true), false},
|
||||
{`{"isCancel":0}`, boolPointer(false), true}, {`{}`, boolPointer(false), false},
|
||||
} {
|
||||
var values map[string]any
|
||||
if err := json.Unmarshal([]byte(tc.raw), &values); err != nil {
|
||||
@@ -59,17 +96,43 @@ func TestSyncCancellationDisplayAndListContract(t *testing.T) {
|
||||
for key, value := range values {
|
||||
row.Raw[key] = value
|
||||
}
|
||||
beforeSync := time.Now().UTC()
|
||||
if err := applyStockDetail(ctx, db, row, detail, &SyncReport{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
afterSync := time.Now().UTC()
|
||||
var records []models.SYBProduct
|
||||
if err := db.Order("id").Find(&records).Error; err != nil {
|
||||
if err := db.Where("order_code = ?", row.Code).Order("id").Find(&records).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(records) != 2 {
|
||||
t.Fatal("must keep both details")
|
||||
if len(records) != 3 {
|
||||
t.Fatal("must keep all three details without duplicates")
|
||||
}
|
||||
firstDetailID = records[0].ID
|
||||
if existingTask.ID == 0 {
|
||||
product := models.PDDProduct{GoodsID: "389001", URL: "https://example.invalid/389001", SpecsJSON: `{}`}
|
||||
if err := db.Create(&product).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
existingTask = models.PurchaseTask{SYBProductID: &records[0].ID, PDDProductID: product.ID,
|
||||
ExecutionMode: models.PurchaseExecutionModeLive, Status: models.PurchaseTaskStatusPending,
|
||||
Quantity: 1, Currency: "TWD", CreateRequestID: "mock-389-existing-task",
|
||||
SpecDecisionSnapshot: `{}`, RuleSnapshot: `{}`, RequiredCapabilitiesJSON: `[]`}
|
||||
if err := db.Create(&existingTask).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.First(&existingTask, existingTask.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
var current models.PurchaseTask
|
||||
if err := db.First(¤t, existingTask.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(current, existingTask) {
|
||||
t.Fatal("cancellation sync must leave the existing purchase task unchanged")
|
||||
}
|
||||
}
|
||||
valid := parseSYBIsCancelled(values) != nil
|
||||
for _, record := range records {
|
||||
got, _ := json.Marshal(record.SYBIsCancelled)
|
||||
want, _ := json.Marshal(tc.want)
|
||||
@@ -82,15 +145,25 @@ func TestSyncCancellationDisplayAndListContract(t *testing.T) {
|
||||
if tc.want != nil && record.SYBStatusSyncedAt == nil {
|
||||
t.Fatal("known must have timestamp")
|
||||
}
|
||||
if !valid && previous != nil && !record.SYBStatusSyncedAt.Equal(*previous) {
|
||||
if !tc.valid && previous != nil && !record.SYBStatusSyncedAt.Equal(*previous) {
|
||||
t.Fatal("missing/invalid must preserve time")
|
||||
}
|
||||
if tc.valid && (record.SYBStatusSyncedAt.Before(beforeSync) || record.SYBStatusSyncedAt.After(afterSync)) {
|
||||
t.Fatal("valid values, including repeats, must refresh the sync timestamp")
|
||||
}
|
||||
if records[0].SYBStatusSyncedAt != nil && !record.SYBStatusSyncedAt.Equal(*records[0].SYBStatusSyncedAt) {
|
||||
t.Fatal("same order must use same timestamp for all details")
|
||||
}
|
||||
if record.PDDExcluded {
|
||||
t.Fatal("display status must not exclude procurement")
|
||||
}
|
||||
}
|
||||
if records[0].SYBStatusSyncedAt != nil && !records[0].SYBStatusSyncedAt.Equal(*records[1].SYBStatusSyncedAt) {
|
||||
t.Fatal("same order must use same timestamp")
|
||||
var other models.SYBProduct
|
||||
if err := db.First(&other, unrelated.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if other.SYBIsCancelled == nil || *other.SYBIsCancelled || other.SYBStatusSyncedAt == nil || !other.SYBStatusSyncedAt.Equal(untouchedTime) {
|
||||
t.Fatal("sync must not alter another order's cancellation status or timestamp")
|
||||
}
|
||||
previous = records[0].SYBStatusSyncedAt
|
||||
}
|
||||
@@ -116,7 +189,7 @@ func TestSyncCancellationDisplayAndListContract(t *testing.T) {
|
||||
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(payload.Data.Items) != 2 {
|
||||
if len(payload.Data.Items) != 4 {
|
||||
t.Fatal("list count")
|
||||
}
|
||||
for _, item := range payload.Data.Items {
|
||||
@@ -135,7 +208,7 @@ func TestSyncCancellationDisplayAndListContract(t *testing.T) {
|
||||
}
|
||||
}
|
||||
response = httptest.NewRecorder()
|
||||
router.ServeHTTP(response, httptest.NewRequest("GET", "/products/1", nil))
|
||||
router.ServeHTTP(response, httptest.NewRequest("GET", fmt.Sprintf("/products/%d", firstDetailID), nil))
|
||||
if response.Code != 200 {
|
||||
t.Fatalf("detail: %d", response.Code)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user