@thorprovider/adapters 5.0.0 → 5.0.1

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.
Files changed (47) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +6 -0
  3. package/README.md +53 -631
  4. package/dist/index.d.mts +406 -194
  5. package/dist/index.d.ts +406 -194
  6. package/dist/index.js +3092 -604
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +3081 -602
  9. package/dist/index.mjs.map +1 -1
  10. package/docs/RETRY_SOLUTION_SUMMARY.md +1 -1
  11. package/docs/authentication.md +0 -26
  12. package/docs/capabilities-system.md +66 -36
  13. package/docs/getting-started.md +1 -1
  14. package/docs/providers-overview.md +16 -13
  15. package/docs/update-documentation.md +10 -8
  16. package/package.json +6 -5
  17. package/quick-reference.md +0 -33
  18. package/src/backend/factory.ts +11 -1
  19. package/src/factory/index.ts +52 -74
  20. package/src/index.ts +17 -3
  21. package/src/providers/bigcommerce/bigcommerce.ts +6 -0
  22. package/src/providers/bigcommerce/index.ts +315 -0
  23. package/src/providers/bigcommerce/transforms.ts +227 -0
  24. package/src/providers/index.ts +16 -1
  25. package/src/providers/magento/index.ts +387 -0
  26. package/src/providers/magento/magento.ts +5 -0
  27. package/src/providers/magento/queries.ts +267 -0
  28. package/src/providers/magento/transforms.ts +249 -0
  29. package/src/providers/medusa/auth.test.ts +94 -92
  30. package/src/providers/medusa/auth.ts +72 -123
  31. package/src/providers/medusa/index.test.ts +392 -0
  32. package/src/providers/medusa/index.ts +1 -21
  33. package/src/providers/medusa/live-smoke.test.ts +412 -0
  34. package/src/providers/shopify/auth.ts +402 -0
  35. package/src/providers/shopify/index.ts +444 -0
  36. package/src/providers/shopify/mutations.ts +247 -0
  37. package/src/providers/shopify/queries.ts +324 -0
  38. package/src/providers/shopify/shopify.ts +7 -0
  39. package/src/providers/shopify/transforms.ts +275 -0
  40. package/src/providers/spree/index.ts +344 -0
  41. package/src/providers/spree/spree.ts +5 -0
  42. package/src/providers/spree/transforms.ts +254 -0
  43. package/src/providers/woocommerce/index.ts +310 -0
  44. package/src/providers/woocommerce/transforms.ts +254 -0
  45. package/src/providers/woocommerce/woocommerce.ts +5 -0
  46. package/src/types/capabilities.types.ts +250 -0
  47. package/vitest.live.config.ts +18 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Adobe Commerce (Magento) Transform Functions
