mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Preserve Daiso pickup answers when Bearer auth degrades
Keep exact stock lookup on the official Bearer-token path while restoring the public selPkupStr fallback for repeated auth blocks. Constraint: PR #250 review required Bearer auth to remain primary without removing the resilient pickup eligibility API. Rejected: Throwing after the retry | it collapses callers back to a brittle single upstream-auth dependency. Confidence: high Scope-risk: narrow Directive: Keep pickupStock quantity semantics separate from pickupEligibility yes/no fallback. Tested: node --test packages/daiso-product-search/test/index.test.js; npm test --workspace daiso-product-search; npm run lint --workspace daiso-product-search; npm run ci; live lookupStoreProductAvailability smoke for 강남역2호점 / VT 리들샷 100. Not-tested: Live forced 403 from Daiso upstream; covered with injected fetch regression tests.
This commit is contained in:
parent
2641f43863
commit
d7263a54b9
4 changed files with 337 additions and 17 deletions
|
|
@ -2,4 +2,4 @@
|
|||
"daiso-product-search": minor
|
||||
---
|
||||
|
||||
Restore actionable Daiso pickup answers when store pickup stock is blocked by adding a `selPkupStr`-backed `getStorePickupEligibility()` helper plus `pickupEligibility` field on `lookupStoreProductAvailability()`. When pickup stock returns `Unauthorized`, the package now reports whether the selected store is registered as a pickup-capable store for the product instead of only saying "unknown".
|
||||
Restore Daiso store pickup stock quantities through the official non-login Bearer flow (`/api/auth/request` + AES-128-CBC token) while keeping the resilient `selPkupStr` fallback API. `getStorePickupStock()` now retries once with a fresh token on 401/403 and returns structured `retrievalStatus: "blocked"` after repeated auth blocks instead of throwing. `getStorePickupEligibility()` remains public, and `lookupStoreProductAvailability()` fills `pickupEligibility` when exact pickup stock remains unavailable.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ npm install
|
|||
|
||||
- 매장명과 상품명 둘 다 필요합니다.
|
||||
- 공식 다이소몰 표면을 우선 사용합니다.
|
||||
- `selStrPkupStck` 는 Bearer 토큰 인증이 필요합니다. `/api/auth/request` 로 비로그인 JWT를 받아 AES-128-CBC / 키 `"PRE_AUTH_ENC_KEY"` 로 암호화한 뒤 Bearer 헤더로 전달합니다. 403 응답 시 토큰을 재발급해 1회 재시도합니다.
|
||||
- `selStrPkupStck` 는 Bearer 토큰 인증이 필요합니다. `/api/auth/request` 로 비로그인 JWT를 받아 AES-128-CBC / 키 `"PRE_AUTH_ENC_KEY"` 로 암호화한 뒤 Bearer 헤더로 전달합니다. 401/403 응답 시 토큰을 재발급해 1회 재시도합니다. 그래도 인증이 막히면 수량 조회는 `retrievalStatus: "blocked"` 로 반환하고 `selPkupStr` 픽업 가능 여부 폴백을 사용할 수 있습니다.
|
||||
- 매장 픽업 재고의 `status` 는 조회 결과 범주입니다. 실제 재고 여부는 `inStock` 또는 `inventoryStatus` (`"in_stock"`, `"out_of_stock"`, `"unknown"`) 를 기준으로 판단합니다.
|
||||
- 공식 표면이 매장 내 진열 위치를 주지 않으면 재고 중심으로 응답해야 합니다.
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ async function main() {
|
|||
console.log(result.selectedStore)
|
||||
console.log(result.selectedProduct)
|
||||
console.log(result.pickupStock)
|
||||
console.log(result.pickupEligibility)
|
||||
console.log(result.onlineStock)
|
||||
}
|
||||
|
||||
|
|
@ -82,10 +83,15 @@ main().catch((error) => {
|
|||
- `searchProducts(query, options?)`
|
||||
- 반환되는 각 상품 후보는 `pdNo` 와 함께 `onldPdNo` 를 포함할 수 있습니다. 다이소몰 온라인 재고 표면이 별도 마스터 상품 번호를 요구하는 경우 이 값을 그대로 `getOnlineStock()` 에 넘기면 됩니다.
|
||||
- `getStorePickupStock({ pdNo, strCd }, options?)`
|
||||
- 호출 전 `/api/auth/request` 로 Bearer 토큰을 자동 빌드합니다. 403 응답 시 토큰을 재발급해 1회 재시도합니다.
|
||||
- 호출 전 `/api/auth/request` 로 Bearer 토큰을 자동 빌드합니다. 401/403 응답 시 토큰을 재발급해 1회 재시도합니다.
|
||||
- 성공한 조회는 `status: "available"`, `retrievalStatus: "resolved"` 를 포함합니다. 여기서 `status` 는 조회 성공 범주이며 상품 재고 여부가 아닙니다.
|
||||
- 실제 재고 여부는 `inStock` 또는 `inventoryStatus` 로 확인합니다. 수량이 0이면 `status: "available"` 이면서 `inventoryStatus: "out_of_stock"` 일 수 있습니다.
|
||||
- 인증이 계속 막히면 예외 대신 `status: "unavailable"`, `retrievalStatus: "blocked"`, `inventoryStatus: "unknown"` 를 반환합니다.
|
||||
- `getStorePickupEligibility({ pdNo, strCd, storeName?, keyword?, pageSize? }, options?)`
|
||||
- `selPkupStr` 로 특정 상품의 픽업 가능 매장 목록을 조회해 선택 매장이 픽업 가능 매장인지 확인합니다.
|
||||
- 수량은 제공하지 않으며 `pickupEligible` (`true`/`false`/`null`) 과 `retrievalStatus` (`"resolved"`, `"blocked"`, `"insufficient_coverage"`) 로 폴백 판단을 전달합니다.
|
||||
- `getOnlineStock({ pdNo, onldPdNo? }, options?)`
|
||||
- 반환값은 `referenceOnly: true` 를 포함합니다. 온라인 재고는 다이소몰 온라인몰 재고 참고값이며 특정 매장의 픽업/진열 재고가 아닙니다.
|
||||
- `lookupStoreProductAvailability({ storeQuery, productQuery, ...options })`
|
||||
- 매장·상품 검색 → Bearer 인증 → 픽업 재고 조회를 한 번에 처리합니다.
|
||||
- 픽업 재고 인증이 계속 막혀 `pickupStock.retrievalStatus === "blocked"` 이면 `pickupEligibility` 에 `selPkupStr` 기반 픽업 가능 여부를 채웁니다. 필요 없으면 `includePickupEligibility: false` 를 전달합니다.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const {
|
|||
BASE_SEARCH_URL,
|
||||
buildSearchGoodsParams,
|
||||
normalizeOnlineStockResponse,
|
||||
normalizePickupEligibilityResponse,
|
||||
normalizeProductIdentifier,
|
||||
normalizeSearchGoodsResponse,
|
||||
normalizeStorePickupStockResponse,
|
||||
|
|
@ -104,6 +105,22 @@ async function buildBearerToken(options = {}) {
|
|||
return { bearer, uid }
|
||||
}
|
||||
|
||||
function isAuthBlockedError(error) {
|
||||
return error instanceof DaisoRequestError && (error.status === 401 || error.status === 403)
|
||||
}
|
||||
|
||||
function normalizeAuthBlockedStock(request, error) {
|
||||
return normalizeStorePickupStockResponse(
|
||||
{
|
||||
success: false,
|
||||
message: "Unauthorized",
|
||||
status: error && error.status,
|
||||
upstreamPayload: error && error.payload ? error.payload : null
|
||||
},
|
||||
request
|
||||
)
|
||||
}
|
||||
|
||||
async function searchStores(query, options = {}) {
|
||||
const body = {
|
||||
keyword: String(query || "").trim(),
|
||||
|
|
@ -144,28 +161,33 @@ async function searchProducts(query, options = {}) {
|
|||
}
|
||||
|
||||
async function getStorePickupStock(request, options = {}) {
|
||||
const body = [{ pdNo: String(request.pdNo), strCd: String(request.strCd) }]
|
||||
|
||||
async function requestStockWithFreshToken() {
|
||||
const { bearer, uid } = await buildBearerToken(options)
|
||||
const authHeaders = { Authorization: `Bearer ${bearer}`, "X-DM-UID": uid }
|
||||
const payload = await requestJson(`${BASE_API_URL}/pd/pdh/selStrPkupStck`, {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${bearer}`, "X-DM-UID": uid },
|
||||
body
|
||||
})
|
||||
|
||||
return normalizeStorePickupStockResponse(payload, request)
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await requestJson(`${BASE_API_URL}/pd/pdh/selStrPkupStck`, {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: authHeaders,
|
||||
body: [{ pdNo: String(request.pdNo), strCd: String(request.strCd) }]
|
||||
})
|
||||
|
||||
return normalizeStorePickupStockResponse(payload, request)
|
||||
return await requestStockWithFreshToken()
|
||||
} catch (error) {
|
||||
if (error instanceof DaisoRequestError && error.status === 403) {
|
||||
const { bearer: newBearer, uid: newUid } = await buildBearerToken(options)
|
||||
const payload = await requestJson(`${BASE_API_URL}/pd/pdh/selStrPkupStck`, {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${newBearer}`, "X-DM-UID": newUid },
|
||||
body: [{ pdNo: String(request.pdNo), strCd: String(request.strCd) }]
|
||||
})
|
||||
return normalizeStorePickupStockResponse(payload, request)
|
||||
if (!isAuthBlockedError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await requestStockWithFreshToken()
|
||||
} catch (error) {
|
||||
if (isAuthBlockedError(error)) {
|
||||
return normalizeAuthBlockedStock(request, error)
|
||||
}
|
||||
|
||||
throw error
|
||||
|
|
@ -186,6 +208,73 @@ async function getOnlineStock(request, options = {}) {
|
|||
return normalizeOnlineStockResponse(payload, normalizedRequest)
|
||||
}
|
||||
|
||||
function buildPickupEligibilityKeyword(value) {
|
||||
return String(value || "")
|
||||
.replace(/\d+\s*호점\s*$/u, "")
|
||||
.replace(/[(].*?[)]/gu, " ")
|
||||
.replace(/\s+/gu, " ")
|
||||
.trim()
|
||||
}
|
||||
|
||||
async function getStorePickupEligibility(request, options = {}) {
|
||||
const pdNo = String(request.pdNo || "").trim()
|
||||
const strCd = String(request.strCd || "").trim()
|
||||
const explicitKeyword =
|
||||
typeof request.keyword === "string" && request.keyword.trim() ? request.keyword.trim() : null
|
||||
const derivedKeyword = explicitKeyword || buildPickupEligibilityKeyword(request.storeName)
|
||||
const pageSize = Number(request.pageSize || 50)
|
||||
|
||||
if (!pdNo) {
|
||||
throw new Error("pdNo is required.")
|
||||
}
|
||||
|
||||
if (strCd && !derivedKeyword) {
|
||||
return {
|
||||
pdNo,
|
||||
strCd,
|
||||
pickupEligible: null,
|
||||
eligibleStoreCount: null,
|
||||
eligibleStores: [],
|
||||
matchedStore: null,
|
||||
searchedKeyword: "",
|
||||
pageSize,
|
||||
totalCount: null,
|
||||
retrievalStatus: "insufficient_coverage",
|
||||
reason: "missing_search_keyword",
|
||||
raw: null
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await requestJson(`${BASE_API_URL}/ms/msg/selPkupStr`, {
|
||||
...options,
|
||||
method: "POST",
|
||||
body: {
|
||||
pdNo,
|
||||
keyword: derivedKeyword || "",
|
||||
currentPage: 1,
|
||||
pageSize
|
||||
}
|
||||
})
|
||||
|
||||
return normalizePickupEligibilityResponse(payload, {
|
||||
pdNo,
|
||||
strCd,
|
||||
keyword: derivedKeyword || "",
|
||||
pageSize
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof DaisoRequestError) {
|
||||
return normalizePickupEligibilityResponse(
|
||||
error.payload || { success: false, message: `HTTP ${error.status}` },
|
||||
{ pdNo, strCd, keyword: derivedKeyword || "", pageSize }
|
||||
)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function lookupStoreProductAvailability(options = {}) {
|
||||
const storeQuery = String(options.storeQuery || "").trim()
|
||||
const productQuery = String(options.productQuery || "").trim()
|
||||
|
|
@ -228,6 +317,23 @@ async function lookupStoreProductAvailability(options = {}) {
|
|||
getStorePickupStock({ pdNo: selectedProduct.pdNo, strCd: selectedStore.strCd }, options)
|
||||
])
|
||||
|
||||
let pickupEligibility = null
|
||||
|
||||
if (
|
||||
options.includePickupEligibility !== false &&
|
||||
pickupStock &&
|
||||
pickupStock.retrievalStatus === "blocked"
|
||||
) {
|
||||
pickupEligibility = await getStorePickupEligibility(
|
||||
{
|
||||
pdNo: selectedProduct.pdNo,
|
||||
strCd: selectedStore.strCd,
|
||||
storeName: selectedStore.name
|
||||
},
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
const onlineStock = await onlineStockPromise
|
||||
|
||||
return {
|
||||
|
|
@ -239,6 +345,7 @@ async function lookupStoreProductAvailability(options = {}) {
|
|||
storeDetail: storeDetailPayload.data || null,
|
||||
selectedProduct,
|
||||
pickupStock,
|
||||
pickupEligibility,
|
||||
onlineStock
|
||||
}
|
||||
}
|
||||
|
|
@ -246,6 +353,7 @@ async function lookupStoreProductAvailability(options = {}) {
|
|||
module.exports = {
|
||||
getOnlineStock,
|
||||
getStoreDetail,
|
||||
getStorePickupEligibility,
|
||||
getStorePickupStock,
|
||||
lookupStoreProductAvailability,
|
||||
searchProducts,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const path = require("node:path")
|
|||
const {
|
||||
getOnlineStock,
|
||||
getStoreDetail,
|
||||
getStorePickupEligibility,
|
||||
getStorePickupStock,
|
||||
lookupStoreProductAvailability,
|
||||
searchProducts,
|
||||
|
|
@ -13,6 +14,7 @@ const {
|
|||
} = require("../src/index")
|
||||
const {
|
||||
buildSearchGoodsParams,
|
||||
normalizePickupEligibilityResponse,
|
||||
normalizeSearchGoodsResponse,
|
||||
normalizeStorePickupStockResponse,
|
||||
normalizeStoreSearchResponse
|
||||
|
|
@ -24,6 +26,27 @@ const searchGoodsPayload = JSON.parse(fs.readFileSync(path.join(fixturesDir, "se
|
|||
const storeDetailPayload = JSON.parse(fs.readFileSync(path.join(fixturesDir, "store-detail.json"), "utf8"))
|
||||
const storePickupStockPayload = JSON.parse(fs.readFileSync(path.join(fixturesDir, "store-pickup-stock.json"), "utf8"))
|
||||
const onlineStockPayload = JSON.parse(fs.readFileSync(path.join(fixturesDir, "online-stock.json"), "utf8"))
|
||||
|
||||
const storePickupEligibilityPayload = {
|
||||
data: [
|
||||
{
|
||||
strCd: "10224",
|
||||
strNm: "강남역2호점",
|
||||
strAddr: "서울특별시 강남구 강남대로",
|
||||
strDtlAddr: "지하 1층",
|
||||
strTno: "02-1234-5678",
|
||||
pkupYn: "Y",
|
||||
opngTime: "1000",
|
||||
clsngTime: "2200",
|
||||
km: "0.2",
|
||||
strLttd: "37.498095",
|
||||
strLitd: "127.02761",
|
||||
totalCnt: 1,
|
||||
currentPageCnt: 1
|
||||
}
|
||||
],
|
||||
success: true
|
||||
}
|
||||
const liveSearchGoodsPayload = {
|
||||
resultSet: {
|
||||
result: [
|
||||
|
|
@ -562,3 +585,186 @@ test("lookupStoreProductAvailability reuses a product candidate's online stock i
|
|||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test("getStorePickupStock sends Bearer auth headers and returns blocked after repeated auth failures", async () => {
|
||||
const originalFetch = global.fetch
|
||||
const stockRequests = []
|
||||
let authCallCount = 0
|
||||
|
||||
global.fetch = async (url, init = {}) => {
|
||||
if (String(url).includes("/api/auth/request")) {
|
||||
authCallCount++
|
||||
return makeAuthResponse()
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/pd/pdh/selStrPkupStck")) {
|
||||
stockRequests.push({ headers: init.headers, body: JSON.parse(init.body) })
|
||||
return makeResponse({ success: false, message: "Unauthorized" }, { status: 403 })
|
||||
}
|
||||
|
||||
return new Response("not found", { status: 404 })
|
||||
}
|
||||
|
||||
try {
|
||||
const pickupStock = await getStorePickupStock({ pdNo: "1049275", strCd: "10224" })
|
||||
|
||||
assert.equal(authCallCount, 2)
|
||||
assert.equal(stockRequests.length, 2)
|
||||
for (const request of stockRequests) {
|
||||
assert.match(request.headers.Authorization, /^Bearer /)
|
||||
assert.equal(request.headers["X-DM-UID"], "test-uid-123")
|
||||
assert.deepEqual(request.body, [{ pdNo: "1049275", strCd: "10224" }])
|
||||
}
|
||||
assert.equal(pickupStock.status, "unavailable")
|
||||
assert.equal(pickupStock.retrievalStatus, "blocked")
|
||||
assert.equal(pickupStock.reason, "unauthorized")
|
||||
} finally {
|
||||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test("getStorePickupEligibility posts pdNo and a derived store keyword to selPkupStr", async () => {
|
||||
const originalFetch = global.fetch
|
||||
let capturedBody = null
|
||||
let capturedUrl = null
|
||||
|
||||
global.fetch = async (url, init = {}) => {
|
||||
capturedUrl = String(url)
|
||||
capturedBody = JSON.parse(init.body)
|
||||
return makeResponse(storePickupEligibilityPayload)
|
||||
}
|
||||
|
||||
try {
|
||||
const eligibility = await getStorePickupEligibility({
|
||||
pdNo: "1049275",
|
||||
strCd: "10224",
|
||||
storeName: "강남역2호점"
|
||||
})
|
||||
|
||||
assert.match(capturedUrl, /\/api\/ms\/msg\/selPkupStr$/)
|
||||
assert.equal(capturedBody.pdNo, "1049275")
|
||||
assert.equal(capturedBody.keyword, "강남역")
|
||||
assert.equal(capturedBody.currentPage, 1)
|
||||
assert.equal(typeof capturedBody.pageSize, "number")
|
||||
assert.equal(eligibility.pickupEligible, true)
|
||||
assert.equal(eligibility.matchedStore.strCd, "10224")
|
||||
} finally {
|
||||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test("lookupStoreProductAvailability falls back to pickup eligibility when Bearer stock remains forbidden", async () => {
|
||||
const originalFetch = global.fetch
|
||||
let eligibilityCalled = false
|
||||
|
||||
global.fetch = async (url) => {
|
||||
if (String(url).includes("/api/auth/request")) {
|
||||
return makeAuthResponse()
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/ms/msg/selStr") && !String(url).includes("selStrInfo") && !String(url).includes("selPkupStr")) {
|
||||
return makeResponse(storeSearchPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/ssn/search/SearchGoods")) {
|
||||
return makeResponse(searchGoodsPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/dl/dla-api/selStrInfo")) {
|
||||
return makeResponse(storeDetailPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/pd/pdh/selStrPkupStck")) {
|
||||
return makeResponse({ success: false, message: "Unauthorized" }, { status: 403 })
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/ms/msg/selPkupStr")) {
|
||||
eligibilityCalled = true
|
||||
return makeResponse(storePickupEligibilityPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/pdo/selOnlStck")) {
|
||||
return makeResponse(onlineStockPayload)
|
||||
}
|
||||
|
||||
return new Response("not found", { status: 404 })
|
||||
}
|
||||
|
||||
try {
|
||||
const availability = await lookupStoreProductAvailability({
|
||||
storeQuery: "강남역2호점",
|
||||
productQuery: "VT 리들샷 100"
|
||||
})
|
||||
|
||||
assert.equal(availability.pickupStock.retrievalStatus, "blocked")
|
||||
assert.equal(eligibilityCalled, true)
|
||||
assert.equal(availability.pickupEligibility.pickupEligible, true)
|
||||
assert.equal(availability.pickupEligibility.matchedStore.strCd, "10224")
|
||||
assert.equal(availability.onlineStock.quantity, 13047)
|
||||
} finally {
|
||||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
test("lookupStoreProductAvailability falls back to pickup eligibility when token issuance is forbidden", async () => {
|
||||
const originalFetch = global.fetch
|
||||
let eligibilityCalled = false
|
||||
|
||||
global.fetch = async (url) => {
|
||||
if (String(url).includes("/api/auth/request")) {
|
||||
return new Response("forbidden", { status: 403, headers: { "content-type": "text/plain" } })
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/ms/msg/selStr") && !String(url).includes("selStrInfo") && !String(url).includes("selPkupStr")) {
|
||||
return makeResponse(storeSearchPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/ssn/search/SearchGoods")) {
|
||||
return makeResponse(searchGoodsPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/dl/dla-api/selStrInfo")) {
|
||||
return makeResponse(storeDetailPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/ms/msg/selPkupStr")) {
|
||||
eligibilityCalled = true
|
||||
return makeResponse(storePickupEligibilityPayload)
|
||||
}
|
||||
|
||||
if (String(url).includes("/api/pdo/selOnlStck")) {
|
||||
return makeResponse(onlineStockPayload)
|
||||
}
|
||||
|
||||
return new Response("not found", { status: 404 })
|
||||
}
|
||||
|
||||
try {
|
||||
const availability = await lookupStoreProductAvailability({
|
||||
storeQuery: "강남역2호점",
|
||||
productQuery: "VT 리들샷 100"
|
||||
})
|
||||
|
||||
assert.equal(availability.pickupStock.retrievalStatus, "blocked")
|
||||
assert.equal(availability.pickupStock.inventoryStatus, "unknown")
|
||||
assert.equal(eligibilityCalled, true)
|
||||
assert.equal(availability.pickupEligibility.pickupEligible, true)
|
||||
} finally {
|
||||
global.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test("normalizePickupEligibilityResponse keeps blocked fallback shape stable", () => {
|
||||
const eligibility = normalizePickupEligibilityResponse(
|
||||
{ success: false, message: "Upstream error" },
|
||||
{ pdNo: "1049275", strCd: "10224" }
|
||||
)
|
||||
|
||||
assert.equal(eligibility.pickupEligible, null)
|
||||
assert.equal(eligibility.eligibleStoreCount, null)
|
||||
assert.deepEqual(eligibility.eligibleStores, [])
|
||||
assert.equal(eligibility.matchedStore, null)
|
||||
assert.equal(eligibility.retrievalStatus, "blocked")
|
||||
assert.equal(eligibility.reason, "upstream_error")
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue