@thorprovider/medusa-extended 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/dropshipper.live-smoke.test.ts +685 -0
- package/src/dropshipper.test.ts +272 -0
- package/src/dropshipper.ts +41 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dropshipper Order Creation — Live Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Exercises the DropshipperClient against a real Medusa backend to verify
|
|
5
|
+
* that orders are created correctly based on payment method, address,
|
|
6
|
+
* shipping options, and geozone parameters.
|
|
7
|
+
*
|
|
8
|
+
* Guarded by DROPSHIPPER_SMOKE_TEST=1 environment variable.
|
|
9
|
+
*
|
|
10
|
+
* Environment variables (all required when DROPSHIPPER_SMOKE_TEST=1):
|
|
11
|
+
* MEDUSA_BACKEND_URL - Backend URL (e.g. https://dev.erpthor.com)
|
|
12
|
+
* TEST_EMAIL - Dropshipper admin email
|
|
13
|
+
* TEST_PASSWORD - Dropshipper admin password
|
|
14
|
+
*
|
|
15
|
+
* These tests CREATE real orders on the backend and then CANCEL them.
|
|
16
|
+
* Run against a staging/test environment, NOT production.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
|
20
|
+
import { server } from '../stelorder/client.test-helpers'
|
|
21
|
+
import { DropshipperClient } from './dropshipper'
|
|
22
|
+
import type {
|
|
23
|
+
ExtendedCreateDropshipperOrderBody,
|
|
24
|
+
GetDropshipperProductsResponse,
|
|
25
|
+
GetDropshipperCustomersResponse,
|
|
26
|
+
GetDropshipperShippingOptionsResponse,
|
|
27
|
+
GetDropshipperAccountResponse,
|
|
28
|
+
} from '@thorprovider/types'
|
|
29
|
+
|
|
30
|
+
const runLive = process.env.DROPSHIPPER_SMOKE_TEST === '1'
|
|
31
|
+
|
|
32
|
+
function requiredEnv(name: string): string {
|
|
33
|
+
const value = process.env[name]
|
|
34
|
+
if (!value) throw new Error(`Missing required env var: ${name}`)
|
|
35
|
+
return value
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!runLive) {
|
|
39
|
+
describe('Dropshipper order creation live smoke', () => {
|
|
40
|
+
it.skip('disabled unless DROPSHIPPER_SMOKE_TEST=1', () => {})
|
|
41
|
+
})
|
|
42
|
+
} else {
|
|
43
|
+
const baseUrl = requiredEnv('MEDUSA_BACKEND_URL')
|
|
44
|
+
const email = requiredEnv('TEST_EMAIL')
|
|
45
|
+
const password = requiredEnv('TEST_PASSWORD')
|
|
46
|
+
|
|
47
|
+
// --------------------------------------------------------------------------
|
|
48
|
+
// Helpers
|
|
49
|
+
// --------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
let jwtToken: string
|
|
52
|
+
let client: DropshipperClient
|
|
53
|
+
|
|
54
|
+
/** Login and obtain a JWT token for the dropshipper */
|
|
55
|
+
const login = async (): Promise<string> => {
|
|
56
|
+
const res = await fetch(`${baseUrl}/auth/user/emailpass`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
|
59
|
+
body: JSON.stringify({ email, password }),
|
|
60
|
+
})
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const text = await res.text()
|
|
63
|
+
throw new Error(`Login failed (${res.status}): ${text}`)
|
|
64
|
+
}
|
|
65
|
+
const data = (await res.json()) as { token: string }
|
|
66
|
+
if (!data.token) throw new Error('Login succeeded but no token returned')
|
|
67
|
+
return data.token
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Cancel a test order to clean up */
|
|
71
|
+
const cancelOrder = async (orderId: string): Promise<void> => {
|
|
72
|
+
try {
|
|
73
|
+
await client.cancelOrder(orderId)
|
|
74
|
+
} catch {
|
|
75
|
+
// Best-effort cleanup — ignore failures
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
beforeAll(async () => {
|
|
80
|
+
// Stop MSW so real HTTP requests reach the backend
|
|
81
|
+
server.close()
|
|
82
|
+
jwtToken = await login()
|
|
83
|
+
client = new DropshipperClient({ baseUrl, token: jwtToken })
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
afterAll(() => {
|
|
87
|
+
// Restore MSW for other tests
|
|
88
|
+
server.listen({ onUnhandledRequest: 'error' })
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// --------------------------------------------------------------------------
|
|
92
|
+
// Test Data Discovery
|
|
93
|
+
// --------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
describe('Test data discovery', () => {
|
|
96
|
+
let products: GetDropshipperProductsResponse['products']
|
|
97
|
+
let customers: GetDropshipperCustomersResponse['customers']
|
|
98
|
+
let shippingOptions: GetDropshipperShippingOptionsResponse['shipping_options']
|
|
99
|
+
let account: GetDropshipperAccountResponse['account']
|
|
100
|
+
|
|
101
|
+
it('fetches products to find valid variant IDs', async () => {
|
|
102
|
+
const res = await client.getProducts({ limit: 5 })
|
|
103
|
+
expect(res.products.length).toBeGreaterThan(0)
|
|
104
|
+
products = res.products
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('fetches customers to find a valid customer ID', async () => {
|
|
108
|
+
const res = await client.getCustomers({ limit: 5 })
|
|
109
|
+
expect(res.customers.length).toBeGreaterThan(0)
|
|
110
|
+
customers = res.customers
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('fetches account to find payment method configs', async () => {
|
|
114
|
+
const res = await client.getAccount()
|
|
115
|
+
expect(res.account.payment_method_configs?.length).toBeGreaterThan(0)
|
|
116
|
+
account = res.account
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('fetches shipping options for the account currency', async () => {
|
|
120
|
+
const res = await client.getOrderShippingOptions({
|
|
121
|
+
currency_code: account.currency_code,
|
|
122
|
+
})
|
|
123
|
+
expect(res.shipping_options.length).toBeGreaterThan(0)
|
|
124
|
+
shippingOptions = res.shipping_options
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('fetches shipping options with country code (geozone filter)', async () => {
|
|
128
|
+
// Use the customer's default shipping address country if available
|
|
129
|
+
const customerDetail = await client.getCustomer(customers[0].id)
|
|
130
|
+
const addr = customerDetail.customer?.default_shipping_address
|
|
131
|
+
if (!addr?.country_code) {
|
|
132
|
+
console.log('Skipping: customer has no default shipping address with country_code')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const res = await client.getOrderShippingOptions({
|
|
137
|
+
currency_code: account.currency_code,
|
|
138
|
+
country_code: addr.country_code,
|
|
139
|
+
province: addr.province ?? undefined,
|
|
140
|
+
city: addr.city ?? undefined,
|
|
141
|
+
})
|
|
142
|
+
expect(res.shipping_options).toBeInstanceOf(Array)
|
|
143
|
+
console.log(
|
|
144
|
+
`Shipping options for ${addr.country_code}/${addr.province ?? 'any'}/${addr.city ?? 'any'}: ${res.shipping_options.length} found`,
|
|
145
|
+
)
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// --------------------------------------------------------------------------
|
|
150
|
+
// Order Creation
|
|
151
|
+
// --------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
describe('Order creation', () => {
|
|
154
|
+
let products: GetDropshipperProductsResponse['products']
|
|
155
|
+
let customers: GetDropshipperCustomersResponse['customers']
|
|
156
|
+
let account: GetDropshipperAccountResponse['account']
|
|
157
|
+
let shippingOptions: GetDropshipperShippingOptionsResponse['shipping_options']
|
|
158
|
+
let createdOrderIds: string[] = []
|
|
159
|
+
|
|
160
|
+
beforeAll(async () => {
|
|
161
|
+
const [productsRes, customersRes, accountRes] = await Promise.all([
|
|
162
|
+
client.getProducts({ limit: 5 }),
|
|
163
|
+
client.getCustomers({ limit: 5 }),
|
|
164
|
+
client.getAccount(),
|
|
165
|
+
])
|
|
166
|
+
products = productsRes.products
|
|
167
|
+
customers = customersRes.customers
|
|
168
|
+
account = accountRes.account
|
|
169
|
+
|
|
170
|
+
const shippingRes = await client.getOrderShippingOptions({
|
|
171
|
+
currency_code: account.currency_code,
|
|
172
|
+
})
|
|
173
|
+
shippingOptions = shippingRes.shipping_options
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
afterAll(async () => {
|
|
177
|
+
// Clean up all created orders
|
|
178
|
+
for (const orderId of createdOrderIds) {
|
|
179
|
+
await cancelOrder(orderId)
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
function getFirstVariantId(): string {
|
|
184
|
+
for (const product of products) {
|
|
185
|
+
if (product.variants && product.variants.length > 0) {
|
|
186
|
+
return product.variants[0].id
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
throw new Error('No products with variants found')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function buildOrderBody(overrides?: Partial<ExtendedCreateDropshipperOrderBody>): ExtendedCreateDropshipperOrderBody {
|
|
193
|
+
const variantId = getFirstVariantId()
|
|
194
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
195
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
customer_id: customers[0].id,
|
|
199
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
200
|
+
items: [{ variant_id: variantId, quantity: 1 }],
|
|
201
|
+
shipping_address: {
|
|
202
|
+
full_name: `${customers[0].first_name ?? 'Test'} ${customers[0].last_name ?? 'User'}`,
|
|
203
|
+
address_1: '123 Test Street',
|
|
204
|
+
city: 'Test City',
|
|
205
|
+
province: '',
|
|
206
|
+
postal_code: '00000',
|
|
207
|
+
country_code: 'mx',
|
|
208
|
+
phone: undefined,
|
|
209
|
+
},
|
|
210
|
+
payment_method_id: paymentMethodId,
|
|
211
|
+
...overrides,
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
it('creates an order with default settings', async () => {
|
|
216
|
+
const body = buildOrderBody()
|
|
217
|
+
const res = await client.createOrder(body)
|
|
218
|
+
expect(res.order).toBeDefined()
|
|
219
|
+
expect(res.order.id).toBeTruthy()
|
|
220
|
+
expect(res.order.display_id).toBeGreaterThan(0)
|
|
221
|
+
expect(res.order.status).toBe('pending')
|
|
222
|
+
expect(res.order.total).toBeGreaterThan(0)
|
|
223
|
+
expect(res.order.created_at).toBeTruthy()
|
|
224
|
+
|
|
225
|
+
createdOrderIds.push(res.order.id)
|
|
226
|
+
console.log(`Created order ${res.order.display_id} (${res.order.id}) — total: ${res.order.total}`)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('creates an order with shipping_method_id', async () => {
|
|
230
|
+
if (shippingOptions.length === 0) {
|
|
231
|
+
console.log('Skipping: no shipping options available')
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const body = buildOrderBody({ shipping_method_id: shippingOptions[0].id })
|
|
236
|
+
const res = await client.createOrder(body)
|
|
237
|
+
expect(res.order.id).toBeTruthy()
|
|
238
|
+
expect(res.order.status).toBe('pending')
|
|
239
|
+
|
|
240
|
+
createdOrderIds.push(res.order.id)
|
|
241
|
+
console.log(`Created order with shipping method: ${shippingOptions[0].name}`)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('creates an order with billing_address', async () => {
|
|
245
|
+
const body = buildOrderBody({
|
|
246
|
+
billing_address: {
|
|
247
|
+
full_name: 'Billing User',
|
|
248
|
+
address_1: '456 Billing Ave',
|
|
249
|
+
city: 'Billing City',
|
|
250
|
+
province: '',
|
|
251
|
+
postal_code: '11111',
|
|
252
|
+
country_code: 'mx',
|
|
253
|
+
phone: undefined,
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
const res = await client.createOrder(body)
|
|
257
|
+
expect(res.order.id).toBeTruthy()
|
|
258
|
+
|
|
259
|
+
createdOrderIds.push(res.order.id)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('creates an order with province and city in shipping address (geozone match)', async () => {
|
|
263
|
+
const body = buildOrderBody({
|
|
264
|
+
shipping_address: {
|
|
265
|
+
full_name: `${customers[0].first_name ?? 'Test'} ${customers[0].last_name ?? 'User'}`,
|
|
266
|
+
address_1: 'Av. Revolucion 123',
|
|
267
|
+
city: 'Monterrey',
|
|
268
|
+
province: 'Nuevo Leon',
|
|
269
|
+
postal_code: '64000',
|
|
270
|
+
country_code: 'mx',
|
|
271
|
+
phone: undefined,
|
|
272
|
+
},
|
|
273
|
+
})
|
|
274
|
+
const res = await client.createOrder(body)
|
|
275
|
+
expect(res.order.id).toBeTruthy()
|
|
276
|
+
|
|
277
|
+
createdOrderIds.push(res.order.id)
|
|
278
|
+
console.log(`Created order with geozone address: Monterrey, Nuevo Leon`)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('creates an order with dropshipper-collected payment method', async () => {
|
|
282
|
+
const dropshipperPayment = account.payment_method_configs?.find(
|
|
283
|
+
(c) => c.is_enabled && c.collected_by === 'dropshipper',
|
|
284
|
+
)
|
|
285
|
+
if (!dropshipperPayment) {
|
|
286
|
+
console.log('Skipping: no dropshipper-collected payment method available')
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const body = buildOrderBody({ payment_method_id: dropshipperPayment.payment_provider_id })
|
|
291
|
+
const res = await client.createOrder(body)
|
|
292
|
+
expect(res.order.id).toBeTruthy()
|
|
293
|
+
|
|
294
|
+
createdOrderIds.push(res.order.id)
|
|
295
|
+
console.log(`Created order with dropshipper-collected payment: ${dropshipperPayment.label}`)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('creates an order with provider-collected payment method', async () => {
|
|
299
|
+
const providerPayment = account.payment_method_configs?.find(
|
|
300
|
+
(c) => c.is_enabled && c.collected_by === 'provider',
|
|
301
|
+
)
|
|
302
|
+
if (!providerPayment) {
|
|
303
|
+
console.log('Skipping: no provider-collected payment method available')
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const body = buildOrderBody({ payment_method_id: providerPayment.payment_provider_id })
|
|
308
|
+
const res = await client.createOrder(body)
|
|
309
|
+
expect(res.order.id).toBeTruthy()
|
|
310
|
+
|
|
311
|
+
createdOrderIds.push(res.order.id)
|
|
312
|
+
console.log(`Created order with provider-collected payment: ${providerPayment.label}`)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('creates an order with promo_codes', async () => {
|
|
316
|
+
// Try to find a valid promotion first
|
|
317
|
+
let promoCode: string | undefined
|
|
318
|
+
try {
|
|
319
|
+
const promos = await client.getPromotions({ limit: 1 })
|
|
320
|
+
if (promos.promotions && promos.promotions.length > 0) {
|
|
321
|
+
promoCode = promos.promotions[0].code
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
// Ignore — promotions may not exist
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!promoCode) {
|
|
328
|
+
console.log('Skipping: no promotions available')
|
|
329
|
+
return
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const body = buildOrderBody({ promo_codes: [promoCode] })
|
|
333
|
+
const res = await client.createOrder(body)
|
|
334
|
+
expect(res.order.id).toBeTruthy()
|
|
335
|
+
|
|
336
|
+
createdOrderIds.push(res.order.id)
|
|
337
|
+
console.log(`Created order with promo code: ${promoCode}`)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('creates an order with multiple items', async () => {
|
|
341
|
+
const variantIds: string[] = []
|
|
342
|
+
for (const product of products) {
|
|
343
|
+
if (product.variants && product.variants.length > 0) {
|
|
344
|
+
variantIds.push(product.variants[0].id)
|
|
345
|
+
if (variantIds.length >= 2) break
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (variantIds.length < 2) {
|
|
350
|
+
console.log('Skipping: need at least 2 variants for multi-item test')
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
355
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
356
|
+
|
|
357
|
+
const body: ExtendedCreateDropshipperOrderBody = {
|
|
358
|
+
customer_id: customers[0].id,
|
|
359
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
360
|
+
items: [
|
|
361
|
+
{ variant_id: variantIds[0], quantity: 2 },
|
|
362
|
+
{ variant_id: variantIds[1], quantity: 1 },
|
|
363
|
+
],
|
|
364
|
+
shipping_address: {
|
|
365
|
+
full_name: `${customers[0].first_name ?? 'Test'} ${customers[0].last_name ?? 'User'}`,
|
|
366
|
+
address_1: '789 Multi Item St',
|
|
367
|
+
city: 'Multi City',
|
|
368
|
+
province: '',
|
|
369
|
+
postal_code: '22222',
|
|
370
|
+
country_code: 'mx',
|
|
371
|
+
phone: undefined,
|
|
372
|
+
},
|
|
373
|
+
payment_method_id: paymentMethodId,
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const res = await client.createOrder(body)
|
|
377
|
+
expect(res.order.id).toBeTruthy()
|
|
378
|
+
expect(res.order.total).toBeGreaterThan(0)
|
|
379
|
+
|
|
380
|
+
createdOrderIds.push(res.order.id)
|
|
381
|
+
console.log(`Created multi-item order: ${res.order.display_id} — total: ${res.order.total}`)
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// --------------------------------------------------------------------------
|
|
386
|
+
// Order Verification
|
|
387
|
+
// --------------------------------------------------------------------------
|
|
388
|
+
|
|
389
|
+
describe('Order verification', () => {
|
|
390
|
+
let products: GetDropshipperProductsResponse['products']
|
|
391
|
+
let customers: GetDropshipperCustomersResponse['customers']
|
|
392
|
+
let account: GetDropshipperAccountResponse['account']
|
|
393
|
+
let createdOrderId: string
|
|
394
|
+
|
|
395
|
+
beforeAll(async () => {
|
|
396
|
+
const [productsRes, customersRes, accountRes] = await Promise.all([
|
|
397
|
+
client.getProducts({ limit: 5 }),
|
|
398
|
+
client.getCustomers({ limit: 5 }),
|
|
399
|
+
client.getAccount(),
|
|
400
|
+
])
|
|
401
|
+
products = productsRes.products
|
|
402
|
+
customers = customersRes.customers
|
|
403
|
+
account = accountRes.account
|
|
404
|
+
|
|
405
|
+
// Create a single order to verify
|
|
406
|
+
const variantId = (() => {
|
|
407
|
+
for (const product of products) {
|
|
408
|
+
if (product.variants && product.variants.length > 0) {
|
|
409
|
+
return product.variants[0].id
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
throw new Error('No products with variants found')
|
|
413
|
+
})()
|
|
414
|
+
|
|
415
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
416
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
417
|
+
|
|
418
|
+
const res = await client.createOrder({
|
|
419
|
+
customer_id: customers[0].id,
|
|
420
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
421
|
+
items: [{ variant_id: variantId, quantity: 1 }],
|
|
422
|
+
shipping_address: {
|
|
423
|
+
full_name: `${customers[0].first_name ?? 'Test'} ${customers[0].last_name ?? 'User'}`,
|
|
424
|
+
address_1: '123 Verification St',
|
|
425
|
+
city: 'Verification City',
|
|
426
|
+
province: '',
|
|
427
|
+
postal_code: '33333',
|
|
428
|
+
country_code: 'mx',
|
|
429
|
+
phone: undefined,
|
|
430
|
+
},
|
|
431
|
+
payment_method_id: paymentMethodId,
|
|
432
|
+
})
|
|
433
|
+
createdOrderId = res.order.id
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
afterAll(async () => {
|
|
437
|
+
if (createdOrderId) {
|
|
438
|
+
await cancelOrder(createdOrderId)
|
|
439
|
+
}
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
it('retrieves the created order by ID', async () => {
|
|
443
|
+
const res = await client.getOrder(createdOrderId)
|
|
444
|
+
expect(res.order.id).toBe(createdOrderId)
|
|
445
|
+
expect(res.order.display_id).toBeGreaterThan(0)
|
|
446
|
+
expect(res.order.status).toBe('pending')
|
|
447
|
+
expect(res.order.totals).toBeDefined()
|
|
448
|
+
expect(res.order.totals.total).toBeGreaterThan(0)
|
|
449
|
+
console.log(
|
|
450
|
+
`Verified order ${res.order.display_id}: subtotal=${res.order.totals.subtotal}, total=${res.order.totals.total}, profit=${res.order.totals.profit}`,
|
|
451
|
+
)
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
it('includes the order in the orders list', async () => {
|
|
455
|
+
const res = await client.getOrders({ limit: 20 })
|
|
456
|
+
const found = res.orders.find((o) => o.id === createdOrderId)
|
|
457
|
+
expect(found).toBeDefined()
|
|
458
|
+
expect(found!.status).toBe('pending')
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('cancels the order successfully', async () => {
|
|
462
|
+
const res = await client.cancelOrder(createdOrderId)
|
|
463
|
+
expect(res.order.status).toBeDefined()
|
|
464
|
+
|
|
465
|
+
// Verify it's cancelled
|
|
466
|
+
const detail = await client.getOrder(createdOrderId)
|
|
467
|
+
expect(detail.order.status).toBe('canceled')
|
|
468
|
+
console.log(`Order ${createdOrderId} cancelled successfully`)
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// --------------------------------------------------------------------------
|
|
473
|
+
// Error Handling
|
|
474
|
+
// --------------------------------------------------------------------------
|
|
475
|
+
|
|
476
|
+
describe('Error handling', () => {
|
|
477
|
+
let products: GetDropshipperProductsResponse['products']
|
|
478
|
+
let customers: GetDropshipperCustomersResponse['customers']
|
|
479
|
+
let account: GetDropshipperAccountResponse['account']
|
|
480
|
+
|
|
481
|
+
beforeAll(async () => {
|
|
482
|
+
const [productsRes, customersRes, accountRes] = await Promise.all([
|
|
483
|
+
client.getProducts({ limit: 5 }),
|
|
484
|
+
client.getCustomers({ limit: 5 }),
|
|
485
|
+
client.getAccount(),
|
|
486
|
+
])
|
|
487
|
+
products = productsRes.products
|
|
488
|
+
customers = customersRes.customers
|
|
489
|
+
account = accountRes.account
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
it('rejects order creation with invalid variant ID', async () => {
|
|
493
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
494
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
495
|
+
|
|
496
|
+
await expect(
|
|
497
|
+
client.createOrder({
|
|
498
|
+
customer_id: customers[0].id,
|
|
499
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
500
|
+
items: [{ variant_id: 'variant_does_not_exist_xyz', quantity: 1 }],
|
|
501
|
+
shipping_address: {
|
|
502
|
+
full_name: 'Test User',
|
|
503
|
+
address_1: '123 Test St',
|
|
504
|
+
city: 'Test City',
|
|
505
|
+
province: '',
|
|
506
|
+
postal_code: '00000',
|
|
507
|
+
country_code: 'mx',
|
|
508
|
+
},
|
|
509
|
+
payment_method_id: paymentMethodId,
|
|
510
|
+
}),
|
|
511
|
+
).rejects.toThrow()
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
it('rejects order creation with empty items array', async () => {
|
|
515
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
516
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
517
|
+
|
|
518
|
+
await expect(
|
|
519
|
+
client.createOrder({
|
|
520
|
+
customer_id: customers[0].id,
|
|
521
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
522
|
+
items: [],
|
|
523
|
+
shipping_address: {
|
|
524
|
+
full_name: 'Test User',
|
|
525
|
+
address_1: '123 Test St',
|
|
526
|
+
city: 'Test City',
|
|
527
|
+
province: '',
|
|
528
|
+
postal_code: '00000',
|
|
529
|
+
country_code: 'mx',
|
|
530
|
+
},
|
|
531
|
+
payment_method_id: paymentMethodId,
|
|
532
|
+
}),
|
|
533
|
+
).rejects.toThrow()
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
it('rejects order creation with invalid customer ID', async () => {
|
|
537
|
+
const paymentMethodId = account.payment_method_configs?.find((c) => c.is_enabled)?.payment_provider_id
|
|
538
|
+
if (!paymentMethodId) throw new Error('No enabled payment method configs found')
|
|
539
|
+
|
|
540
|
+
const variantId = (() => {
|
|
541
|
+
for (const product of products) {
|
|
542
|
+
if (product.variants && product.variants.length > 0) {
|
|
543
|
+
return product.variants[0].id
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
throw new Error('No products with variants found')
|
|
547
|
+
})()
|
|
548
|
+
|
|
549
|
+
await expect(
|
|
550
|
+
client.createOrder({
|
|
551
|
+
customer_id: 'customer_does_not_exist_xyz',
|
|
552
|
+
currency_code: account.currency_code.toLowerCase(),
|
|
553
|
+
items: [{ variant_id: variantId, quantity: 1 }],
|
|
554
|
+
shipping_address: {
|
|
555
|
+
full_name: 'Test User',
|
|
556
|
+
address_1: '123 Test St',
|
|
557
|
+
city: 'Test City',
|
|
558
|
+
province: '',
|
|
559
|
+
postal_code: '00000',
|
|
560
|
+
country_code: 'mx',
|
|
561
|
+
},
|
|
562
|
+
payment_method_id: paymentMethodId,
|
|
563
|
+
}),
|
|
564
|
+
).rejects.toThrow()
|
|
565
|
+
})
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
// --------------------------------------------------------------------------
|
|
569
|
+
// Shipping Options with Geozone Parameters
|
|
570
|
+
// --------------------------------------------------------------------------
|
|
571
|
+
|
|
572
|
+
describe('Shipping options with geozone parameters', () => {
|
|
573
|
+
let account: GetDropshipperAccountResponse['account']
|
|
574
|
+
|
|
575
|
+
beforeAll(async () => {
|
|
576
|
+
const res = await client.getAccount()
|
|
577
|
+
account = res.account
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
it('returns shipping options with currency_code only', async () => {
|
|
581
|
+
const res = await client.getOrderShippingOptions({
|
|
582
|
+
currency_code: account.currency_code,
|
|
583
|
+
})
|
|
584
|
+
expect(res.shipping_options).toBeInstanceOf(Array)
|
|
585
|
+
console.log(`Shipping options (currency only): ${res.shipping_options.length} found`)
|
|
586
|
+
if (res.shipping_options.length > 0) {
|
|
587
|
+
expect(res.shipping_options[0]).toHaveProperty('id')
|
|
588
|
+
expect(res.shipping_options[0]).toHaveProperty('name')
|
|
589
|
+
expect(res.shipping_options[0]).toHaveProperty('amount')
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
it('returns shipping options filtered by country_code', async () => {
|
|
594
|
+
const res = await client.getOrderShippingOptions({
|
|
595
|
+
currency_code: account.currency_code,
|
|
596
|
+
country_code: 'mx',
|
|
597
|
+
})
|
|
598
|
+
expect(res.shipping_options).toBeInstanceOf(Array)
|
|
599
|
+
console.log(`Shipping options (MX): ${res.shipping_options.length} found`)
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
it('returns shipping options filtered by country_code and province', async () => {
|
|
603
|
+
const res = await client.getOrderShippingOptions({
|
|
604
|
+
currency_code: account.currency_code,
|
|
605
|
+
country_code: 'mx',
|
|
606
|
+
province: 'Nuevo Leon',
|
|
607
|
+
})
|
|
608
|
+
expect(res.shipping_options).toBeInstanceOf(Array)
|
|
609
|
+
console.log(`Shipping options (MX/Nuevo Leon): ${res.shipping_options.length} found`)
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
it('returns shipping options filtered by country_code, province, and city', async () => {
|
|
613
|
+
const res = await client.getOrderShippingOptions({
|
|
614
|
+
currency_code: account.currency_code,
|
|
615
|
+
country_code: 'mx',
|
|
616
|
+
province: 'Nuevo Leon',
|
|
617
|
+
city: 'Monterrey',
|
|
618
|
+
})
|
|
619
|
+
expect(res.shipping_options).toBeInstanceOf(Array)
|
|
620
|
+
console.log(`Shipping options (MX/Nuevo Leon/Monterrey): ${res.shipping_options.length} found`)
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
it('returns different results for different countries (geozone differentiation)', async () => {
|
|
624
|
+
const [mxOptions, usOptions] = await Promise.all([
|
|
625
|
+
client.getOrderShippingOptions({
|
|
626
|
+
currency_code: account.currency_code,
|
|
627
|
+
country_code: 'mx',
|
|
628
|
+
}),
|
|
629
|
+
client.getOrderShippingOptions({
|
|
630
|
+
currency_code: account.currency_code,
|
|
631
|
+
country_code: 'us',
|
|
632
|
+
}),
|
|
633
|
+
])
|
|
634
|
+
|
|
635
|
+
console.log(`MX options: ${mxOptions.shipping_options.length}, US options: ${usOptions.shipping_options.length}`)
|
|
636
|
+
|
|
637
|
+
// The options MAY differ by country — log the difference
|
|
638
|
+
const mxNames = mxOptions.shipping_options.map((o) => o.name).join(', ')
|
|
639
|
+
const usNames = usOptions.shipping_options.map((o) => o.name).join(', ')
|
|
640
|
+
console.log(`MX: [${mxNames}]`)
|
|
641
|
+
console.log(`US: [${usNames}]`)
|
|
642
|
+
})
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
// --------------------------------------------------------------------------
|
|
646
|
+
// Payment Methods
|
|
647
|
+
// --------------------------------------------------------------------------
|
|
648
|
+
|
|
649
|
+
describe('Payment methods from account', () => {
|
|
650
|
+
it('returns payment method configs from account', async () => {
|
|
651
|
+
const res = await client.getAccount()
|
|
652
|
+
expect(res.account.payment_method_configs).toBeInstanceOf(Array)
|
|
653
|
+
|
|
654
|
+
const configs = res.account.payment_method_configs!
|
|
655
|
+
console.log(`Payment method configs: ${configs.length} found`)
|
|
656
|
+
|
|
657
|
+
for (const config of configs) {
|
|
658
|
+
console.log(` - ${config.label} (${config.payment_provider_id}): collected_by=${config.collected_by}, enabled=${config.is_enabled}`)
|
|
659
|
+
expect(config).toHaveProperty('payment_provider_id')
|
|
660
|
+
expect(config).toHaveProperty('label')
|
|
661
|
+
expect(config).toHaveProperty('collected_by')
|
|
662
|
+
expect(config).toHaveProperty('is_enabled')
|
|
663
|
+
expect(['dropshipper', 'provider']).toContain(config.collected_by)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const enabled = configs.filter((c) => c.is_enabled)
|
|
667
|
+
expect(enabled.length).toBeGreaterThan(0)
|
|
668
|
+
console.log(`Enabled payment methods: ${enabled.length}`)
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
it('has at least one dropshipper-collected or provider-collected method', async () => {
|
|
672
|
+
const res = await client.getAccount()
|
|
673
|
+
const configs = res.account.payment_method_configs ?? []
|
|
674
|
+
|
|
675
|
+
const dropshipperMethods = configs.filter((c) => c.collected_by === 'dropshipper' && c.is_enabled)
|
|
676
|
+
const providerMethods = configs.filter((c) => c.collected_by === 'provider' && c.is_enabled)
|
|
677
|
+
|
|
678
|
+
console.log(`Dropshipper-collected enabled: ${dropshipperMethods.length}`)
|
|
679
|
+
console.log(`Provider-collected enabled: ${providerMethods.length}`)
|
|
680
|
+
|
|
681
|
+
// At least one type should be available
|
|
682
|
+
expect(dropshipperMethods.length + providerMethods.length).toBeGreaterThan(0)
|
|
683
|
+
})
|
|
684
|
+
})
|
|
685
|
+
}
|