3
+ * Converts Magento GraphQL / REST API responses to @thorprovider/types
4
+ *
5
+ * Magento returns price as { regularPrice: { amount: { value, currency } } }
6
+ * Images have url + label directly.
7
+ */
8
+
9
+ import type { Product, ProductVariant, ProductOption, Image, Collection, Cart, CartItem, Order, OrderItem, Customer, Address, Money } from '@thorprovider/types';
10
+
11
+ // ============================================
12
+ // Money / Price Helpers
13
+ // ============================================
14
+
15
+ function toMoney(value: number | undefined, currencyCode: string): Money {
16
+ return { amount: (value ?? 0).toFixed(2), currencyCode };
17
+ }
18
+
19
+ function extractPrice(priceRange: any, currencyCode: string): { min: Money; max: Money } {
20
+ const minPrice = priceRange?.minimum_price?.regular_price?.value ?? priceRange?.minimum_price?.final_price?.value ?? 0;
21
+ const maxPrice = priceRange?.maximum_price?.regular_price?.value ?? priceRange?.maximum_price?.final_price?.value ?? minPrice;
22
+ const currency = priceRange?.minimum_price?.regular_price?.currency ?? currencyCode;
23
+ return {
24
+ min: toMoney(minPrice, currency),
25
+ max: toMoney(maxPrice, currency),
26
+ };
27
+ }
28
+
29
+ // ============================================
30
+ // Product Transforms
31
+ // ============================================
32
+
33
+ export function transformProduct(data: any, currencyCode: string = 'USD'): Product {
34
+ const id = String(data.id ?? data.uid ?? '');
35
+ const { min, max } = extractPrice(data.price_range, currencyCode);
36
+
37
+ // Images
38
+ const images: Image[] = (data.media_gallery ?? data.media_gallery_entries ?? []).map((img: any) => ({
39
+ url: img.url ?? (img.file ? `${img.file}` : ''),
40
+ altText: img.label ?? data.name ?? '',
41
+ width: undefined,
42
+ height: undefined,
43
+ }));
44
+
45
+ // Small image fallback
46
+ if (images.length === 0 && data.small_image?.url) {
47
+ images.push({ url: data.small_image.url, altText: data.small_image.label ?? data.name ?? '' });
48
+ }
49
+ if (images.length === 0 && data.image?.url) {
50
+ images.push({ url: data.image.url, altText: data.image.label ?? data.name ?? '' });
51
+ }
52
+
53
+ // Variants from configurable_options / variants
54
+ const variants: ProductVariant[] = [];
55
+ if (data.variants && Array.isArray(data.variants)) {
56
+ for (const v of data.variants) {
57
+ const product = v.product ?? {};
58
+ const vPrice = product.price_range?.minimum_price?.final_price ?? {};
59
+ variants.push({
60
+ id: String(product.id ?? product.uid ?? product.sku ?? ''),
61
+ title: product.name ?? product.sku ?? '',
62
+ availableForSale: product.stock_status === 'IN_STOCK',
63
+ price: toMoney(vPrice.value ?? min.amount, vPrice.currency ?? currencyCode),
64
+ compareAtPrice: undefined,
65
+ selectedOptions: (v.attributes ?? []).map((attr: any) => ({
66
+ name: attr.label ?? '',
67
+ value: attr.value_index != null ? String(attr.value_index) : '',
68
+ })),
69
+ });
70
+ }
71
+ }
72
+
73
+ // Default variant if simple product
74
+ if (variants.length === 0) {
75
+ variants.push({
76
+ id: data.sku ?? id,
77
+ title: data.name ?? '',
78
+ availableForSale: data.stock_status === 'IN_STOCK',
79
+ price: min,
80
+ compareAtPrice: undefined,
81
+ selectedOptions: [],
82
+ });
83
+ }
84
+
85
+ // Options
86
+ const options: ProductOption[] = (data.configurable_options ?? []).map((opt: any) => ({
87
+ id: String(opt.attribute_id ?? opt.attribute_code ?? ''),
88
+ name: opt.label ?? opt.attribute_code ?? '',
89
+ values: (opt.values ?? []).map((v: any) => v.label ?? v.store_label ?? String(v.value_index ?? '')),
90
+ }));
91
+
92
+ return {
93
+ id,
94
+ handle: data.url_key ?? data.sku ?? id,
95
+ title: data.name ?? '',
96
+ description: data.short_description?.html ?? data.description?.html ?? '',
97
+ descriptionHtml: data.description?.html ?? '',
98
+ availableForSale: data.stock_status === 'IN_STOCK',
99
+ priceRange: { minVariantPrice: min, maxVariantPrice: max },
100
+ variants,
101
+ options,
102
+ images,
103
+ featuredImage: images[0] ?? { url: '', altText: data.name ?? '' },
104
+ tags: [],
105
+ seo: {
106
+ title: data.meta_title ?? data.name ?? '',
107
+ description: data.meta_description ?? '',
108
+ },
109
+ updatedAt: data.updated_at ?? new Date().toISOString(),
110
+ createdAt: data.created_at ?? new Date().toISOString(),
111
+ };
112
+ }
113
+
114
+ // ============================================
115
+ // Collection Transforms (Magento Categories)
116
+ // ============================================
117
+
118
+ export function transformCollection(data: any): Collection {
119
+ return {
120
+ id: String(data.id ?? data.uid ?? ''),
121
+ handle: data.url_key ?? data.url_path ?? String(data.id ?? data.uid ?? ''),
122
+ title: data.name ?? '',
123
+ description: data.description ?? '',
124
+ seo: {
125
+ title: data.meta_title ?? data.name ?? '',
126
+ description: data.meta_description ?? '',
127
+ },
128
+ updatedAt: data.updated_at ?? new Date().toISOString(),
129
+ path: `/collections/${data.url_key ?? data.url_path ?? data.id}`,
130
+ };
131
+ }
132
+
133
+ // ============================================
134
+ // Cart Transforms (Magento Cart / Quote)
135
+ // ============================================
136
+
137
+ export function transformCart(data: any, currencyCode: string = 'USD'): Cart {
138
+ const items: CartItem[] = (data.items ?? []).map((item: any): CartItem => {
139
+ const product = item.product ?? {};
140
+ const priceVal = item.prices?.price?.value ?? product.price_range?.minimum_price?.final_price?.value ?? 0;
141
+ const totalVal = item.prices?.row_total?.value ?? priceVal * (item.quantity ?? 1);
142
+
143
+ return {
144
+ id: String(item.id ?? item.uid ?? ''),
145
+ quantity: item.quantity ?? 1,
146
+ cost: {
147
+ totalAmount: toMoney(totalVal, item.prices?.price?.currency ?? currencyCode),
148
+ },
149
+ merchandise: {
150
+ id: String(product.sku ?? item.id ?? ''),
151
+ title: product.name ?? item.product?.name ?? '',
152
+ selectedOptions: (item.configurable_options ?? []).map((opt: any) => ({
153
+ name: opt.option_label ?? '',
154
+ value: opt.value_label ?? '',
155
+ })),
156
+ product: {
157
+ id: String(product.id ?? product.uid ?? ''),
158
+ handle: product.url_key ?? '',
159
+ title: product.name ?? '',
160
+ featuredImage: {
161
+ url: product.small_image?.url ?? product.thumbnail?.url ?? '',
162
+ altText: product.name ?? '',
163
+ },
164
+ },
165
+ },
166
+ };
167
+ });
168
+
169
+ const totalVal = data.prices?.grand_total?.value ?? 0;
170
+ const subtotalVal = data.prices?.subtotal_excluding_tax?.value ?? data.prices?.subtotal_including_tax?.value ?? totalVal;
171
+ const taxVal = (data.prices?.applied_taxes ?? []).reduce((sum: number, t: any) => sum + (t.amount?.value ?? 0), 0);
172
+
173
+ return {
174
+ id: String(data.id ?? ''),
175
+ checkoutUrl: '',
176
+ totalQuantity: items.reduce((sum, i) => sum + i.quantity, 0),
177
+ cost: {
178
+ subtotalAmount: toMoney(subtotalVal, data.prices?.grand_total?.currency ?? currencyCode),
179
+ totalAmount: toMoney(totalVal, data.prices?.grand_total?.currency ?? currencyCode),
180
+ totalTaxAmount: toMoney(taxVal, currencyCode),
181
+ },
182
+ lines: items,
183
+ };
184
+ }
185
+
186
+ // ============================================
187
+ // Customer Transforms
188
+ // ============================================
189
+
190
+ export function transformCustomer(data: any): Customer {
191
+ return {
192
+ id: String(data.id ?? ''),
193
+ email: data.email ?? '',
194
+ firstName: data.firstname ?? data.first_name ?? '',
195
+ lastName: data.lastname ?? data.last_name ?? '',
196
+ phone: data.phone ?? '',
197
+ hasAccount: true,
198
+ };
199
+ }
200
+
201
+ function transformAddress(addr: any): Address {
202
+ return {
203
+ id: String(addr.id ?? ''),
204
+ firstName: addr.firstname ?? '',
205
+ lastName: addr.lastname ?? '',
206
+ company: addr.company ?? '',
207
+ address1: Array.isArray(addr.street) ? addr.street[0] ?? '' : addr.street ?? '',
208
+ address2: Array.isArray(addr.street) ? addr.street[1] ?? '' : '',
209
+ city: addr.city ?? '',
210
+ province: addr.region?.region ?? '',
211
+ postalCode: addr.postcode ?? '',
212
+ countryCode: addr.country_code ?? addr.country_id ?? '',
213
+ phone: addr.telephone ?? '',
214
+ };
215
+ }
216
+
217
+ // ============================================
218
+ // Order Transforms
219
+ // ============================================
220
+
221
+ export function transformOrder(data: any, currencyCode: string = 'USD'): Order {
222
+ const items: OrderItem[] = (data.items ?? []).map((item: any) => ({
223
+ id: String(item.id ?? ''),
224
+ title: item.product_name ?? '',
225
+ quantity: item.quantity_ordered ?? item.quantity ?? 1,
226
+ price: toMoney(item.product_sale_price?.value ?? item.price ?? 0, currencyCode),
227
+ total: toMoney((item.product_sale_price?.value ?? item.price ?? 0) * (item.quantity_ordered ?? item.quantity ?? 1), currencyCode),
228
+ sku: item.product_sku ?? item.sku,
229
+ productId: String(item.product_id ?? ''),
230
+ }));
231
+
232
+ return {
233
+ id: String(data.id ?? data.order_number ?? ''),
234
+ orderNumber: data.order_number ?? data.number ?? String(data.id ?? ''),
235
+ email: data.email ?? '',
236
+ status: (data.status === 'complete' ? 'completed' : data.status === 'canceled' ? 'cancelled' : data.status === 'closed' ? 'refunded' : 'pending') as any,
237
+ paymentStatus: (data.payment_status === 'paid' ? 'paid' : 'pending') as any,
238
+ fulfillmentStatus: (data.shipment_status === 'shipped' ? 'fulfilled' : 'unfulfilled') as any,
239
+ items,
240
+ total: toMoney(data.total?.grand_total?.value ?? data.grand_total ?? 0, currencyCode),
241
+ subtotal: toMoney(data.total?.subtotal?.value ?? data.subtotal ?? 0, currencyCode),
242
+ tax: toMoney(data.total?.total_tax?.value ?? data.tax_amount ?? 0, currencyCode),
243
+ shipping: toMoney(data.total?.total_shipping?.value ?? data.shipping_amount ?? 0, currencyCode),
244
+ shippingAddress: data.shipping_address ? transformAddress(data.shipping_address) : undefined,
245
+ billingAddress: data.billing_address ? transformAddress(data.billing_address) : undefined,
246
+ createdAt: data.order_date ?? data.created_at ?? new Date().toISOString(),
247
+ updatedAt: data.updated_at ?? data.order_date ?? new Date().toISOString(),
248
+ };
249
+ }
@@ -1,45 +1,46 @@
1
1
  /**
2
- * @fileoverview Tests for MedusaAuthProvider.linkCustomerToChannel
3
- *
4
- * Uses msw to intercept fetch, a minimal Medusa SDK stub, and a custom
5
- * storage stub to inject a pre-set JWT token.
2
+ * @fileoverview Tests for MedusaAuthProvider
6
3
  */
7
4
 
8
- import { describe, expect, it } from 'vitest'
9
- import { http, HttpResponse } from 'msw'
10
- import { server } from '../stelorder/client.test-helpers'
5
+ import { describe, expect, it, vi } from 'vitest'
6
+ import Medusa from '@medusajs/js-sdk'
11
7
  import { MedusaAuthProvider } from './auth'
12
8
 
13
9
  const BASE = 'https://medusa.example.com'
14
- const PUB_KEY = 'pk_test_123'
15
- const TOKEN = 'jwt-bearer-token'
16
10
 
17
- // ---------------------------------------------------------------------------
18
- // Minimal SDK stub — we only test the linkCustomerToChannel raw-fetch path,
19
- // so the SDK itself does not need to do anything.
20
- // ---------------------------------------------------------------------------
21
-
22
- function makeSdkStub(): any {
11
+ function makeSdkStub() {
23
12
  return {
24
- auth: {},
25
- store: { customer: {} },
26
- } as any
13
+ auth: {
14
+ login: vi.fn(),
15
+ logout: vi.fn(),
16
+ register: vi.fn(),
17
+ resetPassword: vi.fn(),
18
+ updateProvider: vi.fn(),
19
+ },
20
+ store: {
21
+ customer: {
22
+ retrieve: vi.fn(),
23
+ create: vi.fn(),
24
+ listAddress: vi.fn(),
25
+ createAddress: vi.fn(),
26
+ updateAddress: vi.fn(),
27
+ deleteAddress: vi.fn(),
28
+ update: vi.fn(),
29
+ },
30
+ },
31
+ } as unknown as Medusa
27
32
  }
28
33
 
29
- // ---------------------------------------------------------------------------
30
- // Custom storage stub — provides a pre-set token to the auth provider
31
- // ---------------------------------------------------------------------------
32
-
33
34
  function makeStorageWithToken(token: string | null) {
34
35
  let stored: string | null = token
35
36
  return {
36
- getItem: (_key: string) => Promise.resolve(stored),
37
+ getItem: () => Promise.resolve(stored),
37
38
  setItem: (_key: string, value: string) => { stored = value; return Promise.resolve() },
38
- removeItem: (_key: string) => { stored = null; return Promise.resolve() },
39
+ removeItem: () => { stored = null; return Promise.resolve() },
39
40
  }
40
41
  }
41
42
 
42
- function makeAuthConfig(token: string | null = TOKEN) {
43
+ function makeAuthConfig(token: string | null = 'jwt-bearer-token') {
43
44
  return {
44
45
  method: 'jwt' as const,
45
46
  storage: 'memory' as const,
@@ -47,91 +48,92 @@ function makeAuthConfig(token: string | null = TOKEN) {
47
48
  }
48
49
  }
49
50
 
50
- function makeProvider(token: string | null = TOKEN) {
51
- return new MedusaAuthProvider(makeSdkStub(), makeAuthConfig(token), false, BASE, PUB_KEY)
51
+ function makeProvider(token: string | null = 'jwt-bearer-token') {
52
+ return new MedusaAuthProvider(makeSdkStub(), makeAuthConfig(token), false)
52
53
  }
53
54
 
54
- // ---------------------------------------------------------------------------
55
- // Tests
56
- // ---------------------------------------------------------------------------
57
-
58
- describe('MedusaAuthProvider.linkCustomerToChannel', () => {
59
- it('POST /store/thor/customers/me/channels with Authorization header when token present', async () => {
60
- server.use(
61
- http.post(`${BASE}/store/thor/customers/me/channels`, ({ request }) => {
62
- expect(request.headers.get('x-publishable-api-key')).toBe(PUB_KEY)
63
- expect(request.headers.get('Authorization')).toBe(`Bearer ${TOKEN}`)
64
- return HttpResponse.json({ linked: true })
65
- }),
66
- )
67
-
68
- const result = await makeProvider().linkCustomerToChannel()
69
- expect(result.linked).toBe(true)
70
- })
55
+ describe('MedusaAuthProvider', () => {
56
+ it('stores jwt token and returns customer on login', async () => {
57
+ const sdk = makeSdkStub()
58
+ sdk.auth.login = vi.fn().mockResolvedValue('jwt-bearer-token')
59
+ sdk.store.customer.retrieve = vi.fn().mockResolvedValue({ customer: { id: 'cus_1', email: 'test@example.com' } })
71
60
 
72
- it('POST without Authorization header when no token in storage', async () => {
73
- server.use(
74
- http.post(`${BASE}/store/thor/customers/me/channels`, ({ request }) => {
75
- expect(request.headers.get('Authorization')).toBeNull()
76
- expect(request.headers.get('x-publishable-api-key')).toBe(PUB_KEY)
77
- return HttpResponse.json({ linked: true })
78
- }),
79
- )
80
-
81
- const result = await makeProvider(null).linkCustomerToChannel()
82
- expect(result.linked).toBe(true)
83
- })
61
+ const provider = new MedusaAuthProvider(sdk, makeAuthConfig('jwt-bearer-token'), false)
84
62
 
85
- it('returns { linked: true, reason: "already_linked" } on duplicate link', async () => {
86
- server.use(
87
- http.post(`${BASE}/store/thor/customers/me/channels`, () =>
88
- HttpResponse.json({ linked: true, reason: 'already_linked' }),
89
- ),
90
- )
63
+ const result = await provider.login({ email: 'test@example.com', password: 'secret' })
91
64
 
92
- const result = await makeProvider().linkCustomerToChannel()
93
- expect(result.linked).toBe(true)
94
- expect(result.reason).toBe('already_linked')
65
+ expect(result.token).toBe('jwt-bearer-token')
66
+ expect(result.customer?.email).toBe('test@example.com')
67
+ expect(sdk.auth.login).toHaveBeenCalledWith('customer', 'emailpass', {
68
+ email: 'test@example.com',
69
+ password: 'secret',
70
+ })
95
71
  })
96
72
 
97
- it('throws ProviderAPIError with NOT_AUTHENTICATED on 401', async () => {
98
- server.use(
99
- http.post(`${BASE}/store/thor/customers/me/channels`, () =>
100
- HttpResponse.json({ message: 'Unauthorized' }, { status: 401 }),
101
- ),
102
- )
73
+ it('throws ProviderAPIError with login code on auth failure', async () => {
74
+ const sdk = makeSdkStub()
75
+ sdk.auth.login = vi.fn().mockRejectedValue(new Error('bad credentials'))
103
76
 
104
- await expect(makeProvider().linkCustomerToChannel()).rejects.toMatchObject({
77
+ const provider = new MedusaAuthProvider(sdk, makeAuthConfig(), false)
78
+
79
+ await expect(provider.login({ email: 'test@example.com', password: 'secret' })).rejects.toMatchObject({
105
80
  name: 'ProviderAPIError',
106
- provider: 'NOT_AUTHENTICATED',
107
- status: 401,
81
+ code: 'PROVIDER_API_ERROR',
82
+ provider: 'LOGIN_FAILED',
83
+ message: 'Authentication failed: bad credentials',
108
84
  })
109
85
  })
110
86
 
111
- it('throws ProviderAPIError with CHANNEL_RESOLUTION_FAILED on 400', async () => {
112
- server.use(
113
- http.post(`${BASE}/store/thor/customers/me/channels`, () =>
114
- HttpResponse.json({ message: 'No sales channel found for publishable key' }, { status: 400 }),
115
- ),
116
- )
87
+ it('returns requiresAction when login requires verification (v2.16.0)', async () => {
88
+ const sdk = makeSdkStub()
89
+ sdk.auth.login = vi.fn().mockResolvedValue({ verification_required: true, token: 'verify-token' })
117
90
 
118
- await expect(makeProvider().linkCustomerToChannel()).rejects.toMatchObject({
119
- name: 'ProviderAPIError',
120
- provider: 'CHANNEL_RESOLUTION_FAILED',
121
- status: 400,
91
+ const provider = new MedusaAuthProvider(sdk, makeAuthConfig(), false)
92
+ const result = await provider.login({ email: 'test@example.com', password: 'secret' })
93
+
94
+ expect(result.requiresAction).toBe(true)
95
+ expect(result.token).toBe('verify-token')
96
+ })
97
+
98
+ it('stores jwt token and returns customer on register (v2.16.0)', async () => {
99
+ const sdk = makeSdkStub()
100
+ sdk.auth.register = vi.fn().mockResolvedValue(undefined)
101
+ sdk.auth.login = vi.fn().mockResolvedValue('jwt-bearer-token')
102
+ sdk.store.customer.create = vi.fn().mockResolvedValue({
103
+ customer: { id: 'cus_1', email: 'test@example.com', first_name: 'Test', last_name: 'User' },
104
+ })
105
+ sdk.store.customer.retrieve = vi.fn().mockResolvedValue({
106
+ customer: { id: 'cus_1', email: 'test@example.com', first_name: 'Test', last_name: 'User' },
107
+ })
108
+
109
+ const provider = new MedusaAuthProvider(sdk, makeAuthConfig('jwt-bearer-token'), false)
110
+
111
+ const result = await provider.register({
112
+ email: 'test@example.com',
113
+ password: 'secret',
114
+ first_name: 'Test',
115
+ last_name: 'User',
122
116
  })
117
+
118
+ expect(sdk.auth.register).toHaveBeenCalledWith('customer', 'emailpass', {
119
+ email: 'test@example.com',
120
+ password: 'secret',
121
+ })
122
+ expect(sdk.auth.login).toHaveBeenCalledWith('customer', 'emailpass', {
123
+ email: 'test@example.com',
124
+ password: 'secret',
125
+ })
126
+ expect(result.token).toBe('jwt-bearer-token')
127
+ expect(result.customer?.email).toBe('test@example.com')
123
128
  })
124
129
 
125
- it('throws ProviderAPIError with LINK_CHANNEL_FAILED on unexpected status', async () => {
126
- server.use(
127
- http.post(`${BASE}/store/thor/customers/me/channels`, () =>
128
- HttpResponse.json({ message: 'Service unavailable' }, { status: 503 }),
129
- ),
130
- )
130
+ it('requires email when resetting password', async () => {
131
+ const provider = makeProvider()
131
132
 
132
- await expect(makeProvider().linkCustomerToChannel()).rejects.toMatchObject({
133
+ await expect(provider.resetPassword('token', 'new-password')).rejects.toMatchObject({
133
134
  name: 'ProviderAPIError',
134
- provider: 'LINK_CHANNEL_FAILED',
135
+ provider: 'MEDUSA_AUTH_PROVIDER',
136
+ status: 400,
135
137
  })
136
138
  })
137
139
  })